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

202 lines
11 KiB
Markdown

# PRD — MSP i18n Batch 2b-21a: Tickets Migration
- Slug: `2026-04-05-msp-i18n-tickets-migration`
- Date: `2026-04-05`
- Status: Draft
- Parent plan: `/.ai/translation/MSP_i18n_plan.md` (Batch 2b-21a)
## Summary
Wire the existing `features/tickets` namespace into 23 unwired MSP ticket components in
`packages/tickets/src/components/`. Infrastructure is already in place: the JSON file
(`features/tickets.json`, 147 keys across 7 languages) exists and `ROUTE_NAMESPACES` already
loads it for `/msp/tickets`. The client-portal equivalents are 100% wired; this batch closes
the MSP-side gap.
## Problem
MSP users currently see translated navigation, sidebar, dashboard, and settings chrome, but
the ticket module — one of the highest-traffic MSP surfaces — is still hardcoded English.
5 of 28 production ticket components (18%) are wired; 23 remain. Because `ROUTE_NAMESPACES`
for `/msp/tickets` already loads `features/tickets`, the translation assets are downloaded
but unused. Non-English MSP users switch from a translated sidebar into an English wall of
tickets every day.
This is not a new-translation project — it is a mechanical wiring pass plus gap-filling for
MSP-specific strings (bulk operations, dispatch hooks, display settings, dashboard filters)
that the client portal didn't need.
## Goals
1. Wire `useTranslation('features/tickets')` into all 23 unwired production MSP ticket components
2. Identify and add MSP-specific keys missing from `features/tickets.json` (bulk actions,
export options, watch list, materials, display/category settings, dashboard filters,
agent schedule drawer)
3. Regenerate translations for 6 non-English locales + 2 pseudo-locales for any new keys
4. Preserve 100% test pass rate and zero user-facing regressions
5. Measurable: MSP tickets coverage goes from 18% → 100% of production components wired
## Non-goals
- Retranslating existing 147 keys — they already have 7-language coverage and are actively used
- Moving `features/tickets.json` keys into a separate `msp/tickets.json` — the shared namespace
is the correct design per the parent plan
- Translating ticket content itself (titles, descriptions, comments) — those are tenant data,
not UI chrome
- Wiring test-only components (23 `.test.tsx` files in `packages/tickets/src/components/`)
- Extending `ROUTE_NAMESPACES``/msp/tickets` is already wired; only verify route coverage
- Translating EE-only ticket components (ITIL extras) beyond what's exported through
`@alga-psa/tickets/components`
## Users and Primary Flows
**Primary user:** MSP technicians, dispatchers, and managers using non-English UI language
(any of fr, es, de, nl, it, pl).
**Primary flows affected:**
1. `/msp/tickets` list — dashboard, filters, bulk actions, quick-add
2. `/msp/tickets/[id]` detail — ticket info, properties, materials, watch list, email notifications
3. `/msp/settings/*` for ticket configuration — categories, display settings, response states
4. `/msp/service-requests/*` — CategoryPicker reused from tickets package
5. Quick-create dialog (global nav) — QuickAddTicket reused in `layout/QuickCreateDialog`
## UX / UI Notes
- No visual changes. Text replaced inline via `t('key', 'English fallback')`.
- Toast messages (`toast.success` / `toast.error`) also translated.
- `throw new Error('...')` strings that surface to users (handled by error boundaries) translated;
those that only appear in logs stay in English.
- Skeletons, badges, and style-only files (`TicketOriginBadge`, `ResponseSourceBadge`,
`TicketListSkeleton`, `TicketDetailsSkeleton`, `AgentScheduleDrawerStyles`) have no or 0-1
strings — confirm and skip if truly zero.
## Requirements
### Functional Requirements
**Sub-batch A: Large dashboard + quick-add components (4 files, ~150-200 strings)**
| Component | LOC | Est. strings | Key content |
|-----------|-----|--------------|-------------|
| TicketingDashboard.tsx | 2,024 | ~65 | Page title, add button, filter chrome (status/priority/assignee/response state/due date), bulk actions (move/delete/bundle), empty states, toast messages |
| QuickAddTicket.tsx | 1,596 | ~50 | Dialog title, field labels, placeholders, validation errors, toast messages, tag-creation errors |
| ticket/TicketInfo.tsx | 1,587 | ~45 | Ticket detail header, field labels, action buttons, confirmations |
| ticket/TicketProperties.tsx | 1,234 | ~45 | Side panel labels, inline edit controls, status/priority/board selects, assignment UI |
**Sub-batch B: Settings + mid-size (4 files, ~90 strings)**
| Component | LOC | Est. strings | Key content |
|-----------|-----|--------------|-------------|
| settings/CategoriesSettings.tsx | 865 | ~30 | Board/channel scoping, category tree, add/edit/delete dialogs |
| TicketExportDialog.tsx | 243 | ~25 | Export format options, column picker, date range, toast messages |
| ticket/TicketMaterialsCard.tsx | 444 | ~20 | Materials list, add material dialog, cost display, confirmations |
| settings/DisplaySettings.tsx | 250 | ~20 | Column visibility, sort options, per-user preferences |
**Sub-batch C: Small + utility components (15 files, ~50-70 strings)**
| Component | LOC | Est. strings | Notes |
|-----------|-----|--------------|-------|
| ticket/TicketWatchListCard.tsx | 512 | ~10 | Watcher list, add/remove |
| ticket/TicketEmailNotifications.tsx | 172 | ~8 | Notification toggle labels |
| ResponseStateSelect.tsx | 110 | ~6 | Response state options |
| QuickAddCategory.tsx | 274 | ~6 | Inline category creation |
| ticket/TicketDetailsContainer.tsx | 252 | ~5 | Loading/error states |
| CategoryPicker.tsx | 274 | ~5 | Picker placeholder + empty state |
| ticket/CommentMetadataDebugModal.tsx | 97 | ~4 | Debug modal (dev only, low priority) |
| ticket/TicketNavigation.tsx | 143 | ~3 | Prev/next buttons |
| TicketingDashboardContainer.tsx | 607 | ~2 | Mostly logic, few strings |
| ticket/TicketDetailsSkeleton.tsx | 155 | 0 | Skeleton — confirm zero |
| TicketOriginBadge.tsx | 143 | 0-2 | Origin labels — may need small key set |
| ticket/AgentScheduleDrawerStyles.tsx | 93 | 0 | Styles only |
| ResponseSourceBadge.tsx | 56 | 0-2 | Source labels — may need small key set |
| TicketListSkeleton.tsx | 32 | 0 | Skeleton |
| ticket/AgentScheduleDrawer.tsx | 16 | 0 | Re-export shim |
**Namespace key gaps to fill (preliminary):**
The current `features/tickets.json` covers client-portal needs. Likely MSP gaps:
- `bulk.*` — bulk move/delete/bundle actions and their toast messages
- `dashboard.*` — page title, bulk action bar, "N tickets selected"
- `export.*` — export dialog labels, format options
- `settings.categories.*` — board-scoped category management
- `settings.display.*` — column visibility, sort preferences
- `watchList.*` — watcher add/remove, empty state
- `materials.*` — materials add/edit, cost labels
- `quickAdd.*` — extended quickAdd labels beyond existing `create.*`
- `properties.*` — inline-edit side panel labels (many overlap with `fields.*` — reuse where possible)
- `errors.*` — toast error strings (session required, permission denied, partial failure)
- `validation.*` — form validation messages beyond existing `create.errors.*`
Final gap list determined during implementation — run pseudo-locale tests to surface missing keys.
### Non-functional Requirements
1. **No regressions:** all existing ticket tests pass after migration
2. **Lang-pack validation:** after every namespace edit, run
`node scripts/generate-pseudo-locales.cjs && node scripts/validate-translations.cjs`
and commit only when green. The validator covers key parity, missing/extra keys,
`{{variable}}` preservation, pseudo-locale fill patterns, and Italian accent preservation
in a single run. Pseudo-locale files (`xx/`, `yy/`) are **always** regenerated from
English — never hand-edited.
3. **Naming convention:** follow existing `features/tickets.json` patterns (camelCase, nested under semantic groups)
4. **Fallback-safe:** all `t()` calls use `t('key', 'English fallback')` signature so missing keys don't break UI
5. **Shared with client portal:** before adding a key, check if existing `features/tickets` key covers it
## Data / API / Integrations
- No database changes
- No API changes
- No new npm dependencies
- Reuses existing `useTranslation` hook from `@alga-psa/ui/lib/i18n/client`
- Reuses existing i18next infrastructure loaded via `I18nWrapper` (already in MSP layout)
## Security / Permissions
No change. Translation is a pure presentation-layer concern.
## Observability
N/A — no new operational concerns.
## Rollout / Migration
- **Flag:** work proceeds without feature-flag gating because `I18nWrapper` in MSP layout
already forces English fallback when `msp-i18n-enabled` is off (see parent plan Phase 0.7)
- **Per-PR rollout:** ship sub-batches A/B/C as independent PRs to keep review scope manageable
- **Deploy path:** translations are static JSON served from `server/public/locales/`; no cache
invalidation beyond standard Next.js static-asset rebuild
- **Back-out:** each PR is independently revertable; components continue rendering English
via `defaultValue` fallbacks even if JSON keys are reverted
## Open Questions
1. Should `bulk.*`, `dashboard.*`, `export.*`, `settings.*` keys live in `features/tickets.json`
or be extracted to a new `msp/ticketing.json` namespace? **Tentative answer:** keep in
`features/tickets.json` since client portal may eventually surface similar controls
(bulk operations on client-side ticket list). Revisit after sub-batch A if the file
grows past ~250 keys.
2. For toast messages using template literals (e.g., `` `${N} tickets moved` ``), use
i18next interpolation `t('bulk.moved', { count: N })` or keep template string with `t()`
for the static part? **Tentative answer:** interpolation — matches existing patterns in
`features/tickets.json` `messages.*`.
3. For `throw new Error('User must be logged in')` — translate or leave English?
**Tentative answer:** translate only if the error surfaces to user via toast/UI;
leave English if caught and rethrown by error boundaries or logged only.
## Acceptance Criteria (Definition of Done)
- [ ] All 23 unwired production MSP ticket components either (a) import `useTranslation`
and wrap all user-visible strings, or (b) are confirmed to have zero user-visible
strings (skeletons, styles, re-exports)
- [ ] `features/tickets.json` contains all keys referenced by MSP ticket components
- [ ] `node scripts/generate-pseudo-locales.cjs && node scripts/validate-translations.cjs`
exits 0 (covers key parity across 9 locales, pseudo-locale fill patterns, Italian
accent preservation, and `{{variable}}` interpolation preservation)
- [ ] All existing ticket-related unit/integration tests pass
- [ ] Visual smoke test: `/msp/tickets`, `/msp/tickets/[id]`, `/msp/settings/ticketing`,
`/msp/service-requests/[id]` render correctly in `en` and at least one non-English
locale (de or fr recommended); `xx` pseudo-locale shows pseudo-text for every visible
string (no bare English leakage)
- [ ] Parent plan `.ai/translation/MSP_i18n_plan.md` updated: sub-batch 2b-21a marked ✅