Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
183 commits
Select commit Hold shift + click to select a range
391c880
Make use_local_npm.sh work with zsh
ja-openai Nov 20, 2025
6555580
Fix login for user with assigned locales
ja-openai Dec 3, 2025
a14bf93
Add locale management to UserCreateCommand
ja-openai Dec 4, 2025
db9e24a
Add option to generate the user password
ja-openai Dec 4, 2025
dff4dbe
Fix eager fetch of locale for UserWS search by name
ja-openai Dec 17, 2025
7ab33d3
docs: add dev docs index and tracker
ja-openai Nov 20, 2025
80e0645
chore: add new frontend shell
ja-openai Nov 20, 2025
d44611a
docs: Add some task to tracker.md
ja-openai Nov 20, 2025
f731442
docs: note on Auth in the tracker
ja-openai Nov 20, 2025
5a34b13
chore: Add lint/format/test tooling to new frontend
ja-openai Nov 20, 2025
2dbf50d
chore: Add lint and format check in CI
ja-openai Nov 21, 2025
347c027
chore: add react router and base layout
ja-openai Nov 22, 2025
e69da8d
chore: Add repository page
ja-openai Nov 22, 2025
b1b1ccd
chore: add next RepositoryPage step in tracker.md and design doc
ja-openai Dec 9, 2025
61e3a02
chore: in dev mode use header auth with admin user
ja-openai Dec 10, 2025
d6f96ac
chore: split the RepositoriesPage into a container and a view
ja-openai Dec 10, 2025
f254a71
chore: setup react query
ja-openai Dec 10, 2025
dca0066
chore: create a double pan layout for the repositories page
ja-openai Dec 11, 2025
cebe2cd
chore: simplify locale display name resolver
ja-openai Dec 15, 2025
997514c
chore: fetch repositories from API and remove mocked data
ja-openai Dec 15, 2025
576a6da
chore: always show a right pane in the repositories page
ja-openai Dec 16, 2025
0d9d82c
chore: Add auto resizable text area
ja-openai Dec 17, 2025
00b09a8
chore: start workbench page
ja-openai Dec 17, 2025
af01b6d
chore: add status management in the workbench
ja-openai Dec 18, 2025
ddf28a4
chore: add confirmation modal for unsaved translation
ja-openai Dec 18, 2025
85ff4e6
chore: create the text unit api client
ja-openai Dec 19, 2025
69bc5a6
chore: create multi select component
ja-openai Dec 19, 2025
d663687
chore: split the base css for dropdown from multi-select-chip.css
ja-openai Dec 19, 2025
0cb0929
chore: create a pill component
ja-openai Jan 5, 2026
d10f7ad
chore: cleanup unused search parameters
ja-openai Jan 6, 2026
3bc066f
chore: add basic search bar in workbench
ja-openai Dec 23, 2025
a5e2bb8
chore: improve hint and error message display in repository page
ja-openai Jan 6, 2026
07a1e0a
chore: support max height and disable state in multi select chip
ja-openai Jan 6, 2026
31138cb
chore: add a modal component
ja-openai Jan 6, 2026
3238f8d
chore: update UnsavedChangesModal to use the modal component
ja-openai Jan 6, 2026
be6818c
chore: add debounce value hook
ja-openai Jan 7, 2026
e2604ea
chore: add shared spinner css
ja-openai Jan 7, 2026
e29b876
chore: workbench search bar, and workset support
ja-openai Jan 7, 2026
9320e98
chore: implement workbench links
ja-openai Jan 7, 2026
1314e4f
chore: router-state deep link for in-app linking
ja-openai Jan 7, 2026
7425967
chore: add clear input button in workbench search input
ja-openai Jan 8, 2026
70a091c
chore: add in app links from repository page to workbench
ja-openai Jan 7, 2026
e38e94e
chore: add in app links from workbench to workbench
ja-openai Jan 7, 2026
eab2178
chore: refactor deep link logic clarity and simplicity
ja-openai Jan 8, 2026
aef6b34
chore: CSS refactor
ja-openai Jan 8, 2026
ec50ddb
chore: re-style repository page with new search bar
ja-openai Jan 8, 2026
52316f3
chore: filter out root locale where appropriate
ja-openai Jan 8, 2026
169db8f
chore: add script to run 'tsc'
ja-openai Jan 9, 2026
4b32300
chore: remove project tab
ja-openai Jan 9, 2026
7de44a6
chore: use a dropdown for status filter in repository page
ja-openai Jan 9, 2026
97023f4
chore: always show the workset bar
ja-openai Jan 9, 2026
7d79078
chore: add tm text unit id collection management
ja-openai Jan 9, 2026
6708f12
chore: add search by location in TextUnitWS
ja-openai Jan 10, 2026
729abe8
chore: add search by location in the workbench
ja-openai Jan 10, 2026
835733a
chore: remove label for location since we don't have extra info for t…
ja-openai Jan 12, 2026
3a275fa
chore: show only locales assigned to translator in the workbench
ja-openai Jan 12, 2026
837b357
chore: force git empty directory for PullCommandTest_IO#pullAndroidSt…
ja-openai Jan 12, 2026
622132f
chore: show workbench search spinner for new search and links.
ja-openai Jan 12, 2026
7f73df0
chore: add header user dropdown with current identity
ja-openai Jan 13, 2026
12800b6
chore: let translators see all locales and select only theirs
ja-openai Jan 13, 2026
c7898fa
chore: add admin setting page
ja-openai Jan 13, 2026
f26b1f4
chore: control default workset size in admin configuration
ja-openai Jan 13, 2026
77e4ce0
chore: update workset/result set copies
ja-openai Jan 13, 2026
4f204b5
chore: implement has more result in workbench search
ja-openai Jan 13, 2026
233ca22
chore: clamp result size limit for links from repository to workbench
ja-openai Jan 13, 2026
98f3ee8
chore: avoid extra query when loading workbench from links
ja-openai Jan 13, 2026
cfe326a
chore: add admin setting to define preferred locale
ja-openai Jan 13, 2026
fba169e
chore: extract locale dropdown from the workbench
ja-openai Jan 13, 2026
e6aea17
chore: use locale dropdown in the repository page
ja-openai Jan 13, 2026
9d9094f
chore: extract repository dropdown from the workbench
ja-openai Jan 13, 2026
07053dc
chore: use repository dropdown in the repository page
ja-openai Jan 13, 2026
869ed81
chore: use repository dropdown in the repository page
ja-openai Jan 13, 2026
ea9de5c
chore: CSS refactor, use rem, workbench fix and repository cleanup
ja-openai Jan 14, 2026
77356ff
chore: remove the edit mode in workbench
ja-openai Jan 14, 2026
21edd39
chore: introduce MultiSectionFilterChip and refactor
ja-openai Jan 14, 2026
9c3430c
chore: share date quick range for later reuse
ja-openai Jan 14, 2026
a7570b8
chore: share clamp size for repository/locale dropdown
ja-openai Jan 14, 2026
ea157ce
chore: cap max-width size of the chip-dropdown
ja-openai Jan 14, 2026
1b8db7a
chore: extract SearchControl component from workbench for later reuse
ja-openai Jan 14, 2026
3a61d09
chore: extract Virtual List component and refactor
ja-openai Jan 15, 2026
b341ebb
chore: add tools to check on special characters
ja-openai Jan 15, 2026
b322178
chore: add search by translation date
ja-openai Jan 15, 2026
be70b28
chore: add REGEX search in workbench and API
ja-openai Jan 15, 2026
879b606
chore: add workbench cmd+enter shortcut to accept the text unit
ja-openai Jan 15, 2026
818a7c9
chore: fix "clean" display for dates when unset
ja-openai Jan 15, 2026
272e95f
chore: add Locale Pill and refactor workbench
ja-openai Jan 15, 2026
8ac1a52
chore: use Locale dropdown component in the admin page
ja-openai Jan 16, 2026
089178a
chore: hint css cleanup
ja-openai Jan 16, 2026
b0c75ad
chore: move base style into VirtualList and refactor
ja-openai Jan 16, 2026
53fc5bf
chore: style "loading users" message
ja-openai Jan 16, 2026
7a5126b
chore: optimize GET api/repositories
ja-openai Jan 26, 2026
e50231e
chore: prototype WS for chat review
ja-openai Jan 26, 2026
e70a592
chore: add todo for styling
ja-openai Jan 26, 2026
eebc4e0
chore: move checkUserCanEditLocale to UserService for later reuse
ja-openai Jan 26, 2026
cdda2d7
chore: support basic deep-link via URL
ja-openai Jan 26, 2026
449a1ba
chore: load Inter font explicitly
ja-openai Jan 26, 2026
66c3812
chore: create review project from collection
ja-openai Jan 26, 2026
856a8fc
feat(review-projects): add backend model and APIs
ja-openai Jan 26, 2026
43369c5
feat(review): add review project pages in new frontend
ja-openai Jan 26, 2026
e2cf0c7
feat(workbench): support deep link into workbench
ja-openai Jan 26, 2026
d09b069
fix(repositories): open workbench stats with used filter
ja-openai Jan 27, 2026
9edfadd
fix: fix MultiSectionFilterChip bug
ja-openai Jan 27, 2026
10e1d8b
fix(workbench): remeasure virtual rows on resize via ResizeObserver
ja-openai Jan 27, 2026
ea31a7b
fix(review-projects): avoid defaulting to all locales on create
ja-openai Jan 27, 2026
70e3362
fix(review-projects): show clear empty state when no collections exist
ja-openai Jan 27, 2026
724a6c7
feat(review): add review project status endpoint
ja-openai Jan 27, 2026
e9e45a7
feat(review): add close/reopen action in header
ja-openai Jan 27, 2026
80770d3
feat(review): add admin batch close/delete actions
ja-openai Jan 27, 2026
344affb
fix(auth): allow translators to access review project APIs
ja-openai Jan 27, 2026
632e9fc
fix: keep locale filter and stabilize size chip
ja-openai Jan 27, 2026
04b7a4b
fix(review-projects): remove notes length cap
ja-openai Jan 27, 2026
906669e
fix(auth): allow translators to close review projects
ja-openai Jan 27, 2026
bb5503f
fix(review-projects): remove screenshot key input
ja-openai Jan 27, 2026
4855ed2
feat(review-projects): search by request id
ja-openai Jan 27, 2026
819c07f
chore: Allow configurable port for Vite
ja-openai Jan 27, 2026
1ab08af
chore: Rename MOJITO_DEV_USER to VITE_X_FORWARD_USER
ja-openai Jan 27, 2026
f423654
fix(review-projects): skip locales without text units
ja-openai Jan 27, 2026
834e0c5
chore(review-projects): soften admin bar weight
ja-openai Jan 27, 2026
67cb2a7
chore(review-projects): remove admin bar borders
ja-openai Jan 27, 2026
30f8ddc
chore(review-project): make modal danger button red
ja-openai Jan 27, 2026
d9e7540
chore(review-projects): unify link hover styling
ja-openai Jan 27, 2026
058ceeb
chore(review-projects): remove request filter banner
ja-openai Jan 27, 2026
9a1dc8f
chore(review-projects): remove hover underline
ja-openai Jan 27, 2026
401c234
chore: Extract CollectionSelect component and refactor
ja-openai Jan 28, 2026
b97e667
chore: create single select dropdown
ja-openai Jan 28, 2026
0a003b6
feat(ai-translate): port "Ai Translate" page to new frontend
ja-openai Jan 28, 2026
8b4087d
feat(ai-translate): replace the "ai-translate" tab by links in the re…
ja-openai Jan 28, 2026
d1b607c
feat(review-project): refine conflict banner copy and styling
ja-openai Jan 28, 2026
f903fe3
feat(review-project): replace discard confirm alert
ja-openai Jan 28, 2026
5988f36
feat(review-project): blur editors on escape
ja-openai Jan 28, 2026
e44a0dc
feat(review-project): blur editor on save shortcut
ja-openai Jan 28, 2026
e9c2186
feat(review-project): save and advance on shift shortcut
ja-openai Jan 28, 2026
56b1768
feat(review-project): cycle editors with tab
ja-openai Jan 28, 2026
4e35fad
feat(review-project): add shortcuts modal
ja-openai Jan 28, 2026
d1ec20d
fix(review-project): remove empty screenshot messaging
ja-openai Jan 28, 2026
c0ddfa7
fix(review-project): align header CTA styling
ja-openai Jan 29, 2026
fb94b53
fix(workbench): blur translation input after save
ja-openai Jan 29, 2026
1aa2f8e
feat(screenshot): Add screenshots upload page to new frontend
ja-openai Jan 29, 2026
c5a5583
chore(settings): simplify admin settings layout
ja-openai Jan 29, 2026
0b9f455
chore(char-helper): simplify character helper page styling
ja-openai Jan 29, 2026
b409349
chore(review-project): trim unused screenshot styles
ja-openai Jan 30, 2026
9a3f2c5
chore(ui): enhance single select dropdown
ja-openai Jan 30, 2026
f133b3f
feat(settings): admin user management
ja-openai Jan 30, 2026
3bbe386
chore(logging): add json stdout logback config
ja-openai Jan 30, 2026
b3e229d
chore: Add polling to the ai-translate client
ja-openai Jan 31, 2026
efa1bc5
chore: use new poller for text-unit.ts
ja-openai Jan 31, 2026
7ef4901
feat(monitoring): add micrometer registry for Prometheus
ja-openai Jan 31, 2026
ad78273
feat(monitoring): add opt-in permit-all for /actuator/prometheus
ja-openai Jan 31, 2026
cff6cf8
feat(leveraging): New type of source leveraging more appropriate for …
ja-openai Feb 4, 2026
05fe755
feat(workbench): focus dropdown search input on open
ja-openai Feb 4, 2026
c2588e4
feat(workbench): Add pill in status column to show unused text unit
ja-openai Feb 4, 2026
da582b2
chore(review-project): change alignment
ja-openai Feb 5, 2026
1434566
feat(workbench): add session-backed ws navigation, reset, and stable …
ja-openai Feb 4, 2026
a959f77
feat(workbench): Add text unit detail page
ja-openai Feb 4, 2026
1081745
feat(workbench): Add basic detail and history
ja-openai Feb 4, 2026
bc1026e
feat(workbench): Add AI Chat review and improve layout
ja-openai Feb 4, 2026
3b0ff6d
feat(ai-chat-review): Add AiChatReview component and use in review
ja-openai Feb 5, 2026
ff65bff
feat(ai-chat-review): Improve rating badges
ja-openai Feb 5, 2026
8eefe77
feat(search): Add search by "not accept"
ja-openai Feb 5, 2026
7df0f37
feat(frontend): add bounded session token storage utility
ja-openai Feb 5, 2026
3ea319b
feat(workbench): use shared session token storage
ja-openai Feb 5, 2026
0d6d025
feat(repositories): persist and restore filter state via session token
ja-openai Feb 5, 2026
b6a5114
feat(review-project): sync selected text unit with URL query
ja-openai Feb 5, 2026
e9fe95e
feat(ai-chat-review): show suggestion scores instead of dot markers
ja-openai Feb 5, 2026
0ec3d0a
feat(text-unit-detail): repeat repository and locale in metadata
ja-openai Feb 5, 2026
255c3bd
style(text-unit-detail): rebalance metadata label column
ja-openai Feb 5, 2026
56efa2a
style(text-unit-detail): bump source/comment value weight
ja-openai Feb 5, 2026
18237b6
style(review-project): bump key info value weight
ja-openai Feb 5, 2026
b018aeb
style(ai-chat-review): emphasize suggestion text
ja-openai Feb 5, 2026
0fd14df
feat(message-format): add ICU message preview tool with FormatJS infe…
ja-openai Feb 5, 2026
0c0f733
feat(message-format): add inline source and target ICU preview in tex…
ja-openai Feb 5, 2026
a202cd6
chore(mf): fix dependencies for proper plural support
ja-openai Feb 5, 2026
7230128
feat(text-unit-detail): tighten ICU preview and plural controls
ja-openai Feb 5, 2026
430bde0
fix(workbench): correct Accept CTA status/save behavior
ja-openai Feb 6, 2026
47ccdbf
fix(review-project): align Accept button and status dropdown behavior
ja-openai Feb 6, 2026
772b521
feat(review-project): add 'a' shortcut to accept text unit
ja-openai Feb 6, 2026
184089f
style(review-project): rebalance detail controls and move status to s…
ja-openai Feb 6, 2026
ea09b15
feat(review-project): default accepted review items to decided state
ja-openai Feb 6, 2026
413912c
feat(review-project): warn on close with pending items and add Review…
ja-openai Feb 6, 2026
085aa4b
fix(ai-review): allow authenticated access to chat endpoint
ja-openai Feb 9, 2026
91992f0
fix(review-project): adjust Accept enablement and default accepted it…
ja-openai Feb 9, 2026
68cfa3d
style(review-project): make decision toggle white with subtle blue ac…
ja-openai Feb 9, 2026
6bcfc47
feat(text-unit-detail): add delete action and deleted history marker
ja-openai Feb 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ application.pid
*.iml
npm-debug.log
/webapp/node
/webapp/node_modules
/webapp/src/main/resources/new/js/bundle.min.js
.sass-cache/
/mavenplugin/target/
Expand All @@ -29,7 +28,15 @@ release.properties
/docs/.jekyll-cache/
/docs/_site/
/.java-version
/node_modules/
# Ignore node_modules everywhere
**/node_modules/
# Vite/Node caches and outputs
**/.vite/
**/.eslintcache
**/.parcel-cache/
**/.turbo/
**/.cache/
**/*.tsbuildinfo
/tmp/
/local/
.vscode/
19 changes: 19 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
Agent Guide (Codex assistants)
==============================

Purpose
- Document conventions and expectations for assistants working in this repo.

General Guidance
- Default to minimal, purposeful changes; avoid gratuitous complexity.
- Keep docs and tracker entries in sync with work you land.

When you see "finalize commit"
- Review all touched code for the simplest implementation that meets the task; suggest simplifications if possible.
- Update relevant docs (e.g., design notes, README snippets) to reflect the change.
- Update `dev-docs/tracker.md`: add new items or remove items that are done/no longer relevant (no "Done" section).
- Propose a concise commit message that fits the change.

Notes
- For frontend tooling, reuse the Maven-managed Node/npm (see `webapp/use_local_npm.sh`).
- New frontend lives under `webapp/new-frontend` and is served at `/n/`.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.box.l10n.mojito.cli.filefinder.FileMatch;
import com.box.l10n.mojito.cli.filefinder.file.FileType;
import com.box.l10n.mojito.rest.client.RepositoryClient;
import com.box.l10n.mojito.rest.entity.LeveragingType;
import com.box.l10n.mojito.rest.entity.Repository;
import com.box.l10n.mojito.rest.entity.SourceAsset;
import com.ibm.icu.text.MessageFormat;
Expand Down Expand Up @@ -147,6 +148,13 @@ public class PushCommand extends Command {
converter = AssetMappingConverter.class)
Map<String, String> assetMapping;

@Parameter(
names = {"--leveraging-type"},
required = false,
description =
"Leveraging strategy during push: LEGACY_SOURCE, ASSET_SOURCE_AND_COMMENT, or CROSS_ASSET_FALLBACK")
LeveragingType leveragingType = LeveragingType.LEGACY_SOURCE;

@Autowired RepositoryClient repositoryClient;

@Autowired CommandHelper commandHelper;
Expand Down Expand Up @@ -229,6 +237,7 @@ public void execute() throws CommandException {
sourceAsset.setFilterOptions(
commandHelper.getFilterOptionsOrDefaults(
sourceFileMatch.getFileType(), filterOptionsParam));
sourceAsset.setLeveragingType(leveragingType);

return sourceAsset;
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
import com.box.l10n.mojito.rest.client.exception.ResourceNotCreatedException;
import com.box.l10n.mojito.rest.entity.Role;
import com.box.l10n.mojito.rest.entity.User;
import java.security.SecureRandom;
import java.util.Base64;
import java.util.List;
import org.fusesource.jansi.Ansi;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -70,18 +73,61 @@ public class UserCreateCommand extends Command {
description = Param.COMMON_NAME_DESCRIPTION)
String commonName;

@Parameter(
names = {"--locales", "-l"},
variableArity = true,
description = "List of locales (BCP47 tags) translators can work on, e.g. fr-FR ja-JP")
List<String> locales;

@Parameter(
names = {"--generate-password", "-gp"},
description = "Generate a secure random password instead of prompting")
boolean generatePassword = false;

@Autowired Console console;

private static final SecureRandom SECURE_RANDOM = new SecureRandom();

@Override
protected void execute() throws CommandException {
consoleWriter.a("Create user: ").fg(Ansi.Color.CYAN).a(username).println();

try {
consoleWriter.a("Enter new password for " + username + ":").println();
String password = console.readPassword();
String password;
if (generatePassword) {
password = generateSecurePassword();
consoleWriter
.a("Generated password for ")
.fg(Ansi.Color.CYAN)
.a(username)
.a(": ")
.fg(Ansi.Color.YELLOW)
.a(password)
.println();
} else {
consoleWriter.a("Enter new password for " + username + ":").println();
password = console.readPassword();
}

Role role = Role.fromString(rolename);
User user = userClient.createUser(username, password, role, surname, givenName, commonName);

User user;
if (Role.ROLE_TRANSLATOR.equals(role)) {
boolean hasLocales = locales != null && !locales.isEmpty();
boolean canTranslateAllLocales = !hasLocales;
user =
userClient.createUser(
username,
password,
role,
surname,
givenName,
commonName,
locales,
canTranslateAllLocales);
} else {
user = userClient.createUser(username, password, role, surname, givenName, commonName);
}
consoleWriter
.newLine()
.a("created --> user: ")
Expand All @@ -92,4 +138,10 @@ protected void execute() throws CommandException {
throw new CommandException(ex.getMessage(), ex);
}
}

private String generateSecurePassword() {
byte[] bytes = new byte[18];
SECURE_RANDOM.nextBytes(bytes);
return Base64.getUrlEncoder().withoutPadding().encodeToString(bytes);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.box.l10n.mojito.cli.command;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
Expand All @@ -11,6 +12,10 @@
import com.box.l10n.mojito.entity.security.user.User;
import com.box.l10n.mojito.security.Role;
import com.box.l10n.mojito.service.security.user.UserRepository;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import org.junit.Test;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
Expand Down Expand Up @@ -62,7 +67,46 @@ public void testCreateUserWithDuplicatedUsername() throws Exception {
outputCapture.toString().contains("User with username [" + username + "] already exists"));
}

@Test
public void testCreateUserWithGeneratedPassword() throws Exception {

String username = testIdWatcher.getEntityName("user");
String commonName = createTestUser(username, null, null, true);

User user = userRepository.findByUsername(username);
assertEquals(commonName, user.getCommonName());
assertTrue(outputCapture.toString().contains("Generated password for " + username + ":"));
}

@Test
public void testCreateTranslatorWithLocales() throws Exception {

String username = testIdWatcher.getEntityName("user");
List<String> locales = Arrays.asList("fr-FR", "ja-JP");
String commonName = createTestUser(username, "TRANSLATOR", locales);

User user = userRepository.findByUsername(username);
assertEquals(commonName, user.getCommonName());
assertFalse(user.getCanTranslateAllLocales());
assertEquals(
locales.stream().collect(Collectors.toSet()),
user.getUserLocales().stream()
.map(userLocale -> userLocale.getLocale().getBcp47Tag())
.collect(Collectors.toSet()));
}

private String createTestUser(String username, String role) throws Exception {
return createTestUser(username, role, null);
}

private String createTestUser(String username, String role, List<String> localeTags)
throws Exception {
return createTestUser(username, role, localeTags, false);
}

private String createTestUser(
String username, String role, List<String> localeTags, boolean generatePassword)
throws Exception {
String surname = "Mojito";
String givenName = "Test";
String commonName = "Test Mojito " + username;
Expand All @@ -83,32 +127,34 @@ public String answer(InvocationOnMock invocation) throws Throwable {
userCreateCommand.console = mockConsole;

logger.debug("Creating user with username: {}", username);
if (role == null) {
l10nJCommander.run(
"user-create",
Param.USERNAME_SHORT,
username,
Param.SURNAME_SHORT,
surname,
Param.GIVEN_NAME_SHORT,
givenName,
Param.COMMON_NAME_SHORT,
commonName);
} else {
l10nJCommander.run(
"user-create",
Param.USERNAME_SHORT,
username,
Param.ROLE_SHORT,
role,
Param.SURNAME_SHORT,
surname,
Param.GIVEN_NAME_SHORT,
givenName,
Param.COMMON_NAME_SHORT,
commonName);
List<String> params =
new ArrayList<>(
Arrays.asList(
"user-create",
Param.USERNAME_SHORT,
username,
Param.SURNAME_SHORT,
surname,
Param.GIVEN_NAME_SHORT,
givenName,
Param.COMMON_NAME_SHORT,
commonName));

if (role != null) {
params.addAll(Arrays.asList(Param.ROLE_SHORT, role));
}

if (localeTags != null && !localeTags.isEmpty()) {
params.add("-l");
params.addAll(localeTags);
}

if (generatePassword) {
params.add("-gp");
}

l10nJCommander.run(params.toArray(new String[0]));

assertTrue(outputCapture.toString().contains("created --> user: "));
return commonName;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

13 changes: 13 additions & 0 deletions dev-docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
Dev Docs for Codex
==================

This directory holds design docs and working documents used by Codex during implementation. Record key decisions and context here.

Conventions
- Keep this separate from the public site content under `docs/`.
- Design docs live in `dev-docs/design/` and use numeric prefixes for ordering: `001-title.md`, `002-title.md`, ... (pad to 3+ digits).
- Increment the number for each new design doc to keep chronological order without exposing dates.

Tracker
- Use `dev-docs/tracker.md` to capture improvement ideas/tech debt before they become full design docs.
- When an item becomes active, promote it into a numbered design doc.
30 changes: 30 additions & 0 deletions dev-docs/design/001-new-frontend-shell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
Mojito New Frontend Shell (React + Vite)
=======================================

Context
- We are introducing a new frontend that coexists with the legacy UI.
- It should be production-ready, incrementally extensible, and deploy inside the existing Spring Boot app.

Decisions
- Stack: React + Vite + TypeScript.
- Served path: `/n/` (Vite `base: '/n/'`).
- Output path: `webapp/target/classes/public/n` so Spring Boot serves it from the classpath.
- Build integration: `frontend-maven-plugin` in `webapp/pom.xml` runs `npm install` and `npm run build` inside `webapp/new-frontend` during `compile`.
- Dev proxy: Vite proxies `/api/*` to `http://localhost:8080`.
- Server forwarding: `NewFrontendController` forwards `/n` and client-side `/n/**` (except assets/files) to `/n/index.html` so deep links load the SPA.
- Security: `/n/**` is allowlisted in `WebSecurityJWTConfig` so the SPA can load under stateless JWT mode.

Dev Workflow
- Backend: `cd webapp && mvn spring-boot:run -Dspring-boot.run.jvmArguments="-Dspring.config.additional-location=optional:file://$HOME/.l10n/config/webapp/ -Dspring.profiles.active=$USER,npm -Duser.timezone=UTC"`
- Frontend: `cd webapp/new-frontend && npm install && npm run dev` (default `http://localhost:5173`).
- Packaged app: `http://localhost:8080/n/`.

Implementation Notes
- Location: `webapp/new-frontend` with `vite.config.ts` configured to write into `../target/classes/public/n`.
- Keep all new UI assets under this folder; do not mix with legacy webpack output.
- Ensure `.gitignore` excludes Node artifacts globally (`**/node_modules/`, `**/.vite/`, etc.).

Open Questions / Next Steps
- Routing strategy and auth integration for `/n/` paths.
- Add ESLint/Prettier/Vitest.
- Establish UI component conventions and theming.
27 changes: 27 additions & 0 deletions dev-docs/design/002-new-frontend-tooling.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
New Frontend Tooling (ESLint/Prettier/Vitest)
=============================================

Context
- The new Vite/React/TS frontend (`webapp/new-frontend`) is currently bare-bones with no lint/format/test guardrails.
- We want lightweight defaults before building pages so conventions stick and CI can enforce them.

Goals
- Add ESLint + Prettier with sensible defaults for React + TypeScript and keep configs minimal (now includes simple-import-sort for imports/exports).
- Add Vitest + @testing-library/react for unit/component tests.
- Provide npm scripts that match repo conventions (`lint`, `lint:fix`, `format`, `format:check`, `test`, `test:watch`).
- Keep noise low: ignore build output/node_modules; align with Maven-managed Node/npm notes.

Non-goals
- No CI wiring yet; no strict style bikeshedding. Just defaults that can evolve.
- No storybook/e2e yet.

Plan (narrow)
- Install dev deps: eslint, @typescript-eslint/parser/plugin, eslint-plugin-react(+hooks), eslint-plugin-react-refresh, eslint-config-prettier; prettier + plugins for json/yaml/markdown; vitest + @testing-library/react + jsdom; @types/node for tests.
- Add `eslint.config.js` (flat config) and `.prettierrc.cjs` + `.prettierignore`.
- Create basic `vitest.config.ts` aligned with Vite, jsdom environment, and path setup.
- Add a starter React component test to verify wiring.
- Add npm scripts and update docs if needed.

Open questions
- Do we enforce import sorting? (decided yes, using simple-import-sort, auto-fixed by eslint --fix.)
- JSX runtime: use React 17 classic vs. automatic (Vite default uses automatic). Align ESLint config with Vite default.
28 changes: 28 additions & 0 deletions dev-docs/design/003-new-frontend-routing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
New Frontend Routing + Shell Layout
===================================

Context
- The new frontend only rendered a bare headline; we need routing scaffolding to grow feature pages.
- SPA lives under `/n`, so client routing must respect that base path.

Goals
- Adopt React Router (v7) with `BrowserRouter` basename `/n` to align with server routing and deep links.
- Introduce a shared shell layout with top navigation and routed content slots.
- Provide initial routes for Repositories and Workbench with sensible defaults/redirects.
- Establish baseline theming tokens and page structure CSS for light/dark support.

Non-goals
- No data fetching, auth, or real page content yet—routes are placeholders.
- No design system or component library decisions; styles are minimal tokens/layout only.
- No 404/500 UX polish beyond redirecting unknown paths.

Plan (narrow)
- Add `react-router-dom@^7` dependency.
- Wrap `App` in `BrowserRouter` (basename `/n`), define `Routes`/`Route` tree with a shared `AppLayout` using `Outlet`.
- Provide nav links for `/repositories` and `/workbench`; redirect `/` and fallthrough paths to `/repositories`.
- Add `app.css` with CSS variables (colors, spacing, radius) and shell layout styles, and import it in `App.tsx`.

Open questions
- Should navigation highlight active routes or adopt a design-system component?
- Do we want per-route code splitting once pages grow?
- Where should shared tokens live long-term (global CSS vs. theming system)?
Loading