Hermes 284313f908
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
Initial import of AlgaPSA codebase from PSA server
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz

Source: /opt/alga-psa on psa.joliet.tech
2026-06-22 16:12:17 -05:00

824 lines
62 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 tooltips 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 dashboards 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 files 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 files 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
files 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 tables `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 creators 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 files
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 components 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 components 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 cards 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 components 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 components
`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 components 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.