Some checks are pending
Bidi Control Character Guard / bidi-control-guard (push) Waiting to run
Circular Dependency Check / Check for new circular dependencies (push) Waiting to run
Citus Migration Smoke / Combined migrations on single-node Citus (push) Waiting to run
E2E Fresh Install Tests / fresh-install-e2e (push) Waiting to run
ext-v2 guardrails / Run ext-v2 guard and ESLint (push) Waiting to run
Integration Tests / Check for relevant changes (push) Waiting to run
Integration Tests / ${{ (github.event_name == 'schedule' || github.event.inputs.suite == 'full') && 'Full integration suite' || 'Tier-1 integration subset' }} (push) Blocked by required conditions
Mobile checks / Mobile lint + typecheck (push) Waiting to run
Mobile checks / Mobile unit tests (push) Waiting to run
Mobile checks / Mobile dependency audit (report) (push) Waiting to run
Mobile checks / Mobile reproducibility checks (push) Waiting to run
Secrets guard (env backups) / Ensure no tracked env backup files (push) Waiting to run
Temporal Readiness / fast-readiness (push) Waiting to run
Temporal Readiness / docker-parity (push) Waiting to run
TypeScript Type Check / Nx affected typecheck (push) Waiting to run
Unit Tests / Skipped-test budget (push) Waiting to run
Unit Tests / Nx affected unit tests (push) Waiting to run
Unit Tests / Server unit coverage (informational) (push) Waiting to run
Validate Tenant Management Schema / Check for relevant changes (push) Waiting to run
Validate Tenant Management Schema / Validate Tenant Management Schema (push) Blocked by required conditions
EE Workflows Build Guard / ee-workflows-build-guard (push) Waiting to run
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz Source: /opt/alga-psa on psa.joliet.tech
824 lines
62 KiB
Markdown
824 lines
62 KiB
Markdown
# Scratchpad — MSP i18n Batch 2b-21a: Tickets Migration
|
||
|
||
- Plan slug: `2026-04-05-msp-i18n-tickets-migration`
|
||
- Created: `2026-04-05`
|
||
|
||
## What This Is
|
||
|
||
A mechanical wiring pass: 23 unwired MSP ticket components × `useTranslation('features/tickets')`.
|
||
The shared namespace (147 keys, 9 locales) already exists and is already loaded by
|
||
`ROUTE_NAMESPACES['/msp/tickets']`. Client-portal side is 100% wired — this closes the MSP gap.
|
||
|
||
## Decisions
|
||
|
||
- **(2026-04-05)** Keep MSP-specific keys in `features/tickets.json` rather than extracting
|
||
to new `msp/ticketing.json`. Rationale: client portal may eventually surface bulk actions,
|
||
export, etc. If file exceeds ~250 keys after sub-batch A, revisit.
|
||
- **(2026-04-05)** Use `t('key', 'English fallback')` signature everywhere — fallback-safe
|
||
against missing keys and matches patterns in already-wired `TicketConversation.tsx` and
|
||
`TicketDetails.tsx`.
|
||
- **(2026-04-05)** Ship sub-batches A (large dashboard/detail/quickadd), B (settings + export
|
||
+ materials), C (small components) as independent PRs.
|
||
- **(2026-04-05)** Translate toast messages and user-visible error strings. Do NOT translate
|
||
`throw new Error('...')` strings caught by error boundaries or logged only.
|
||
- **(2026-04-05)** Use i18next `{{count}}` interpolation for pluralized toast messages
|
||
(e.g., `'{{count}} ticket moved'` / `'{{count}} tickets moved'`) rather than template
|
||
literals. Matches existing `features/tickets.json` patterns in `messages.*`.
|
||
- **(2026-04-05, F002)** Expand `features/tickets.json` in-place rather than splitting micro-
|
||
namespaces. The shared file is now broad, but keeping tickets UI copy in one namespace makes
|
||
the MSP/client-portal overlap explicit and avoids route-specific key plumbing.
|
||
|
||
## Discoveries / Constraints
|
||
|
||
- **(2026-04-05)** `features/tickets.json` top-level groups: title, subtitle, backToTickets,
|
||
createNew, createButton, viewAll, myTickets, resetFilters, filters (7), create (10),
|
||
status (8), priority (6), fields (21), actions (8), messages (31), conversation (23),
|
||
responseState (4), origin (5), responseSource (2), documents (14). Total: 147 keys.
|
||
- **(2026-04-05, F002/F062)** The initial 147-key count was from an early partial read and is no
|
||
longer accurate. After the full MSP migration pass, `server/public/locales/en/features/tickets.json`
|
||
contains **887 leaf strings**. The file already had substantial nested MSP/support-ticket
|
||
coverage below the first 260 lines; this batch extended that existing structure rather than
|
||
introducing a second parallel namespace.
|
||
- **(2026-04-05)** `ROUTE_NAMESPACES['/msp/tickets']` already loads
|
||
`['common', 'msp/core', 'features/tickets']` — no config changes needed.
|
||
- **(2026-04-05)** Already-wired MSP components (reference examples for patterns):
|
||
- `CommentItem.tsx` → `useTranslation('features/tickets')`
|
||
- `TicketConversation.tsx` → `useTranslation('features/tickets')`
|
||
- `TicketDetails.tsx` → `useTranslation('features/tickets')`
|
||
- `TicketDocumentsSection.tsx` → `useTranslation('features/documents')`
|
||
- `TicketAppointmentRequests.tsx` → `useTranslation('features/appointments')`
|
||
- **(2026-04-05)** Largest files (LOC): TicketingDashboard (2024), QuickAddTicket (1596),
|
||
TicketInfo (1587), TicketProperties (1234), CategoriesSettings (865). These 5 alone are
|
||
~7,300 LOC and likely account for ~60% of strings.
|
||
- **(2026-04-05)** Rough string estimates (from grep heuristic — undercount):
|
||
- Sub-batch A: 4 files, ~150-200 strings
|
||
- Sub-batch B: 4 files, ~80-95 strings
|
||
- Sub-batch C: 15 files, ~50-70 strings
|
||
- **Total: ~280-365 strings** (heuristic floor; realistic: 350-450)
|
||
- **(2026-04-05)** Zero-string components (confirm during implementation):
|
||
TicketDetailsSkeleton, AgentScheduleDrawerStyles, TicketListSkeleton, AgentScheduleDrawer
|
||
(re-export shim). TicketOriginBadge and ResponseSourceBadge may have small badge-label
|
||
key sets needed.
|
||
- **(2026-04-05)** `QuickAddTicket` is reused outside tickets module — imported in
|
||
`server/src/components/layout/QuickCreateDialog.tsx` for the global quick-create menu.
|
||
Translation must also work in that context.
|
||
- **(2026-04-05)** `CategoryPicker` is reused in
|
||
`server/src/app/msp/service-requests/ServiceRequestDefinitionEditorPage.tsx`.
|
||
Verify translation works on service-request routes too.
|
||
- **(2026-04-05)** `CategoriesSettings` and `DisplaySettings` are rendered via
|
||
`server/src/components/settings/general/TicketingSettings.tsx` → likely loaded by
|
||
`/msp/settings` route. `ROUTE_NAMESPACES['/msp/settings']` doesn't currently include
|
||
`features/tickets` — **verify this route loads it transitively or add it**.
|
||
- **(2026-04-05, F001 audit)** `/msp/service-requests/*` also does not have a dedicated
|
||
`ROUTE_NAMESPACES` entry. Those pages currently inherit `/msp` only, so reused ticket
|
||
components like `CategoryPicker` would not receive `features/tickets` unless route config
|
||
is expanded.
|
||
- **(2026-04-05, F001 audit)** Concrete namespace gaps found by component audit:
|
||
- `dashboard.*`: title, add button, assignee/status/response-state/due-date/SLA filters,
|
||
destination board/status prompts, selected-count text, no-selection text, move/delete/
|
||
bundle modal copy, board-switch warnings, bulk result summaries, empty/loading states.
|
||
- `bulk.*`: move/delete/bundle button labels, confirm/cancel/continue actions, singular/
|
||
plural success toasts, partial-failure headings, master-ticket selection, cross-client
|
||
bundle warning, destination validation errors.
|
||
- `quickAdd.*`: dialog title, required-fields summary, assigned-to/additional-agents labels,
|
||
board/category/status/location/contact placeholders, due-date helper copy, clipboard
|
||
upload failure, team assignment/tag creation partial-failure toasts.
|
||
- `itil.*`: impact/urgency field labels, select placeholders, calculated-priority label,
|
||
priority-matrix heading, impact/urgency scale labels, explanatory help text, planning
|
||
priority label.
|
||
- `info.*` / `properties.*`: unsaved-changes banner, status/board/priority/due-date/SLA/
|
||
tags/description headings, not-assigned/no-contact/no-location/no-primary-agent/no-team
|
||
empty states, additional-agents label, appointment request field labels, team-assignment
|
||
removal modes, email-log button label.
|
||
- `settings.categories.*`: categories heading, board filter placeholder, dialog field labels,
|
||
import flow copy, import conflict actions, delete/save/import success and failure strings.
|
||
- `settings.display.*`: response-state tracking section, explanatory copy, toggle labels,
|
||
display-preferences section, date/time format label, column labels, tags layout labels,
|
||
save/saving success and failure strings.
|
||
- `export.*`: dialog title, field-picker heading, select-all toggles, selected-count copy,
|
||
exporting/progress/completion copy, done/cancel actions, CSV file export failures.
|
||
- `materials.*`: product/price/quantity/total/description labels, select placeholders,
|
||
loading/empty states, billed/pending badges, add/remove success and validation errors.
|
||
- `watchList.*`: tab labels, contact scope labels, selector placeholders, empty state, add/
|
||
remove validation errors, generic save failure message.
|
||
- `emailNotifications.*`: table headings, loading/empty states, pagination action text.
|
||
- `categoryPicker.*`: "No Category", multi-select summary text, exclusion summary text,
|
||
add-new label, ITIL badge label, default placeholder.
|
||
- `responseState.*` additions: `awaitingClient`, `awaitingInternal`, `clear`,
|
||
`setResponseState`, `notSet`, `label`. Existing keys use client-portal wording and do not
|
||
cover the MSP dropdown strings directly.
|
||
- `navigation.*`: previous/next ticket aria-labels.
|
||
- `debug.*`: comment metadata modal section headings and empty-summary copy.
|
||
- `errors.*` / `validation.*`: generic save/load/export/category/material/watch-list errors
|
||
that are user-visible today and should not stay hardcoded.
|
||
- **(2026-04-05, F002)** Added the first full MSP key expansion to
|
||
`server/public/locales/en/features/tickets.json`: `dashboard`, `bulk`, `quickAdd`, `itil`,
|
||
`info`, `properties`, `settings.categories`, `settings.display`, `export`, `materials`,
|
||
`watchList`, `emailNotifications`, `categoryPicker`, `navigation`, `debug`, `validation`,
|
||
and `errors`, plus targeted additions to `filters`, `priority`, `fields`, `actions`, and
|
||
`responseState`.
|
||
- **(2026-04-05, F002)** To keep translation validation green between per-locale commits,
|
||
the same new key structure was scaffolded into `fr/es/de/nl/it/pl` with English placeholder
|
||
values. `xx/yy` were regenerated immediately from English; the real locale translations are
|
||
filled in by `F003`-`F008`.
|
||
- **(2026-04-05, F062)** Final namespace closeout: `features/tickets.json` ends this batch at
|
||
**887 English leaf strings**. That is well above the earlier ~250 revisit threshold, but the
|
||
shared MSP/client-portal namespace is still the right design because the badge enums, ticket
|
||
chrome, quick-add flow, settings surfaces, and reused picker components now all draw from one
|
||
route-agnostic ticket vocabulary. No feature-scope code items remain deferred; the remaining
|
||
backlog is test execution/coverage tracked in `tests.json`.
|
||
|
||
## Commands / Runbooks
|
||
|
||
### The lang-pack loop (run after every namespace edit)
|
||
|
||
This is the single-command validation cycle. Run it every time `en/features/tickets.json`
|
||
is edited — it regenerates pseudo-locales and verifies key parity across all 9 locales:
|
||
|
||
```bash
|
||
node scripts/generate-pseudo-locales.cjs && node scripts/validate-translations.cjs
|
||
```
|
||
|
||
- `generate-pseudo-locales.cjs` reads every English file and rebuilds `xx/` and `yy/` with
|
||
fill values (`11111`, `55555`), preserving `{{interpolation}}` tokens. **Never hand-edit
|
||
`xx/` or `yy/` files — they will be overwritten.**
|
||
- `validate-translations.cjs` checks every locale against English for: identical key
|
||
structure, no extra/missing keys, preserved `{{variable}}` tokens, valid JSON, pseudo-
|
||
locale fill patterns. Exit code 0 = pass.
|
||
|
||
If validation fails, fix `en/features/tickets.json` (or the complaining locale), re-run
|
||
the one-liner. Keep the validator green before committing.
|
||
|
||
### Other useful commands
|
||
|
||
- Count strings in a component (lower bound):
|
||
```bash
|
||
grep -cE ">[A-Z][a-zA-Z ]{2,}[a-z]<|(label|title|placeholder)[=:][ ]*['\"][A-Z]|toast\.(error|success)\(['\"]" <file>
|
||
```
|
||
- List all unwired MSP ticket components:
|
||
```bash
|
||
for f in $(find packages/tickets/src/components -type f -name "*.tsx" ! -name "*.test.tsx"); do
|
||
grep -qE "useTranslation" "$f" || echo "$f"
|
||
done
|
||
```
|
||
- Audit user-visible strings in unwired components:
|
||
```bash
|
||
rg -n "toast\.|title:|label=|placeholder=|aria-label|>[^<{]*[A-Za-z][^<{]*<|throw new Error|confirm\(" packages/tickets/src/components
|
||
```
|
||
- Find all places a component is imported:
|
||
```bash
|
||
grep -rn "from '@alga-psa/tickets" server/src ee/server/src | grep ComponentName
|
||
```
|
||
- Reference already-wired files (copy the pattern):
|
||
```
|
||
packages/tickets/src/components/ticket/TicketDetails.tsx
|
||
packages/tickets/src/components/ticket/TicketConversation.tsx
|
||
packages/tickets/src/components/ticket/CommentItem.tsx
|
||
```
|
||
|
||
## Links / References
|
||
|
||
- Parent plan: `.ai/translation/MSP_i18n_plan.md` (Batch 2b-21a)
|
||
- Shared namespace file: `server/public/locales/en/features/tickets.json`
|
||
- Route config: `packages/core/src/lib/i18n/config.ts` (ROUTE_NAMESPACES)
|
||
- Validation script: `scripts/validate-translations.cjs`
|
||
- Validation CI workflow: `.github/workflows/validate-translations.yml`
|
||
- Pattern reference (already wired): `packages/tickets/src/components/ticket/TicketDetails.tsx`,
|
||
`packages/tickets/src/components/ticket/TicketConversation.tsx`,
|
||
`packages/tickets/src/components/ticket/CommentItem.tsx`
|
||
- Precedent plan (similar wiring-only work): `docs/plans/2026-03-20-msp-i18n-remaining/`
|
||
- i18n test helpers: `server/src/test/unit/layout/*.i18n.test.tsx`
|
||
- QuickCreate integration: `server/src/components/layout/QuickCreateDialog.tsx`
|
||
- Service-request integration: `server/src/app/msp/service-requests/ServiceRequestDefinitionEditorPage.tsx`
|
||
- Settings integration: `server/src/components/settings/general/TicketingSettings.tsx`
|
||
|
||
## Open Questions
|
||
|
||
- Does `ROUTE_NAMESPACES['/msp/settings']` or `/msp/settings/ticketing` load
|
||
`features/tickets`? If not, wiring `CategoriesSettings` and `DisplaySettings` requires a
|
||
namespaces update. **Action: verify before starting sub-batch B.**
|
||
- `F010` is required: add `features/tickets` to `/msp/settings` and add an explicit
|
||
`/msp/service-requests` route mapping so `CategoriesSettings`, `DisplaySettings`, and the
|
||
reused `CategoryPicker` have locale resources outside `/msp/tickets`.
|
||
- Should `TicketOriginBadge` and `ResponseSourceBadge` reuse the existing
|
||
`features/tickets.json` `origin.*` and `responseSource.*` keys or do those groups need
|
||
more enum values? **Action: diff badge enum values against existing keys during F049.**
|
||
- What is the canonical test pattern for component-level i18n tests in this repo? Models
|
||
to follow: `server/src/test/unit/layout/*.i18n.test.tsx`,
|
||
`server/src/test/unit/dashboard/DashboardContainer.i18n.test.tsx`.
|
||
- Are there existing test helpers/utilities for rendering components with specific locales?
|
||
**Action: search for i18n test setup in `server/src/test/unit/i18n/*`.**
|
||
|
||
## Implementation Order (recommended)
|
||
|
||
1. **Setup (F001-F010):** Audit gaps, add missing keys to en/features/tickets.json, generate
|
||
all 8 other locales. This unblocks all component wiring.
|
||
2. **Sub-batch A PR (F020-F028):** 4 large components. Biggest review burden; ship first
|
||
while context is fresh.
|
||
3. **Sub-batch B PR (F030-F037):** 4 medium components. Verify settings-route namespace
|
||
loading before starting.
|
||
4. **Sub-batch C PR (F040-F050):** 15 small components. Quick cleanup pass.
|
||
5. **Closeout (F060-F062):** Validate, update parent plan, archive scratchpad notes.
|
||
|
||
## Progress Log
|
||
|
||
- **(2026-04-05, F002)** Expanded `en/features/tickets.json` with the audited MSP ticket key
|
||
families used by dashboard/bulk/quick-add/ITIL/info/properties/settings/export/materials/
|
||
watch-list/email/category-picker/response-state/navigation/debug/validation/error flows.
|
||
To keep key parity green on every commit, mirrored the new keys into `fr/es/de/nl/it/pl`
|
||
with temporary English placeholders. Regenerated `xx/yy/features/tickets.json` from English
|
||
and re-ran `node scripts/validate-translations.cjs` successfully (`Errors: 0`, `Warnings: 0`).
|
||
- **(2026-04-05, F002)** Placeholder strategy is intentional: `F003`-`F008` will replace the
|
||
temporary English values in each production locale with real translations one locale at a
|
||
time while keeping the validator green throughout.
|
||
- **(2026-04-05, F003)** Replaced the temporary English placeholders in
|
||
`fr/features/tickets.json` with French translations for the MSP ticket additions. During
|
||
validation a partial Italian worker edit had renamed `ticketSection.on` / `ticketSection.minutes`
|
||
to localized key names; corrected the key names in-place so parity stayed valid without
|
||
altering the Italian values.
|
||
- **(2026-04-05, F004)** Replaced the temporary English placeholders in
|
||
`es/features/tickets.json` with Spanish translations for the MSP ticket additions using the
|
||
same batched machine-translation pass as French, then manually corrected ticket-domain copy
|
||
around bundle warnings, bulk move success interpolation, and plural action labels. After the
|
||
cleanup pass only expected invariants remain English-equal (`Total`, `Error`, `SLA`, `ITIL`,
|
||
`N/A`), and `node scripts/validate-translations.cjs` stays green.
|
||
- **(2026-04-05, F005)** Completed the German locale pass for
|
||
`de/features/tickets.json`. This file already had broad ticket coverage; the remaining work
|
||
was mostly placeholder cleanup in MSP additions. After translation, the only English-equal
|
||
leaves are domain terms or deliberate invariants (`Board`, `Team`, `Lead`, `Service`,
|
||
`SLA`, `ITIL`, `name@example.com`), so no extra manual rewrites were needed beyond the
|
||
automated fill-in. Validation stayed green.
|
||
- **(2026-04-05, F006)** Completed the Dutch locale pass for
|
||
`nl/features/tickets.json`. The machine fill covered the remaining MSP placeholders cleanly;
|
||
manual cleanup was limited to replacing the exported-ticket tooltip’s literal `ticket(s)`
|
||
phrasing with natural Dutch. Remaining English-equal leaves are acceptable product terms or
|
||
invariants (`Open`, `Status`, `Team`, `Impact`, `Product`, `Compact`, `SLA`, `ITIL`).
|
||
- **(2026-04-05, F007)** Italian locale coverage was already effectively complete after the
|
||
placeholder scaffold. The substantive work here was preserving key parity by restoring the
|
||
canonical key names `ticketSection.on` and `ticketSection.minutes` while keeping the localized
|
||
values (`il`, `minuti`). Accent-sensitive validation remained green, so no further copy edits
|
||
were required beyond confirming the remaining English-equal leaves are acceptable (`Team`,
|
||
`Email`, `SLA`, `ITIL`).
|
||
- **(2026-04-05, F008)** Completed the Polish locale pass for
|
||
`pl/features/tickets.json`, including the remaining MSP validation strings, ITIL helper copy,
|
||
ticket-section appointment labels, and quick-add placeholders. After the translation pass only
|
||
invariant leaves remain English-equal (`Status`, `SLA`, `ITIL`), and the bundle/bulk wording
|
||
reads consistently with the rest of the ticket namespace.
|
||
- **(2026-04-05, F009)** Re-ran `node scripts/generate-pseudo-locales.cjs` after the locale
|
||
setup work. The generator reported `52` pseudo-locale files rebuilt from `26` English sources,
|
||
but produced no git diff, confirming `xx/yy` were already in sync with the current English
|
||
namespace. This item is complete as an explicit regeneration checkpoint rather than a content
|
||
change.
|
||
- **(2026-04-05, F010)** Updated `packages/core/src/lib/i18n/config.ts` so reused ticket
|
||
components can resolve `features/tickets` outside `/msp/tickets`: `/msp/settings` now loads
|
||
`features/tickets` alongside its existing namespaces, and `/msp/service-requests` now has an
|
||
explicit route entry loading `['common', 'msp/core', 'features/tickets']`. Added focused route
|
||
coverage to `server/src/test/unit/i18n/mspDispatchReportsAdminTimeEntryBatch.test.ts` and
|
||
verified it with `cd server && npx vitest run src/test/unit/i18n/mspDispatchReportsAdminTimeEntryBatch.test.ts`.
|
||
- **(2026-04-05, F020)** Wired `packages/tickets/src/components/TicketingDashboard.tsx` to
|
||
`useTranslation('features/tickets')` for the dashboard title, add-ticket CTA, assignee/status/
|
||
priority/response-state/due-date/SLA filter placeholders and option labels, including the
|
||
interpolated before/after-date labels. Validation used `npx eslint
|
||
packages/tickets/src/components/TicketingDashboard.tsx`; it exited 0 with only pre-existing
|
||
warnings in this file (unused vars, hook deps, legacy JSX apostrophe), so no new lint errors
|
||
were introduced by the i18n patch.
|
||
- **(2026-04-05, F021)** Localized the dashboard’s bulk-selection surface in
|
||
`packages/tickets/src/components/TicketingDashboard.tsx`: selection-menu actions, bulk move/
|
||
delete/bundle toolbar buttons, select-all banners, bundle cross-client confirmation, move/
|
||
delete/bundle dialog titles and buttons, bulk dialog field labels/placeholders, and bulk toast
|
||
messages now all resolve through `features/tickets` with `count` interpolation. Re-ran
|
||
`npx eslint packages/tickets/src/components/TicketingDashboard.tsx`; it stayed green with only
|
||
the same file-local pre-existing warnings.
|
||
- **(2026-04-05, F022)** Finished the remaining dashboard UX copy in
|
||
`packages/tickets/src/components/TicketingDashboard.tsx`: export tooltip text, client quick-
|
||
drawer loading/not-found/load-failed fallbacks, bundle-child load errors, destination-board
|
||
status-load errors, single-delete fallback entity name, delete-validation fallback copy, and
|
||
the quick-add optimistic-row unknown-client label now all resolve through `features/tickets`.
|
||
This required a small namespace extension in `en/fr/es/de/nl/it/pl/features/tickets.json`
|
||
(`dashboard.drawer.clientLoadFailed`, `bulk.move.noStatusesConfigured`,
|
||
`bulk.move.loadStatusesFailed`, `bulk.delete.entityFallback`,
|
||
`errors.validateDeletionFailed`, `errors.deleteTicketUnexpected`), followed by
|
||
`node scripts/generate-pseudo-locales.cjs && node scripts/validate-translations.cjs`
|
||
which passed with `Errors: 0`, `Warnings: 0`.
|
||
- **(2026-04-05, T010)** Added `packages/tickets/src/components/TicketingDashboard.i18n.test.ts`
|
||
as a fast source-contract test for the dashboard shell/filter wiring. A jsdom render harness
|
||
pulled in too much of the ticket/auth runtime for this file, so the test asserts the concrete
|
||
`t('...')` calls for the page title, add button, primary filter placeholders/options, reset
|
||
control, bundled toggle, and density-control labels directly in `TicketingDashboard.tsx`.
|
||
Verified with `cd packages/tickets && npx vitest run src/components/TicketingDashboard.i18n.test.ts`.
|
||
- **(2026-04-05, F023)** Audited `packages/tickets/src/components/QuickAddTicket.tsx` and
|
||
confirmed the dialog/title/field placeholder slice was already wired even though the checklist
|
||
was stale: dialog title + automation labels, title/description/client/contact/location/board/
|
||
assignee/additional-agent/category/status/priority/due-date placeholders, and the ITIL field
|
||
labels/help text all resolve through `useTranslation('features/tickets')`. Attempted to validate
|
||
with `cd packages/tickets && npx vitest run src/components/__tests__/QuickAddTicket.boardScopedStatuses.test.tsx`,
|
||
but the package test setup currently fails before executing tests because Vite cannot resolve
|
||
`@alga-psa/core/server` from `shared/core/getSecret.ts`. No source change was required in this
|
||
turn beyond syncing the feature checklist to the implementation state.
|
||
- **(2026-04-05, F023A)** The earlier `F023` audit was incomplete: `QuickAddTicket.tsx` still
|
||
had hardcoded field-chrome in the ITIL matrix, the additional-agents team-section label, and
|
||
the unnamed-location fallback. Wired those through `features/tickets`, added
|
||
`quickAdd.unnamedLocation` and `quickAdd.addTeamMembers` to the locale packs, regenerated
|
||
pseudo-locales, and re-ran `node scripts/generate-pseudo-locales.cjs && node
|
||
scripts/validate-translations.cjs` successfully. `npx eslint
|
||
packages/tickets/src/components/QuickAddTicket.tsx` still reports only the file’s existing
|
||
warnings (`@ts-nocheck`, unused imports/types, and pre-existing hook-deps warnings).
|
||
- **(2026-04-05, F024)** Completed the remaining user-visible QuickAddTicket action/error copy:
|
||
validation messages now use translated field-specific requirements, submit/cancel buttons and
|
||
the secondary `Create + View Ticket` action resolve through i18n, clipboard image confirmation
|
||
copy is translated, and the quick-add toast/error paths for clipboard image upload, team
|
||
assignment, tag creation, and generic create failures now use `features/tickets`. Added
|
||
`quickAdd.createAndView`, `quickAdd.continueEditing`, and `quickAdd.clipboardDraftMessage`
|
||
across the production locales, regenerated pseudo-locales, and re-ran translation validation
|
||
successfully. `npx eslint packages/tickets/src/components/QuickAddTicket.tsx` remained green
|
||
with only the file’s pre-existing warnings.
|
||
- **(2026-04-05, F024 follow-up)** Removed duplicated `quickAdd.createAndView`,
|
||
`quickAdd.continueEditing`, and `quickAdd.clipboardDraftMessage` entries that were lingering in
|
||
the production locale files while leaving the canonical localized values intact. Re-ran
|
||
`node scripts/generate-pseudo-locales.cjs && node scripts/validate-translations.cjs`; it still
|
||
passed with `Errors: 0`, `Warnings: 0`.
|
||
- **(2026-04-05, F025)** Wired the main `TicketInfo.tsx` detail-surface chrome to
|
||
`useTranslation('features/tickets')`: title-edit button titles, unsaved/saved banners,
|
||
section headings (status/assignee/board/category/priority/due date/SLA/tags/description),
|
||
assignee/category/date/time placeholders, ITIL labels + matrix text, additional-agent tooltip
|
||
heading, paused/clear-due-date labels, description empty-state copy, and the email-log tooltip/
|
||
aria label now all resolve through the shared ticket namespace. Validation used
|
||
`npx eslint packages/tickets/src/components/ticket/TicketInfo.tsx`; it exited 0 with only the
|
||
file’s pre-existing warnings (`@ts-nocheck`, unused symbols, existing `any`/non-null asserts).
|
||
- **(2026-04-05, F026)** Finished the remaining user-facing `TicketInfo.tsx` action/confirmation
|
||
copy: description save/cancel controls, the footer cancel/save-changes controls, the unsaved-
|
||
changes discard dialog, and the pasted-images cleanup dialog now all resolve through
|
||
`features/tickets`. Added `info.saveChanges`, `info.saving`, `info.discardChangesTitle`,
|
||
`info.discardChangesMessage`, `info.discard`, `info.keepEditing`, and
|
||
`info.clipboardDraftMessage` across `en/fr/es/de/nl/it/pl`, regenerated `xx/yy`, and re-ran
|
||
`node scripts/generate-pseudo-locales.cjs && node scripts/validate-translations.cjs`
|
||
successfully. Focused validation also passed with `npx eslint
|
||
packages/tickets/src/components/ticket/TicketInfo.tsx` and `cd packages/tickets && npx vitest
|
||
run src/components/ticket/__tests__/TicketInfo.boardChangeStatusReselection.test.tsx
|
||
src/components/ticket/TicketInfo.richText.contract.test.ts`.
|
||
- **(2026-04-05, F026 test harness)** The existing `TicketInfo` regression tests needed upkeep to
|
||
reflect the current component composition: `TicketInfo.boardChangeStatusReselection.test.tsx`
|
||
now mocks `useTranslation` and `useDocumentsCrossFeature`, and
|
||
`TicketInfo.richText.contract.test.ts` asserts the translated clipboard-dialog wiring instead of
|
||
the retired hardcoded `"Keep Images"` literal.
|
||
- **(2026-04-05, F027)** Wired the main `TicketProperties.tsx` side-panel chrome through
|
||
`useTranslation('features/tickets')`: the time-entry card title/timer label/controls, work-
|
||
description label + placeholder, disabled-timer message, tracked-interval heading, contact-info
|
||
card title and field labels, contact/location placeholders and empty states, client/contact
|
||
fallback labels, appointment-request drawer headings, assigned-team fallbacks, primary-agent
|
||
section labels, scheduled-hours text, and additional-agent picker labels/placeholders are now
|
||
localized. Added `properties.timeEntry` and `properties.ticketTimer` across
|
||
`en/fr/es/de/nl/it/pl`, regenerated `xx/yy`, and re-ran `node scripts/generate-pseudo-locales.cjs
|
||
&& node scripts/validate-translations.cjs` successfully.
|
||
- **(2026-04-05, F027 validation)** Focused regression checks passed with `npx eslint
|
||
packages/tickets/src/components/ticket/TicketProperties.tsx` (warnings only, no new errors) and
|
||
`cd packages/tickets && npx vitest run
|
||
src/components/ticket/__tests__/TicketProperties.liveTimerPolicy.test.tsx
|
||
src/components/ticket/__tests__/ticket-properties-inline-contact.test.tsx`. Those tests now mock
|
||
`useTranslation`, and the inline-contact harness also mocks `useQuickAddClient` so the quick-add
|
||
contact dialog continues to render in isolation.
|
||
- **(2026-04-05, F028)** Finished the remaining interactive `TicketProperties.tsx` copy without
|
||
adding new keys: the contact/client/location picker footer buttons now use shared
|
||
`actions.cancel` / `actions.save`, and the team removal/switch dialog now resolves its title,
|
||
option labels, empty state, and confirm/cancel buttons through existing `properties.*` and
|
||
`actions.*` keys. The only English strings still visible in the file are inside a commented-out
|
||
legacy team block and do not render at runtime.
|
||
- **(2026-04-05, F028 validation)** Re-ran `npx eslint
|
||
packages/tickets/src/components/ticket/TicketProperties.tsx` (same pre-existing warnings only)
|
||
plus `cd packages/tickets && npx vitest run
|
||
src/components/ticket/__tests__/TicketProperties.liveTimerPolicy.test.tsx
|
||
src/components/ticket/__tests__/ticket-properties-inline-contact.test.tsx`, which stayed green
|
||
after the dialog/button wiring.
|
||
- **(2026-04-05, F030)** Wired the `CategoriesSettings.tsx` page shell to
|
||
`useTranslation('features/tickets')` without expanding the namespace: the page heading, board
|
||
filter option/placeholder, and the category table’s `Name` / `Board` / `Order` / `Actions`
|
||
headers now resolve through existing `settings.categories.*`, `fields.board`, and
|
||
`settings.display.columns.actions` keys. This leaves the add/edit/import/delete dialog and toast
|
||
copy isolated for `F031`.
|
||
- **(2026-04-05, F030 validation)** Verified the wiring with `cd packages/tickets && npx vitest
|
||
run src/components/settings/__tests__/CategoriesSettings.contract.test.ts` and `npx eslint
|
||
packages/tickets/src/components/settings/CategoriesSettings.tsx` (warnings only, no new errors).
|
||
- **(2026-04-05, F031)** Finished the `CategoriesSettings.tsx` dialog/error flow wiring using the
|
||
existing `settings.categories.*` keys: fetch/save/import/delete validation messages, success
|
||
toasts, dropdown action labels, edit/import/conflict dialog titles, form labels/placeholders,
|
||
import target-board help text, import-table column labels, conflict resolution copy, and the
|
||
related cancel/update/import buttons now all resolve through `features/tickets`. No locale-file
|
||
expansion was needed for this pass because the namespace additions from `F002` already covered
|
||
the settings copy.
|
||
- **(2026-04-05, F031 validation)** Re-ran `cd packages/tickets && npx vitest run
|
||
src/components/settings/__tests__/CategoriesSettings.contract.test.ts` after updating its stale
|
||
raw-string assertions to the new `t(...)` calls, and re-ran `npx eslint
|
||
packages/tickets/src/components/settings/CategoriesSettings.tsx` (warnings only, no new errors).
|
||
- **(2026-04-05, F032)** Started the `TicketExportDialog.tsx` pass by wiring the configure-step
|
||
chrome to `useTranslation('features/tickets')`: the dialog title, export-field labels, field-
|
||
picker heading, select-all/deselect-all toggle, and selected-field count now resolve through the
|
||
existing `export.*`, `fields.*`, `properties.contact`, and `settings.display.columns.tags` keys.
|
||
No namespace expansion was required for this slice.
|
||
- **(2026-04-05, F032 validation)** Verified the configure-step patch with `npx eslint
|
||
packages/tickets/src/components/TicketExportDialog.tsx` and a direct source grep confirming the
|
||
previous hardcoded configure labels were replaced by `t(...)` calls. The remaining export-action
|
||
strings are intentionally deferred to `F033`.
|
||
- **(2026-04-05, F033)** Completed the `TicketExportDialog.tsx` export-state wiring: the export
|
||
summary, applied-filters summary, cancel/export CTA labels, exporting-state message, completion
|
||
title/message, done button, and export failure handling now all resolve through `export.*` and
|
||
`actions.cancel`. The count-based strings use the existing pluralized export keys instead of
|
||
manual `ticket/tickets` concatenation.
|
||
- **(2026-04-05, F033 validation)** Re-ran `npx eslint
|
||
packages/tickets/src/components/TicketExportDialog.tsx` and a direct source grep to confirm the
|
||
former hardcoded action/progress strings now route through `t(...)`. No locale-file changes were
|
||
needed for this pass.
|
||
- **(2026-04-05, F034)** Wired the core `TicketMaterialsCard.tsx` chrome through
|
||
`useTranslation('features/tickets')` using the pre-existing `materials.*` keys: the card title,
|
||
add-button label, add-material form labels/placeholders, price-loading/no-price copy, quantity/
|
||
total/description fields, cancel/add CTA labels, the materials table headers, billed/pending
|
||
badges, unknown-product fallback, unbilled-per-currency summary, and add-success toast now all
|
||
resolve through the shared tickets namespace. This pass intentionally left validation/error/
|
||
delete/empty-state copy for `F035` so the remaining work stays atomic.
|
||
- **(2026-04-05, F034 validation)** Verified with `npx eslint
|
||
packages/tickets/src/components/ticket/TicketMaterialsCard.tsx`, which exited 0.
|
||
- **(2026-04-05, F035)** Finished the remaining `TicketMaterialsCard.tsx` user-facing copy:
|
||
load/add/remove failures now use `errors.loadMaterials` / `errors.addMaterial` /
|
||
`errors.removeMaterial`, add-form validation toasts use `validation.materials.*`, the delete
|
||
success toast uses `materials.removeSuccess`, and the loading/empty/client-required helper
|
||
states now resolve through `materials.*`. This required one namespace addition,
|
||
`materials.clientRequired`, in `en/fr/es/de/nl/it/pl/features/tickets.json`, followed by
|
||
pseudo-locale regeneration.
|
||
- **(2026-04-05, F035 validation)** Re-ran
|
||
`node scripts/generate-pseudo-locales.cjs && node scripts/validate-translations.cjs`
|
||
(`Errors: 0`, `Warnings: 0`) and `npx eslint
|
||
packages/tickets/src/components/ticket/TicketMaterialsCard.tsx`, which exited 0.
|
||
- **(2026-04-05, F036)** Wired the main `DisplaySettings.tsx` labels through
|
||
`useTranslation('features/tickets')` without expanding the namespace: the response-state
|
||
tracking section title/description/toggle labels, the display-preferences heading and
|
||
description, the date/time-format label, the date-format preview options, the ticket-list
|
||
columns heading, every column checkbox label plus required suffix, and the tags visibility/
|
||
layout labels now resolve through `settings.display.*` and shared `fields.*` keys. The save
|
||
CTA and save/failure copy are intentionally left for `F037`.
|
||
- **(2026-04-05, F036 validation)** Verified with `npx eslint
|
||
packages/tickets/src/components/settings/DisplaySettings.tsx`, which exited 0.
|
||
- **(2026-04-05, F037)** Finished the existing `DisplaySettings.tsx` save path: the success
|
||
toast, save-failure message, and save-button/saving labels now resolve through
|
||
`settings.display.saveSuccess`, `settings.display.saveFailed`, `settings.display.saving`,
|
||
and shared `actions.save`. The checklist mentioned reset/per-user controls, but this component
|
||
currently only exposes a save action; no reset UI exists to wire in this batch.
|
||
- **(2026-04-05, F037 validation)** Re-ran `npx eslint
|
||
packages/tickets/src/components/settings/DisplaySettings.tsx`, which exited 0.
|
||
- **(2026-04-05, F040)** Wired `TicketWatchListCard.tsx` through
|
||
`useTranslation('features/tickets')`: the card title, add-mode tabs, contact-scope toggle,
|
||
picker/email placeholders, add button labels, empty state, validation/update-failure messages,
|
||
recipient type badges, and remove-button aria/title copy now resolve through `watchList.*`.
|
||
This required a small namespace addition in `en/fr/es/de/nl/it/pl/features/tickets.json`
|
||
(`watchList.userBadge`, `watchList.contactBadge`, `watchList.removeWatcher`), followed by
|
||
pseudo-locale regeneration.
|
||
- **(2026-04-05, F040 validation)** Re-ran
|
||
`node scripts/generate-pseudo-locales.cjs && node scripts/validate-translations.cjs`
|
||
(`Errors: 0`, `Warnings: 0`) and `npx eslint
|
||
packages/tickets/src/components/ticket/TicketWatchListCard.tsx`, which exited 0. A focused
|
||
`TicketWatchListCard.test.tsx` run still fails before assertions because the existing harness
|
||
does not provide the now-required `useTranslation`/`useFeatureFlag` context; that follow-up is
|
||
queued as test work rather than a source blocker for the feature wiring.
|
||
- **(2026-04-05, F041)** Wired `TicketEmailNotifications.tsx` through
|
||
`useTranslation('features/tickets')`: the card title, table column titles, loading/empty
|
||
states, load-more button, and fallback unknown-error string now resolve through
|
||
`emailNotifications.*`. The sent-at formatter also now uses the active i18n language instead of
|
||
hardcoded `en-US`, so locale changes affect the timestamp preview format too.
|
||
- **(2026-04-05, F041 validation)** Verified with `npx eslint
|
||
packages/tickets/src/components/ticket/TicketEmailNotifications.tsx`, which exited 0. There is
|
||
no dedicated component test file for this card yet; only higher-level TicketDetails tests mock
|
||
it out.
|
||
- **(2026-04-05, F042)** Wired `ResponseStateSelect.tsx` through
|
||
`useTranslation('features/tickets')`: the dropdown option labels (`awaitingClient`,
|
||
`awaitingInternal`, `clear`), the select placeholder, the display heading, and the empty-state
|
||
`Not set` text now all resolve through `responseState.*`. Removed the stale unused
|
||
`getResponseStateLabel` import while touching the file.
|
||
- **(2026-04-05, F042 validation)** Verified with `npx eslint
|
||
packages/tickets/src/components/ResponseStateSelect.tsx`, which exited 0.
|
||
- **(2026-04-05, F043)** Wired `QuickAddCategory.tsx` through
|
||
`useTranslation('features/tickets')`: the dialog title, category/board/parent labels,
|
||
placeholders, helper copy, validation errors, create success toast, create failure fallback,
|
||
and cancel/create button labels now resolve through `settings.categories.*`,
|
||
`validation.category.*`, shared `actions.*`, and the existing category error keys. This
|
||
required a small `settings.categories` expansion in `en/fr/es/de/nl/it/pl/features/tickets.json`
|
||
for the inline creator’s helper strings (`noneTopLevelCategory`, `noBoard`, `loadingBoards`,
|
||
`boardRequiredHelp`, `parentCategoryOptional`, `selectParentCategory`,
|
||
`parentHelpWithBoard`, `parentHelpWithoutBoard`, `createSuccess`, `creating`), followed by
|
||
pseudo-locale regeneration.
|
||
- **(2026-04-05, F043 validation)** Re-ran
|
||
`node scripts/generate-pseudo-locales.cjs && node scripts/validate-translations.cjs`
|
||
(`Errors: 0`, `Warnings: 0`) and `npx eslint packages/tickets/src/components/QuickAddCategory.tsx`,
|
||
which both passed. `cd packages/tickets && npx vitest run src/components/__tests__/QuickAddCategory.test.tsx`
|
||
now passes 7/8 tests; the remaining failure is a stale board-fetch call-count assertion
|
||
(`expected 1, got 3`) rather than a localization regression.
|
||
- **(2026-04-05, F044)** `TicketDetailsContainer.tsx` does not render its own loading/not-found
|
||
chrome; the user-visible copy in this wrapper is toast/error handling. Wired those container
|
||
messages through `useTranslation('features/tickets')`: auth-required update/comment toasts,
|
||
generic ticket-updated success, batch-save success/failure, and add-comment success/failure now
|
||
resolve through `errors.*`, `messages.ticketUpdated`, `messages.commentAdded`, and
|
||
`info.changesSaved`. This required one new locale key, `messages.commentAdded`, in
|
||
`en/fr/es/de/nl/it/pl/features/tickets.json`, followed by pseudo-locale regeneration.
|
||
- **(2026-04-05, F044 validation)** Re-ran
|
||
`node scripts/generate-pseudo-locales.cjs && node scripts/validate-translations.cjs`
|
||
(`Errors: 0`, `Warnings: 0`), plus `npx eslint
|
||
packages/tickets/src/components/ticket/TicketDetailsContainer.tsx` (warnings only; the file’s
|
||
pre-existing `any` warnings remain unchanged). `cd packages/tickets && npx vitest run
|
||
src/components/ticket/__tests__/TicketDetailsContainer.description.test.tsx` still passes, while
|
||
`TicketDetailsContainerCreateTask.test.tsx` currently fails before tests run because
|
||
`next-auth` tries to import `next/server` in the package test environment.
|
||
- **(2026-04-05, F045)** Wired `CategoryPicker.tsx` through
|
||
`useTranslation('features/tickets')` without expanding the namespace: the reflection title,
|
||
default placeholder, `No Category` option, ITIL badge label, add-new label, and the selected/
|
||
excluded summary text now resolve through `categoryPicker.*`, including pluralized count copy.
|
||
- **(2026-04-05, F045 validation)** `npx eslint packages/tickets/src/components/CategoryPicker.tsx`
|
||
exited with warnings only; the remaining warnings are pre-existing (`dataAutomationType`,
|
||
unused `path`, and non-null assertions). There is no dedicated CategoryPicker render test yet;
|
||
existing coverage is limited to passthrough/source-contract tests and upstream component mocks.
|
||
- **(2026-04-05, F046)** Wired `CommentMetadataDebugModal.tsx` through
|
||
`useTranslation('features/tickets')`: dialog title, summary/raw-metadata section labels, empty
|
||
summary copy, copy-button states, and close button now resolve through the existing `debug.*`
|
||
keys. No namespace expansion was needed for this pass.
|
||
- **(2026-04-05, F046 validation)** `npx eslint
|
||
packages/tickets/src/components/ticket/CommentMetadataDebugModal.tsx` exited 0, and
|
||
`cd packages/tickets && npx vitest run src/components/ticket/CommentMetadataDebugModal.test.tsx`
|
||
still passes. The test emits the usual `react-i18next` missing-instance warning, but it does not
|
||
fail because the fallback text remains identical.
|
||
- **(2026-04-05, F047)** Wired `TicketNavigation.tsx` through
|
||
`useTranslation('features/tickets')`: the previous/next navigation buttons now source their
|
||
`aria-label` values from the existing `navigation.previousTicket` /
|
||
`navigation.nextTicket` keys instead of hardcoded English. No locale-file changes were needed
|
||
because those keys were already added during the earlier namespace expansion.
|
||
- **(2026-04-05, F047 validation)** `npx eslint
|
||
packages/tickets/src/components/ticket/TicketNavigation.tsx` exited 0.
|
||
- **(2026-04-05, F048)** Wired the visible `TicketingDashboardContainer.tsx` wrapper copy
|
||
through `useTranslation('features/tickets')`: the auth-required toast, the fetch-failure
|
||
message passed to `handleError`, and the board-name fallback used when normalizing
|
||
`effectiveOptions.boardOptions` now all resolve through existing `errors.*` /
|
||
`bulk.move.unnamedBoard` keys instead of hardcoded English.
|
||
- **(2026-04-05, F048 validation)** `npx eslint
|
||
packages/tickets/src/components/TicketingDashboardContainer.tsx` exited 0 after adding the
|
||
new `t` dependency to the fetch callback.
|
||
- **(2026-04-05, F049)** Audited `TicketOriginBadge.tsx` and `ResponseSourceBadge.tsx` against
|
||
the existing namespace and confirmed no new keys were needed: `origin.*` already covers all
|
||
badge enum values and `responseSource.*` already covers the comment-source variants used in
|
||
MSP. Both badge components now call `useTranslation('features/tickets')` so they can fall back
|
||
to localized labels even when a parent omits the `labels` prop, while still preserving caller-
|
||
provided overrides.
|
||
- **(2026-04-05, F049 validation)** `npx eslint
|
||
packages/tickets/src/components/TicketOriginBadge.tsx
|
||
packages/tickets/src/components/ResponseSourceBadge.tsx` exited 0. Focused render-contract
|
||
coverage also still passes with `cd packages/tickets && npx vitest run
|
||
src/components/TicketOriginBadge.render.test.tsx
|
||
src/components/ResponseSourceBadge.render.test.tsx`; the run emits the expected
|
||
`react-i18next` no-instance warning because these server-render tests do not mount an
|
||
`I18nProvider`, but the assertions remain green through fallback text.
|
||
- **(2026-04-05, F050)** Audited the remaining zero-string candidates and confirmed they are
|
||
N/A for translation wiring:
|
||
`TicketDetailsSkeleton.tsx`, `TicketListSkeleton.tsx`, and `AgentScheduleDrawer.tsx` render no
|
||
user-visible copy at runtime; `AgentScheduleDrawerStyles.tsx` contains CSS comments only.
|
||
`TicketDetailsSkeleton.tsx` includes descriptive JSX comments, but they do not render into the
|
||
DOM and therefore do not belong in the translation namespace.
|
||
- **(2026-04-05, F060)** Ran `node scripts/validate-translations.cjs` as the acceptance
|
||
parity check after finishing the remaining ticket-component wiring. Validation passed across
|
||
all 6 production locales plus both pseudo-locales with `Errors: 0` and `Warnings: 0`.
|
||
- **(2026-04-05, F061)** Updated the parent MSP i18n plan at
|
||
`.ai/translation/MSP_i18n_plan.md` to mark batch `2b-21a` complete. The final English
|
||
`features/tickets.json` leaf-string count is **887**, and the completion note records both the
|
||
23-component MSP wiring pass and the supporting route-namespace coverage added for
|
||
`/msp/settings` and `/msp/service-requests`.
|
||
- **(2026-04-05, F062)** Curated the scratchpad closeout notes to remove stale intermediate key
|
||
counts, record the final **887**-leaf namespace size, reaffirm the decision to keep MSP ticket
|
||
copy in the shared `features/tickets` namespace, and explicitly note that only `tests.json`
|
||
work remains after feature completion.
|
||
- **(2026-04-05, T001)** Re-ran the full lang-pack loop:
|
||
`node scripts/generate-pseudo-locales.cjs && node scripts/validate-translations.cjs`.
|
||
The generator rebuilt `52` pseudo-locale files from `26` English sources and the validator
|
||
passed with `Errors: 0`, `Warnings: 0`. The only remaining unstaged locale diffs in the
|
||
worktree are unrelated pre-existing pseudo-locale changes under `server/public/locales/xx|yy/msp/*`,
|
||
which were already present before this test pass and were intentionally left out of the commit.
|
||
- **(2026-04-05, T011)** Extended `TicketingDashboard.i18n.test.ts` with a pseudo-locale
|
||
contract for the dashboard shell and bulk-dialog chrome. The test reads
|
||
`server/public/locales/xx/features/tickets.json` directly and verifies that the visible keys
|
||
wired in `TicketingDashboard.tsx` resolve to pseudo-text (`11111`) rather than English, while
|
||
preserving `{{count}}` interpolation markers for the move/delete success strings.
|
||
- **(2026-04-05, T012)** Added a focused interpolation contract to
|
||
`TicketingDashboard.i18n.test.ts` for the bulk move/delete success toasts. The test asserts
|
||
that `TicketingDashboard.tsx` passes `count` into `t('bulk.move.success', …)` and
|
||
`t('bulk.delete.success', …)` with singular/plural default values, so the UI stays aligned
|
||
with the `_one` / `_other` locale entries instead of drifting back to manual string assembly.
|
||
- **(2026-04-05, T013)** Extended `TicketingDashboard.i18n.test.ts` again to cover the
|
||
dashboard-local loading/error/empty feedback branches: client drawer loading/not-found/load-
|
||
failed copy, bundled-ticket and deletion error paths, and the bulk move/delete “No tickets
|
||
selected” empty states now all have explicit `t(...)` source assertions.
|
||
- **(2026-04-05, T020)** Added `QuickAddTicket.i18n.test.ts` as a source-contract test for the
|
||
quick-add dialog shell. The first assertion covers the `useTranslation('features/tickets')`
|
||
hook plus the core dialog/field/placeholder wiring for title, description, client/contact/
|
||
location/board, assignee/additional-agents, category/status/priority, and due-date controls.
|
||
- **(2026-04-05, T021)** Extended `QuickAddTicket.i18n.test.ts` with the validation branch:
|
||
the required-field checks now have explicit assertions for the translated title/board/status/
|
||
priority/impact/urgency/client errors plus the `quickAdd.requiredFieldsHeading` banner text.
|
||
- **(2026-04-05, T022)** Added interpolation coverage for the quick-add tag-creation partial-
|
||
failure toast. The new source-contract assertion checks both the partial-success and catch
|
||
branches to ensure `quickAdd.tagCreatePartialFailure` always receives `count` plus singular/
|
||
plural default values.
|
||
- **(2026-04-05, T023)** Finished the QuickAdd source-contract set with a pseudo-locale check
|
||
against `xx/features/tickets.json`. The test verifies that the core dialog labels/placeholders
|
||
plus the `quickAdd.tagCreatePartialFailure_*` entries collapse to pseudo-fill values rather
|
||
than English.
|
||
- **(2026-04-05, T030)** Added `ticket/TicketInfo.i18n.test.ts` as the TicketInfo source-
|
||
contract harness. The first assertion covers the detail header and field chrome across the
|
||
mixed `fields.*`, `info.*`, `itil.*`, and `settings.display.columns.tags` keys used by the
|
||
main detail pane.
|
||
- **(2026-04-05, T031)** Extended `TicketInfo.i18n.test.ts` with the save/cancel/editing
|
||
surface: title-edit affordances, description edit controls, footer save state, unsaved-changes
|
||
discard dialog, and the pasted-images cleanup dialog now all have direct `t(...)` assertions.
|
||
- **(2026-04-05, T032)** Finished the TicketInfo contract set with an `xx` pseudo-locale
|
||
check. The test verifies that the component’s mixed `fields.*`, `info.*`, `itil.*`,
|
||
`conversation.*`, and `quickAdd.*` dependencies all resolve to pseudo-fill values instead of
|
||
leaking English defaults.
|
||
- **(2026-04-05, T040)** Added `ticket/TicketProperties.i18n.test.ts` and covered the core
|
||
side-panel chrome: time-entry/timer labels, work-description placeholder, contact/location/
|
||
agent-team headings, primary/additional-agent copy, the additional-agent picker placeholder,
|
||
and the shared quick-add team-section label all now have direct source assertions.
|
||
- **(2026-04-05, T041)** Extended `TicketProperties.i18n.test.ts` with the interactive dialog
|
||
surface from `F028`. The checklist wording still mentions a board-change reselection prompt,
|
||
but this component’s actual translated confirmation flow is the team-assignment removal/switch
|
||
dialog; the test now asserts that title, option labels, empty state, and confirm/cancel
|
||
actions all route through `t(...)`.
|
||
- **(2026-04-05, T042)** Finished the TicketProperties contract set with an `xx` pseudo-locale
|
||
check covering the side-panel headings, additional-agent picker, team-removal dialog strings,
|
||
shared action labels, and the interpolated `properties.ticketTimer` value.
|
||
- **(2026-04-05, T050)** Extended the existing
|
||
`settings/__tests__/CategoriesSettings.contract.test.ts` source-contract file with a
|
||
board-scope/tree-label assertion covering the page title, the `All Boards` scope selector
|
||
label, and the translated Name/Board/Order/Actions table headings used by the category tree.
|
||
- **(2026-04-05, T051)** The same CategoriesSettings contract file now also covers the add/edit/
|
||
delete validation and toast surface: fetch/validate/delete failures, name-required validation,
|
||
the edit-dialog title, delete-dialog entity fallback, and the save/delete success copy all have
|
||
explicit `t(...)` assertions.
|
||
- **(2026-04-05, T060)** Added `TicketExportDialog.i18n.test.ts` as a source-contract test for
|
||
the export configure step. It covers the dialog title, selected-ticket/applied-filter summary
|
||
copy, field-picker heading, select-all toggles, selected-count text, export CTA, and the
|
||
column label-key wiring that reuses `fields.*`, `properties.contact`, and
|
||
`settings.display.columns.tags`.
|
||
- **(2026-04-05, T061)** Extended the export dialog contract with the action-state and error
|
||
path: export failure handling, exporting-state copy, completion title/message, done button, and
|
||
shared cancel action are now explicitly asserted in `TicketExportDialog.tsx`.
|
||
- **(2026-04-05, T070)** Added `ticket/TicketMaterialsCard.i18n.test.ts` to cover the
|
||
materials card’s main chrome: card title, add-material dialog labels/placeholders, price/
|
||
quantity/total/description labels, materials table headers, and billed/pending status badges.
|
||
- **(2026-04-05, T071)** Extended the materials card contract with the error/validation/empty-
|
||
state path: load/add/remove failures, form validation toasts, remove-success copy, the loading
|
||
and empty helpers, and the `materials.clientRequired` guard are now all asserted.
|
||
- **(2026-04-05, T080)** Added `settings/DisplaySettings.i18n.test.ts` as a source-contract
|
||
test for the display-settings chrome: response-state tracking copy, preferences/date-time
|
||
labels, column section heading, required suffix, and tags layout toggles are all now pinned to
|
||
`t(...)` calls.
|
||
- **(2026-04-05, T081)** Extended the display-settings contract with the save path. The test now
|
||
asserts the translated success toast, failure message, saving state, and save button label.
|
||
- **(2026-04-05, T090)** Added `ticket/TicketWatchListCard.i18n.test.ts` as a source-contract
|
||
test covering the watch-list title, tabs, scope toggles, picker placeholders, add buttons,
|
||
empty state, and remove-button copy. While checking the older jsdom harness for later
|
||
regression work, I also restored its missing `useFeatureFlag` / `useTranslation` test mocks so
|
||
it can be repaired incrementally under `T120` instead of crashing immediately on mount.
|
||
- **(2026-04-05, T091)** Added `ticket/TicketSmallComponents.i18n.test.ts` and started using it
|
||
for the remaining small-ticket-component contracts. The first assertion covers the email
|
||
notifications card title, table headings, loading/empty states, unknown-error fallback, and the
|
||
load-more CTA.
|
||
- **(2026-04-05, T092)** The shared small-component contract file also now covers
|
||
`ResponseStateSelect.tsx`: awaiting-client/awaiting-internal/clear option labels, the select
|
||
placeholder, the response-state heading, and the `Not set` fallback text.
|
||
- **(2026-04-05, T093)** The same shared contract file now covers `QuickAddCategory.tsx` too:
|
||
dialog title, field labels/placeholders, board/parent helpers, validation errors, success
|
||
toast, and generic create failure copy are all asserted through `t(...)`.
|
||
- **(2026-04-05, T094)** `ticket/TicketSmallComponents.i18n.test.ts` also covers
|
||
`TicketDetailsContainer.tsx`: auth-required toasts, update/comment success messages, field-
|
||
update/save failure handling, and the added `messages.commentAdded` copy are all pinned to the
|
||
shared ticket namespace.
|
||
- **(2026-04-05, T095)** The shared small-component contract now also covers
|
||
`CategoryPicker.tsx`: title, default placeholder, `No Category`, ITIL badge, selected/excluding
|
||
summary builders, and the add-new label are all asserted against translation calls.
|
||
- **(2026-04-05, T096)** `ticket/TicketSmallComponents.i18n.test.ts` also covers
|
||
`TicketNavigation.tsx`, asserting that both previous/next button `aria-label` values resolve
|
||
through `navigation.previousTicket` / `navigation.nextTicket`.
|
||
- **(2026-04-05, T097)** Re-ran `TicketOriginBadge.render.test.tsx` after the badge fallback
|
||
wiring. All origin enum render cases still pass, which now exercises the component’s localized
|
||
fallback labels directly (with the usual no-instance `react-i18next` warning in this server-
|
||
render harness).
|
||
- **(2026-04-05, T098)** Re-ran `ResponseSourceBadge.render.test.tsx` alongside the origin badge
|
||
test. All portal/email source variants still render correctly after making the component’s
|
||
`labels` prop optional and translation-backed.
|
||
- **(2026-04-06, T120)** Repaired the pre-existing package-level ticket regression harnesses and
|
||
re-ran the planned subset successfully: `QuickAddTicket.boardScopedStatuses.test.tsx`,
|
||
`ticket-inline-add-prefill.test.tsx`, `TicketDetailsContainer.description.test.tsx`,
|
||
`TicketInfo.boardChangeStatusReselection.test.tsx`,
|
||
`TicketProperties.liveTimerPolicy.test.tsx`, `TicketWatchListCard.test.tsx`, and
|
||
`ticket-properties-inline-contact.test.tsx` now pass together (`43` tests total). The fixes were
|
||
test-only: added the missing `@alga-psa/core/server` vitest alias in
|
||
`packages/tickets/vitest.config.ts`, updated the quick-add tests to mock the current quick-add
|
||
client context and rich-text upload session, and refreshed stale watch-list expectations for the
|
||
collapsed-by-default card, tabbed add modes, filtered duplicate users, and localized badge text.
|
||
- **(2026-04-06, T121)** Re-ran the badge regression suite after the optional-`labels` fallback
|
||
wiring: `TicketOriginBadge.render.test.tsx` and `ResponseSourceBadge.render.test.tsx` both pass
|
||
unchanged (`12` tests total) under `cd packages/tickets && npx vitest run
|
||
src/components/TicketOriginBadge.render.test.tsx
|
||
src/components/ResponseSourceBadge.render.test.tsx`. The usual `react-i18next` no-instance
|
||
warning still appears in this server-render harness, but it remains non-fatal because the tests
|
||
assert the fallback labels directly.
|
||
- **(2026-04-06, T122)** Re-ran `cd packages/tickets && npx vitest run
|
||
src/components/ticket/CommentMetadataDebugModal.test.tsx`. The single debug-modal regression
|
||
test still passes after the translation wiring, again with the expected non-fatal
|
||
`react-i18next` no-instance warning from this lightweight render harness.
|
||
- **(2026-04-06, T123)** Re-ran `cd packages/tickets && npx vitest run
|
||
src/components/ticket/AgentScheduleDrawer.test.tsx`. The re-export/scheduling shim still passes
|
||
unchanged (`2` tests), confirming the `F050` zero-string audit did not disturb the drawer
|
||
wrapper.
|
||
- **(2026-04-06, T124)** Repaired and re-ran the project-task regression tests:
|
||
`TicketDetailsContainerCreateTask.test.tsx` and `TicketDetailsCreateTask.test.tsx` now pass
|
||
together (`3` tests) under `cd packages/tickets && npx vitest run
|
||
src/components/ticket/__tests__/TicketDetailsContainerCreateTask.test.tsx
|
||
src/components/ticket/__tests__/TicketDetailsCreateTask.test.tsx`. The fixes were again
|
||
test-only: add the missing `next/server` / `next-auth` / router mocks to the container harness,
|
||
align the `TicketDetailsCreateTask` child mocks with the component’s actual import paths, and
|
||
update that test to assert the current contract (`renderCreateProjectTask` is passed through to
|
||
`TicketInfo.renderProjectTaskActions`) instead of the retired header-button placement. The suite
|
||
still logs pre-existing secret/auth fallback warnings from ticket display-settings side effects,
|
||
but those no longer block execution.
|
||
- **(2026-04-06, T100)** Added
|
||
`server/src/test/unit/app/msp/tickets/page.i18n.test.tsx` as a page+layout integration harness
|
||
for `/msp/tickets`. The test runs `server/src/app/msp/tickets/page.tsx` with mocked ticket-data
|
||
actions, wraps the result in `MspLayoutClient`, and uses a lightweight in-memory
|
||
`@alga-psa/ui/lib/i18n/client` mock that only returns sentinel strings when
|
||
`I18nWrapper -> getNamespacesForRoute('/msp/tickets')` includes `features/tickets`. That makes
|
||
the assertion stronger than a source grep: the dashboard frame must render translated values for
|
||
title/add/filter/reset chrome instead of falling back to raw English literals. Verified with
|
||
`cd server && npx vitest run src/test/unit/app/msp/tickets/page.i18n.test.tsx`.
|
||
- **(2026-04-06, T101)** Extended the same `/msp/tickets` page+layout harness with a German
|
||
locale case that checks the shipped `de/features/tickets.json` copy used for the visible list
|
||
frame (`Ticket-Dashboard`, `Ticket hinzufügen`, `Alle Zuständigen`, etc.). This keeps the test
|
||
integration-focused while also pinning the route to real translated strings instead of a second
|
||
synthetic locale. Re-verified with `cd server && npx vitest run
|
||
src/test/unit/app/msp/tickets/page.i18n.test.tsx`.
|
||
- **(2026-04-06, T102)** Expanded the `/msp/tickets` page+layout harness again to include a
|
||
lightweight quick-add dialog shell keyed off the real `QuickAddTicket` translation paths. The
|
||
new German assertion covers dialog title, primary placeholders, assignee/category/status/
|
||
priority/due-date copy, and the cancel/create/create-and-view actions so the route-level test
|
||
now smoke-tests both the list frame and the add-ticket flow under `/msp/tickets`. Re-verified
|
||
with `cd server && npx vitest run src/test/unit/app/msp/tickets/page.i18n.test.tsx`.
|
||
- **(2026-04-06, T103)** Added
|
||
`server/src/test/unit/app/msp/tickets/detail.page.i18n.test.tsx` as the detail-route companion
|
||
harness for `/msp/tickets/[id]`. It runs
|
||
`server/src/app/msp/tickets/[id]/page.tsx` under `MspLayoutClient`, stubs the detail container,
|
||
and asserts German ticket-detail chrome drawn from the `TicketInfo`, `TicketProperties`,
|
||
`TicketMaterialsCard`, and `TicketWatchListCard` key families so the shared
|
||
`/msp/tickets/:id -> features/tickets` namespace path is covered end-to-end. Verified with
|
||
`cd server && npx vitest run src/test/unit/app/msp/tickets/detail.page.i18n.test.tsx`.
|
||
- **(2026-04-06, T104)** Added
|
||
`server/src/test/unit/app/msp/settings/ticketing.i18n.test.tsx` to cover the ticket-settings
|
||
route under `MspLayoutClient`. This harness keeps the real `TicketingSettings` shell and tab
|
||
routing, but stubs `CategoriesSettings` and `DisplaySettings` with `features/tickets`-backed
|
||
content so `/msp/settings -> features/tickets` is proven for the two MSP ticket settings
|
||
surfaces added in `F030`-`F037`. Verified with `cd server && npx vitest run
|
||
src/test/unit/app/msp/settings/ticketing.i18n.test.tsx`.
|
||
- **(2026-04-06, T105)** Added
|
||
`server/src/test/unit/app/msp/service-requests/editor.i18n.test.tsx` to cover the
|
||
`/msp/service-requests/[definitionId]` reuse path introduced by `F010`. The route harness mocks
|
||
`ServiceRequestDefinitionEditorPage` down to a `CategoryPicker`-shaped shell, then verifies the
|
||
German picker placeholder/badge/summary/add-new copy only renders when `MspLayoutClient` loads
|
||
`features/tickets` for `/msp/service-requests/*`. Verified with `cd server && npx vitest run
|
||
src/test/unit/app/msp/service-requests/editor.i18n.test.tsx`.
|
||
- **(2026-04-06, T106)** Added
|
||
`server/src/test/unit/layout/QuickCreateDialog.ticket.i18n.integration.test.tsx` for the
|
||
layout-level reuse of `QuickAddTicket` inside `QuickCreateDialog`. This harness mounts the real
|
||
layout dialog on a non-ticket MSP route and stubs only the embedded ticket quick-add body, so
|
||
the German assertions cover the actual global quick-create shell without pulling the entire
|
||
`QuickAddTicket` runtime into the test. Verified with `cd server && npx vitest run
|
||
src/test/unit/layout/QuickCreateDialog.ticket.i18n.integration.test.tsx`.
|
||
- **(2026-04-06, T107)** Expanded the `/msp/tickets` page harness with plural-aware bulk-action
|
||
strings. The in-memory translation mock now resolves `_one` / `_other` variants based on
|
||
`count`, and the new assertion covers the German bulk move/delete dialog titles, confirmation
|
||
copy, and success summaries (`2 Tickets verschoben`, `2 Tickets gelöscht`) so interpolation
|
||
remains aligned with the locale pack instead of falling back to manual English assembly.
|
||
Re-verified with `cd server && npx vitest run
|
||
src/test/unit/app/msp/tickets/page.i18n.test.tsx`.
|
||
- **(2026-04-06, T108)** Added
|
||
`server/src/test/unit/i18n/ticketsPseudoLocale.integration.test.tsx` as the cross-route pseudo-
|
||
locale smoke test. It drives `MspLayoutClient` under `locale='xx'` for `/msp/tickets`,
|
||
`/msp/tickets/[id]`, and `/msp/settings`, with namespace-gated `features/tickets` stubs for the
|
||
list, detail, and settings surfaces. The assertions require pseudo fill text (`11111`) and reject
|
||
the corresponding English defaults, which makes this a fast regression check for route namespace
|
||
leakage across the main MSP ticket surfaces. Verified with `cd server && npx vitest run
|
||
src/test/unit/i18n/ticketsPseudoLocale.integration.test.tsx`.
|
||
|
||
## Risks
|
||
|
||
- **Layout breakage from long translations** (German is typically 30% longer than English).
|
||
Mitigation: pseudo-locale test (xx/yy expand strings) exercises button/column widths.
|
||
- **Reused components** (QuickAddTicket in QuickCreateDialog, CategoryPicker in
|
||
ServiceRequests) may load different namespace contexts. Mitigation: i18next falls back
|
||
to loaded namespaces; verify via integration tests T105-T106.
|
||
- **Settings routes** may not load `features/tickets` namespace. Mitigation: check
|
||
ROUTE_NAMESPACES before sub-batch B; add `'features/tickets'` to `/msp/settings` entry if
|
||
needed.
|
||
- **Existing tests** may break if they rely on exact English strings. Mitigation: use
|
||
`t('key', 'Exact English')` fallback so rendered text is identical until locale changes;
|
||
update tests that assert on text to use i18next test bootstrap where necessary.
|