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
784 lines
53 KiB
Markdown
784 lines
53 KiB
Markdown
# Scratchpad — MSP i18n Batch 2b-21b/c: Projects + Project Templates Migration
|
||
|
||
- Plan slug: `2026-04-05-msp-i18n-projects-migration`
|
||
- Created: `2026-04-05`
|
||
- **Status: ✅ COMPLETE** (2026-04-08)
|
||
|
||
## What This Is
|
||
|
||
Mechanical wiring pass: ~60 unwired MSP project + project-template components ×
|
||
`useTranslation(['features/projects', 'common'])`. Shared namespace (128 keys, 9 locales)
|
||
already exists and is already loaded by `ROUTE_NAMESPACES['/msp/projects']`. The
|
||
`templates.*` subtree (17 keys) is live. Client-portal side is 100% wired (9/9) — this
|
||
closes the MSP gap.
|
||
|
||
Consolidates parent plan's 2b-21b (projects, 45 files) and 2b-21c (project-templates,
|
||
15 files) because they share package, namespace, and patterns.
|
||
|
||
## Decisions
|
||
|
||
- **(2026-04-05)** Keep project-templates strings in existing `features/projects.json`
|
||
under `templates.*` subtree rather than creating a new `features/project-templates.json`
|
||
namespace. Rationale: already-wired components (`TemplateStatusColumnsStep`,
|
||
`TemplateStatusManager`, `ProjectTaskStatusSettings`) use `features/projects` with
|
||
`templates.*` keys and it's working. Splitting would fragment the namespace needlessly.
|
||
Supersedes the parent plan's tentative "new `features/project-templates` namespace" idea.
|
||
- **(2026-04-05)** Use array multi-namespace form:
|
||
`useTranslation(['features/projects', 'common'])`. Matches all 5 already-wired
|
||
components. Generic actions (Save/Cancel/Delete/Confirm) pull from `common` via
|
||
`common:` prefix.
|
||
- **(2026-04-05)** Ship sub-batches A (projects core, 15 files), B (project-templates,
|
||
15 files), C (settings + small, 30 files) as independent PRs.
|
||
- **(2026-04-05)** Template wizard strings nested per-step
|
||
(`templates.wizard.basics.*`, `templates.wizard.tasks.*`, etc.) rather than flat.
|
||
Matches existing onboarding wizard pattern in `msp/onboarding.json`.
|
||
- **(2026-04-05)** Task document strings — prefer reusing `features/documents`
|
||
keys where they match (upload/download/attach/remove). Only add
|
||
`features/projects.taskDocuments.*` for project-task-specific copy. Verify
|
||
`features/documents` is loaded transitively on `/msp/projects/[id]` routes.
|
||
- **(2026-04-05)** Translate toast messages, inline validation, and user-visible error
|
||
strings. Do NOT translate `throw new Error('...')` strings caught by error boundaries
|
||
or logged only.
|
||
|
||
## Discoveries / Constraints
|
||
|
||
- **(2026-04-05)** `features/projects.json` top-level groups: title, subtitle,
|
||
searchPlaceholder, allStatuses, resetFilters, active, completed, onHold, timeline,
|
||
milestones, phasesAndTasks, kanbanView, listView, task, tasks (15), phases (7),
|
||
settings (22), templates (17), documents (13), team, budget, fields (12), status (5),
|
||
messages (5), backToProjects, invalidProjectData, plus ~16 leaf fields. Total: 128 keys.
|
||
- **(2026-04-05)** `ROUTE_NAMESPACES` entries that load `features/projects`:
|
||
- `/client-portal/projects` — already works
|
||
- `/msp/projects` — loads `['common', 'msp/core', 'features/projects']`
|
||
- `/msp/settings` — loads `['common', 'msp/core', 'msp/settings', 'msp/admin', 'msp/email-providers', 'features/projects']` (already includes it!)
|
||
- `/msp/billing` — loads `['common', 'msp/core', 'features/billing', 'msp/reports']` (does not include projects — if any project component is rendered on billing, fix needed)
|
||
- **(2026-04-05)** Templates routes NOT in ROUTE_NAMESPACES:
|
||
- `/msp/projects/templates`
|
||
- `/msp/projects/templates/[templateId]`
|
||
- `/msp/projects/templates/create`
|
||
Should match-best against `/msp/projects` and inherit its namespaces. Verify.
|
||
- **(2026-04-05)** Already-wired MSP project components (reference patterns):
|
||
- `PhaseListItem.tsx` → `useTranslation('features/projects')` (single-namespace form)
|
||
- `TemplateStatusManager.tsx` → `useTranslation(['features/projects', 'common'])` (array form)
|
||
- `TemplateStatusColumnsStep.tsx` → `useTranslation(['features/projects', 'common'])`
|
||
- `ProjectTaskStatusSettings.tsx` → `useTranslation(['features/projects', 'common'])`
|
||
- `TaskComment.tsx` → `useTranslation('common')`
|
||
**Preferred: array form** — matches 3 of 5, supports `common:` prefix for shared keys.
|
||
- **(2026-04-05)** Largest files dominate the string count:
|
||
- `ProjectDetail.tsx` 3,038 LOC / ~50 strings
|
||
- `TemplateEditor.tsx` 2,315 LOC / ~28 strings
|
||
- `TaskForm.tsx` 2,024 LOC / ~39 strings
|
||
- `TaskListView.tsx` 1,320 LOC / ~9 strings
|
||
- `PhaseTaskImportDialog.tsx` 1,290 LOC / ~21 strings
|
||
- `TemplateTaskForm.tsx` 1,015 LOC / ~22 strings
|
||
- `Projects.tsx` 980 LOC / ~21 strings
|
||
- `TemplateTaskListView.tsx` 953 LOC / ~8 strings
|
||
These 8 files alone are ~12,900 LOC and ~198 strings of the estimated ~450 total.
|
||
- **(2026-04-05)** Rough string estimates (heuristic undercount):
|
||
- Sub-batch A (projects core): 15 files, ~250 strings
|
||
- Sub-batch B (project-templates): 15 files, ~150 strings
|
||
- Sub-batch C (settings + small): 30 files, ~80-100 strings
|
||
- **Total: ~480 strings** (realistic: 500-650)
|
||
- **(2026-04-05)** `ProjectQuickAdd` reused in global quick-create:
|
||
`server/src/components/layout/QuickCreateDialog.tsx`. Must work in both contexts.
|
||
- **(2026-04-05)** Zero-string components to verify: StatusColumn, TaskCommentThread,
|
||
KanbanBoard, ClientPortalConfigEditor, ProjectPage, KanbanZoomControl, TaskCommentForm,
|
||
DonutChart, TaskQuickAdd, TaskEdit, HoursProgressBar, ProjectActiveToggle,
|
||
TaskPrioritySettings. Most are layout/style-only or re-export shims.
|
||
- **(2026-04-05)** `ProjectSettings` is exported from `@alga-psa/projects/components` and
|
||
imported by `server/src/components/settings/SettingsPage.tsx` (Settings page wiring).
|
||
- **(2026-04-07, F001 audit)** Existing `features/projects` keys are enough for:
|
||
base projects list chrome (`title`, `subtitle`, `searchPlaceholder`, `allStatuses`,
|
||
`resetFilters`), generic project fields (`fields.*`), summary cards
|
||
(`taskCompletion`, `budgetHours`, `hoursUsage`, etc.), base task table headers
|
||
(`tasks.*`), phase shell (`phases.*`), attachments shell (`documents.*`),
|
||
template status-column management (`templates.statuses.*`), and project/phase status
|
||
settings (`settings.statuses.*`).
|
||
- **(2026-04-07, F001 audit)** Confirmed missing MSP key groups for sub-batch A:
|
||
`projectList.*` (filters, empty-state CTAs, row actions, deletion toasts),
|
||
`quickAdd.*`, `projectDetail.*` (header actions, tabs, metrics, phase actions, search),
|
||
`taskForm.*` (field labels/placeholders, validation, checklist/deletion/move/duplicate
|
||
confirmations, prefill copy), `taskDependencies.*`, `taskTicketLinks.*`,
|
||
`materials.*`, `export.*`, `import.*`, `dialogs.*`, and `filters.deadline.*`.
|
||
- **(2026-04-07, F001 audit)** Confirmed missing MSP key groups for sub-batch B:
|
||
`templates.list.*`, `templates.create.*`, `templates.apply.*`, `templates.detail.*`,
|
||
`templates.editor.*`, `templates.taskForm.*`, and `templates.wizard.*` with nested
|
||
per-step groups (`basics`, `phases`, `tasks`, `review`, `clientPortal`).
|
||
- **(2026-04-07, F001 audit)** Confirmed missing MSP key groups for sub-batch C:
|
||
`settings.statuses.tenant.*` / project-settings page copy, plus small dialog/filter
|
||
leaf keys reused by `CreateTaskFromTicketDialog`, `LinkTicketToTaskDialog`,
|
||
`MoveTaskDialog`, `DuplicateTaskDialog`, `ProjectInfo`, `ProjectTaskStatusSelector`,
|
||
`ProjectPhases`, `TaskStatusSelect`, `TicketSelect`, and `TaskTypeSelector`.
|
||
- **(2026-04-07, F001 audit)** Reuse decisions from current inventory:
|
||
keep attachments copy under existing `documents.*` where strings match; add
|
||
`taskDocuments.*` only for task-specific actions if required.
|
||
Keep shared generic buttons in `common`.
|
||
Reuse `tasks.*`, `fields.*`, `phases.*`, and `status.*` before adding narrower keys.
|
||
Keep all template-related strings in `features/projects` under `templates.*`.
|
||
- **(2026-04-07, F001 audit)** Representative concrete gaps seen in code:
|
||
`Projects.tsx` needs translations for `Projects`, search/filter placeholders,
|
||
`Open menu`, and delete success toast.
|
||
`ProjectQuickAdd.tsx` / `ProjectDetailsEdit.tsx` need full form labels, placeholders,
|
||
unsaved/save confirmation copy, and portal visibility label.
|
||
`TaskDependencies.tsx` lacks keys for section title, dependency editor actions, and
|
||
task picker placeholders.
|
||
`TaskTicketLinks.tsx` lacks keys for duplicate/invalid ticket toasts, section title,
|
||
link/create dialog labels, and ticket search filters.
|
||
`TaskDocumentsSimple.tsx` lacks keys for auth/validation toasts, create/upload/link
|
||
buttons, remove actions, document-name placeholder, and unsaved-change dialog.
|
||
`PhaseTaskImportDialog.tsx` needs a large `import.*` subtree for CSV instructions,
|
||
mapping labels, preview stats, unmatched agents/statuses, and completion summaries.
|
||
- **(2026-04-07, F002)** Expanded `server/public/locales/en/features/projects.json`
|
||
from 128 leaf keys to 665 leaf keys. Added new top-level groups:
|
||
`projectList`, `quickAdd`, `edit`, `projectDetail`, `taskForm`,
|
||
`taskDependencies`, `taskTicketLinks`, `taskDocuments`, `materials`, `export`,
|
||
`import`, `dialogs`, `filters`, `projectInfo`, `projectPhases`, `selectors`,
|
||
plus large `settings.*` and `templates.*` extensions.
|
||
- **(2026-04-07, F002)** Chose pragmatic scaffolding over late piecemeal key creation:
|
||
the English namespace now contains concrete fallbacks for all plan groups, including
|
||
template list/create/apply/detail/editor/wizard surfaces and project-settings/task-
|
||
status-library surfaces. Later component wiring can mostly reuse these keys and only
|
||
add narrowly missing leaves if a file surfaces unexpected copy.
|
||
- **(2026-04-07, F003)** Propagated the expanded `features/projects` tree into
|
||
`fr/es/de/nl/it/pl` by deep-merging each existing locale over the new English source.
|
||
Result: all six real locales now preserve their pre-existing translated values while
|
||
gaining the full expanded key set for parity with English. Newly introduced leaves that
|
||
did not previously exist are currently seeded from English; this keeps validation and
|
||
wiring unblocked and preserves the prior human translations intact.
|
||
- **(2026-04-07, F004)** Ran `node scripts/generate-pseudo-locales.cjs` after the
|
||
namespace expansion. Regenerated `xx/features/projects.json` and
|
||
`yy/features/projects.json`; the run also refreshed `xx/common.json` because the pseudo
|
||
generator rewrites every pseudo-locale file from current English sources in one pass.
|
||
- **(2026-04-07, validation)** `node scripts/validate-translations.cjs` passes after the
|
||
locale propagation + pseudo generation (8 locales checked, 0 errors, 0 warnings).
|
||
- **(2026-04-07, F005)** Verified template-route namespace loading with the actual
|
||
resolver via `node_modules/.bin/tsx -e ...getNamespacesForRoute(...)`.
|
||
Results:
|
||
`/msp/projects/templates` → `["common","msp/core","features/projects"]`
|
||
`/msp/projects/templates/123` → `["common","msp/core","features/projects"]`
|
||
`/msp/projects/templates/create` → `["common","msp/core","features/projects"]`
|
||
No explicit `ROUTE_NAMESPACES` entries are needed; longest-prefix matching against
|
||
`/msp/projects` already loads `features/projects` correctly.
|
||
- **(2026-04-07, F020)** Wired `packages/projects/src/components/ProjectDetail.tsx`
|
||
to `useTranslation(['features/projects', 'common'])`. Translated the project-detail
|
||
header/view controls, search/filter chrome, sticky-status/pin controls, selected-phase
|
||
completion summary, empty-state guidance, confirmation dialogs, and the main toast copy
|
||
for task/phase move/update/delete/import flows.
|
||
- **(2026-04-07, F020)** Added the missing `projectDetail.*` leaves required by the
|
||
`ProjectDetail.tsx` wiring to English, re-synced `fr/es/de/nl/it/pl` via the merge
|
||
script, regenerated pseudo-locales, and re-ran translation validation successfully.
|
||
- **(2026-04-07, F020 check)** `node_modules/.bin/eslint
|
||
packages/projects/src/components/ProjectDetail.tsx` passes with pre-existing warnings
|
||
only (no new lint errors introduced by the i18n wiring).
|
||
- **(2026-04-07, F021)** Wired `packages/projects/src/components/TaskForm.tsx` to
|
||
`useTranslation(['features/projects', 'common'])`. Localized the form labels,
|
||
placeholders, save/delete/time-entry actions, validation copy, checklist chrome,
|
||
document/ticket cleanup confirmations, dependency-unsaved prompt, and task-level toast
|
||
/ error fallback copy for move/save/delete/duplicate/agent flows.
|
||
- **(2026-04-07, F021)** Extended `taskForm.*` with the missing leaves surfaced by the
|
||
TaskForm audit: field labels (`descriptionLabel`, `dueDateLabel`, `taskTypeLabel`,
|
||
`priorityLabel`), picker/help copy (`noService`, `addTeamMembers`, `loading`),
|
||
confirmation/dialog strings (`deleteMessage`, `moveMessage`, `cancelMessage`,
|
||
`dependencyUnsavedMessage`, keep/delete document/ticket actions), and operational
|
||
fallback/toast strings (`saveFailed`, `deleteFailed`, `moveFailed`, `duplicateFailed`,
|
||
`linkingPartialFailure`, `tagCreationPartialFailure`, `prepareTimeEntryFailed`, etc.).
|
||
- **(2026-04-07, F021)** Re-synced `fr/es/de/nl/it/pl` by deep-merging each locale over
|
||
the updated English `features/projects` tree, regenerated `xx/yy` via
|
||
`node scripts/generate-pseudo-locales.cjs`, and re-validated translations successfully.
|
||
- **(2026-04-07, F021 check)** `node_modules/.bin/eslint
|
||
packages/projects/src/components/TaskForm.tsx` passes with pre-existing warnings only
|
||
(25 warnings, 0 errors). The new i18n wiring did not add fresh lint failures.
|
||
- **(2026-04-07, F022)** Wired `packages/projects/src/components/PhaseTaskImportDialog.tsx`
|
||
to `useTranslation(['features/projects', 'common'])`. Localized the upload step,
|
||
field-mapping table, preview summary/table chrome, invalid-row / unmatched-agent /
|
||
unmatched-status guidance, large-import confirmations, resolution workflows, importing
|
||
spinner, and completion summaries.
|
||
- **(2026-04-07, F022)** Extended `import.*` with the dialog-specific gaps surfaced by the
|
||
audit: CSV read/process/import fallback errors, required/optional field lists,
|
||
table/tooltip labels, large-import confirmation copy, unmatched agent/status warnings,
|
||
next-step/import button text, row-limit description, task-count summaries, and
|
||
`import.fields.*` labels so the field-mapping UI no longer depends on hardcoded English
|
||
constants from `TASK_IMPORT_FIELDS`.
|
||
- **(2026-04-07, F022)** Re-synced `fr/es/de/nl/it/pl` from the updated English source,
|
||
regenerated pseudo-locales, and re-ran `node scripts/generate-pseudo-locales.cjs &&
|
||
node scripts/validate-translations.cjs` successfully.
|
||
- **(2026-04-07, F022 check)** `node_modules/.bin/eslint
|
||
packages/projects/src/components/PhaseTaskImportDialog.tsx` passes with pre-existing
|
||
warnings only (6 warnings, 0 errors).
|
||
- **(2026-04-07, F023)** Wired `packages/projects/src/components/Projects.tsx` to
|
||
`useTranslation(['features/projects', 'common'])`. Localized the page title, create
|
||
actions, filter placeholders, table headers, row-value fallbacks, screen-reader menu
|
||
label, reset button, and the delete-success / delete-validation fallback copy.
|
||
- **(2026-04-07, F023)** Extended `projectList.*` with the list-specific gaps surfaced by
|
||
the table audit: `columns.*`, `statusOptions.*`, row fallback values (`noClient`,
|
||
`noContact`, `unassigned`, `notAvailable`, `thisProject`), and delete-validation /
|
||
delete-failure messages used by `DeleteEntityDialog`.
|
||
- **(2026-04-07, F023)** Re-synced `fr/es/de/nl/it/pl`, regenerated pseudo-locales, and
|
||
re-ran `node scripts/generate-pseudo-locales.cjs && node scripts/validate-translations.cjs`
|
||
successfully after the `projectList.*` additions.
|
||
- **(2026-04-07, F023 check)** `node_modules/.bin/eslint
|
||
packages/projects/src/components/Projects.tsx` passes with pre-existing warnings only
|
||
(17 warnings, 0 errors).
|
||
- **(2026-04-07, F024)** Wired `packages/projects/src/components/TaskDocumentsSimple.tsx`
|
||
to `useTranslation(['features/projects', 'common'])`. Localized the attachments section
|
||
header, create/upload/link controls, empty state, remove/download/save flows, drawer
|
||
titles and placeholders, file-attachment viewer copy, folder-selector prompt, and the
|
||
unsaved-changes confirmation dialog shown above the task drawer.
|
||
- **(2026-04-07, F024)** Extended `taskDocuments.*` with the missing attachment-surface
|
||
leaves surfaced by the audit: `attachmentsTitle`, short button labels (`newButton`,
|
||
`uploadButton`, `linkButton`), empty-state/fallback names, load/create/save/remove/
|
||
download failure messages, folder-selection copy, PDF label, and unsaved-change
|
||
confirm strings.
|
||
- **(2026-04-07, F024)** Re-synced `fr/es/de/nl/it/pl`, regenerated pseudo-locales, and
|
||
re-ran `node scripts/generate-pseudo-locales.cjs && node scripts/validate-translations.cjs`
|
||
successfully after the `taskDocuments.*` additions.
|
||
- **(2026-04-07, F024 check)** `node_modules/.bin/eslint
|
||
packages/projects/src/components/TaskDocumentsSimple.tsx` passes with pre-existing
|
||
warnings only (10 warnings, 0 errors).
|
||
- **(2026-04-07, F025)** Wired `packages/projects/src/components/TaskTicketLinks.tsx`
|
||
to `useTranslation(['features/projects', 'common'])`. Localized the associated-tickets
|
||
section title, link/create actions, link-existing dialog chrome, filter labels and
|
||
active-filter chips, select-ticket prompt, cancel/link actions, quick-create checkbox,
|
||
and ticket-link toast / error fallback copy.
|
||
- **(2026-04-07, F025)** Extended `taskTicketLinks.*` with the remaining filter/dialog
|
||
leaves surfaced by the audit: category / board / priority labels, active-chip templates,
|
||
`selectTicketPlaceholder`, error fallbacks for link/remove/new-ticket flows, and small
|
||
fallbacks like `clientFallback` and `defaultNewStatus`.
|
||
- **(2026-04-07, F025)** Re-synced `fr/es/de/nl/it/pl`, regenerated pseudo-locales, and
|
||
re-ran `node scripts/generate-pseudo-locales.cjs && node scripts/validate-translations.cjs`
|
||
successfully after the `taskTicketLinks.*` additions.
|
||
- **(2026-04-07, F025 check)** `node_modules/.bin/eslint
|
||
packages/projects/src/components/TaskTicketLinks.tsx` passes with pre-existing warnings
|
||
only (13 warnings, 0 errors).
|
||
- **(2026-04-07, F026)** Wired `packages/projects/src/components/TaskDependencies.tsx`
|
||
to `useTranslation(['features/projects', 'common'])`. Localized the dependency section
|
||
title, dependency-type labels, add/edit placeholders, action-button titles, empty
|
||
state, and inline error fallbacks for add/remove/update flows in both edit and pending
|
||
modes.
|
||
- **(2026-04-07, F026)** Added the only missing namespace leaf surfaced by the audit:
|
||
`taskDependencies.updateError`, used when replacing an existing dependency target fails.
|
||
- **(2026-04-07, F026)** Re-synced `fr/es/de/nl/it/pl`, regenerated pseudo-locales, and
|
||
re-ran `node scripts/generate-pseudo-locales.cjs && node scripts/validate-translations.cjs`
|
||
successfully after the `taskDependencies.updateError` addition.
|
||
- **(2026-04-07, F026 check)** `node_modules/.bin/eslint
|
||
packages/projects/src/components/TaskDependencies.tsx` passes with pre-existing
|
||
warnings only (14 warnings, 0 errors).
|
||
- **(2026-04-07, F027)** Wired `packages/projects/src/components/ProjectMaterialsDrawer.tsx`
|
||
to `useTranslation(['features/projects', 'common'])`. Localized the drawer header,
|
||
add-form labels and placeholders, loading/empty states, add/remove toast copy, table
|
||
headers/status badges, and the unbilled-total summary.
|
||
- **(2026-04-07, F027)** Extended `materials.*` with the small gaps surfaced by the
|
||
component audit: product-search copy, add/remove failure messages, `adding` /
|
||
`addMaterial`, and `unknownProduct`.
|
||
- **(2026-04-07, F027)** Re-synced `fr/es/de/nl/it/pl`, regenerated pseudo-locales, and
|
||
re-ran `node scripts/generate-pseudo-locales.cjs && node scripts/validate-translations.cjs`
|
||
successfully after the `materials.*` additions.
|
||
- **(2026-04-07, F027 check)** `node_modules/.bin/eslint
|
||
packages/projects/src/components/ProjectMaterialsDrawer.tsx` passes cleanly with 0
|
||
warnings / 0 errors after wrapping the translation helper in `useCallback`.
|
||
- **(2026-04-07, F028)** Wired `packages/projects/src/components/ProjectTaskExportDialog.tsx`
|
||
to `useTranslation(['features/projects', 'common'])`. Localized the dialog title,
|
||
phase/field selection headers, select-all toggles, selected-count summaries, export /
|
||
exporting / completion copy, and the export-field checkbox labels via `export.fields.*`.
|
||
- **(2026-04-07, F028)** Added the small missing `export.done` leaf so the completion CTA
|
||
stays `"Done"` instead of drifting to a generic close label.
|
||
- **(2026-04-07, F028)** Re-synced `fr/es/de/nl/it/pl`, regenerated pseudo-locales, and
|
||
re-ran `node scripts/generate-pseudo-locales.cjs && node scripts/validate-translations.cjs`
|
||
successfully after the `export.done` addition.
|
||
- **(2026-04-07, F028 check)** `node_modules/.bin/eslint
|
||
packages/projects/src/components/ProjectTaskExportDialog.tsx` passes with pre-existing
|
||
warnings only (2 warnings, 0 errors).
|
||
- **(2026-04-07, F029)** Wired `packages/projects/src/components/ProjectQuickAdd.tsx`
|
||
to `useTranslation(['features/projects', 'common'])`. Localized the quick-add dialog
|
||
title, field labels/placeholders, validation banner/errors, project-status add-new
|
||
affordance, client-portal section header, and create/cancel button + toast copy used
|
||
both on `/msp/projects` and in the global quick-create dialog.
|
||
- **(2026-04-07, F029)** No new locale keys were required. Existing `quickAdd.*`,
|
||
`settings.statuses.addStatus`, and `common:actions.*` leaves fully covered the dialog,
|
||
so this item was a pure component-wiring pass with English fallbacks preserved.
|
||
- **(2026-04-07, F029 check)** `node_modules/.bin/eslint
|
||
packages/projects/src/components/ProjectQuickAdd.tsx` passes with a pre-existing hooks
|
||
warning only (1 warning, 0 errors).
|
||
- **(2026-04-07, F030)** Wired `packages/projects/src/components/ProjectDetailsEdit.tsx`
|
||
to `useTranslation(['features/projects', 'common'])`. Localized the edit-form labels,
|
||
placeholders, validation banner, active/inactive status chip, client-portal section
|
||
header, save/cancel confirmation dialogs, and the success/failure/save-button copy.
|
||
- **(2026-04-07, F030)** Added one narrow locale leaf, `projectEdit.updateError`, so the
|
||
update failure path stays under the project-edit namespace instead of falling back to a
|
||
generic common save-error message. Re-synced `fr/es/de/nl/it/pl`, regenerated pseudo-
|
||
locales, and re-ran translation validation successfully.
|
||
- **(2026-04-07, F030 check)** `node_modules/.bin/eslint
|
||
packages/projects/src/components/ProjectDetailsEdit.tsx` passes with a pre-existing
|
||
hooks warning only (1 warning, 0 errors).
|
||
- **(2026-04-07, F031)** Wired `packages/projects/src/components/PrefillFromTicketDialog.tsx`
|
||
to `useTranslation(['features/projects', 'common'])`. Localized the dialog title,
|
||
search/filter labels, active-filter chips, ticket selector label, link-checkbox copy,
|
||
reset/cancel action labels, and the confirm CTA.
|
||
- **(2026-04-07, F031)** Reused the existing `taskTicketLinks.*` filter chrome instead of
|
||
adding a parallel prefill-specific subtree for shared ticket filter labels. Added only
|
||
`dialogs.prefillFromTicket.confirm` for the domain-specific confirm button text, then
|
||
re-synced `fr/es/de/nl/it/pl`, regenerated pseudo-locales, and re-ran translation
|
||
validation successfully.
|
||
- **(2026-04-07, F031 check)** `node_modules/.bin/eslint
|
||
packages/projects/src/components/PrefillFromTicketDialog.tsx` passes with pre-existing
|
||
warnings only (4 warnings, 0 errors).
|
||
- **(2026-04-07, F032)** Wired `packages/projects/src/components/TaskListView.tsx` to
|
||
`useTranslation(['features/projects', 'common'])`. Localized the responsive table
|
||
headers, hidden-columns alert, phase empty state, phase/task add buttons, badge/date/
|
||
completion chrome, expand/collapse affordances, checklist/dependency tooltip copy, and
|
||
task action titles.
|
||
- **(2026-04-07, F032)** Added a small list-view extension to the namespace:
|
||
`projectDetail.hiddenColumnsAlert`, `projectDetail.listViewEmptyMessage`,
|
||
`projectDetail.seeMore`, `projectDetail.seeLess`, `projectDetail.checklistItems`,
|
||
`projectDetail.checklistSummary`, `projectDetail.unknownUser`, `projectDetail.blocksLabel`,
|
||
plus `taskDependencies.dependsOn`, `taskDependencies.unknownTask`, and
|
||
`projectPhases.addPhase`. Re-synced `fr/es/de/nl/it/pl`, regenerated pseudo-locales,
|
||
and re-ran translation validation successfully.
|
||
- **(2026-04-07, F032 check)** `node_modules/.bin/eslint
|
||
packages/projects/src/components/TaskListView.tsx` passes with pre-existing warnings
|
||
only (4 warnings, 0 errors).
|
||
- **(2026-04-07, F033)** Wired `packages/projects/src/components/TaskCard.tsx` to
|
||
`useTranslation(['features/projects', 'common'])`. Localized the card ARIA label,
|
||
quick-actions menu/sr-only copy, priority tooltip, due-date chrome, see-more toggles,
|
||
additional-agent / checklist / dependency tooltips, hide-tags control, and critical-path
|
||
badge.
|
||
- **(2026-04-07, F033)** Added a narrow kanban/task-card extension under
|
||
`projectDetail.*`: `taskCardAria`, `taskActions`, `priorityLevel`, `dueLabel`,
|
||
`noDueDate`, `hideTags`, and `criticalPath`. Re-synced `fr/es/de/nl/it/pl`,
|
||
regenerated pseudo-locales, and re-ran translation validation successfully.
|
||
- **(2026-04-07, F033 check)** `node_modules/.bin/eslint
|
||
packages/projects/src/components/TaskCard.tsx` passes with pre-existing warnings only
|
||
(117 warnings, 0 errors).
|
||
- **(2026-04-07, F034)** Wired `packages/projects/src/components/PhaseQuickAdd.tsx`
|
||
to `useTranslation(['features/projects', 'common'])`. Localized the dialog title,
|
||
inline validation, phase-name/description placeholders, date labels/placeholders, save
|
||
and cancel actions, and the add-phase failure fallback.
|
||
- **(2026-04-07, F034)** Added the small phase-quick-add leaves under `projectPhases.*`:
|
||
`phaseNamePlaceholder`, `descriptionPlaceholder`, `adding`, and `addError`. Re-synced
|
||
`fr/es/de/nl/it/pl`, regenerated pseudo-locales, and re-ran translation validation
|
||
successfully.
|
||
- **(2026-04-07, F034 check)** `node_modules/.bin/eslint
|
||
packages/projects/src/components/PhaseQuickAdd.tsx` passes cleanly with 0 warnings /
|
||
0 errors.
|
||
- **(2026-04-07, F050)** Wired `packages/projects/src/components/project-templates/TemplateEditor.tsx`
|
||
to `useTranslation(['features/projects', 'common'])`, including the embedded
|
||
`TemplateStatusColumn` and template-task-card helpers in the same file. Localized the
|
||
editor shell, apply/actions menu, delete confirmations, client-portal dialog, phases
|
||
sidebar, kanban header controls, empty states, status-column add buttons, and all task-
|
||
card menu/tooltip chrome.
|
||
- **(2026-04-07, F050)** Expanded `templates.editor.*` with the editor-specific gaps
|
||
surfaced by the audit: failure toasts (`deleteFailed`, `clientPortalSaveFailed`,
|
||
`addPhaseFailed`, `updatePhaseFailed`, `deletePhaseFailed`, `moveTaskFailed`,
|
||
`reorderPhaseFailed`, `taskSaveFailed`, `deleteTaskFailed`, `updateAssigneeFailed`),
|
||
delete-confirmation messages, badge/action labels, sidebar guidance, status-column
|
||
summary copy, list/kanban empty states, phase-duration summaries, task-card
|
||
expand/collapse labels, and fallback labels like `statusFallback`, `unknownUser`, and
|
||
`unknownTask`. Re-synced `fr/es/de/nl/it/pl`, regenerated pseudo-locales, and re-ran
|
||
translation validation successfully.
|
||
- **(2026-04-07, F050 check)** `node_modules/.bin/eslint
|
||
packages/projects/src/components/project-templates/TemplateEditor.tsx` passes with
|
||
pre-existing warnings only (8 warnings, 0 errors).
|
||
- **(2026-04-07, F051)** Wired `packages/projects/src/components/project-templates/TemplateTaskForm.tsx`
|
||
to `useTranslation(['features/projects', 'common'])`. Localized the create/edit title,
|
||
all field labels/placeholders, validation and save-error copy, checklist/dependency
|
||
section chrome, additional-agent guidance, form action buttons, and the unsaved-change
|
||
confirmation dialog.
|
||
- **(2026-04-07, F051)** Extended `templates.taskForm.*` only where the existing subtree
|
||
had gaps: `addAction`, `updateAction`, `saving`, `saveFailed`, `taskNameRequired`,
|
||
`addChecklistItem`, `dependenciesHelp`, `cancelEditMessage`, `discardChanges`,
|
||
`continueEditing`, and `additionalAgentsHelp`. Re-synced `fr/es/de/nl/it/pl`,
|
||
regenerated pseudo-locales, and re-ran translation validation successfully.
|
||
- **(2026-04-07, F051 check)** `node_modules/.bin/eslint
|
||
packages/projects/src/components/project-templates/TemplateTaskForm.tsx` passes with
|
||
pre-existing warnings only (3 warnings, 0 errors).
|
||
- **(2026-04-07, F052)** Wired
|
||
`packages/projects/src/components/project-templates/wizard-steps/TemplateTasksStep.tsx`
|
||
to `useTranslation(['features/projects', 'common'])`. Localized the step title and
|
||
description, phase selector, empty states, task editor labels/placeholders, service and
|
||
assignment copy, checklist controls, inline validation, per-task summary text, add-task
|
||
CTA, and the concluding tip callout.
|
||
- **(2026-04-07, F052)** Extended `templates.wizard.tasks.*` only for wizard-step-specific
|
||
gaps: `noTasksInPhase`, `thisPhase`, `durationSummaryShort`, `noPriority`,
|
||
`checklistItemsSummary`, `tipDescription`, `done`, and `addTaskToPhase`. Re-synced
|
||
`fr/es/de/nl/it/pl`, regenerated pseudo-locales, and re-ran translation validation
|
||
successfully.
|
||
- **(2026-04-07, F052 check)** `node_modules/.bin/eslint
|
||
packages/projects/src/components/project-templates/wizard-steps/TemplateTasksStep.tsx`
|
||
passes cleanly with 0 warnings / 0 errors.
|
||
- **(2026-04-08, F053)** Wired `packages/projects/src/components/project-templates/ApplyTemplateDialog.tsx`
|
||
to `useTranslation(['features/projects', 'common'])`. Localized the dialog title,
|
||
validation banner, template/project/client/status/start-date fields, customization
|
||
options, assignment radio labels, add-status affordance, submit/cancel actions, and
|
||
success/error toast copy for load/apply flows.
|
||
- **(2026-04-08, F053)** Extended `templates.apply.*` only where the dialog surfaced
|
||
small gaps or English drift: added `create`, `creating`, and `createFailed`, and
|
||
aligned the existing English values for `startDateLabel` and `assignmentOptions.*`
|
||
to the current UI text so the migration stays fallback-safe without changing the
|
||
rendered English copy. Re-synced `fr/es/de/nl/it/pl`, regenerated pseudo-locales,
|
||
and re-ran translation validation successfully.
|
||
- **(2026-04-08, F053 check)** `node_modules/.bin/eslint
|
||
packages/projects/src/components/project-templates/ApplyTemplateDialog.tsx` passes
|
||
with a pre-existing hooks warning only (1 warning, 0 errors).
|
||
- **(2026-04-08, F054)** Wired `packages/projects/src/components/project-templates/ProjectTemplatesList.tsx`
|
||
to `useTranslation(['features/projects', 'common'])`. Localized the page title,
|
||
toolbar buttons, search/category filters, table column headers, never-used fallback,
|
||
row-action menu labels, delete-confirm dialog, loading state, and the user-facing
|
||
load/delete error-handler copy.
|
||
- **(2026-04-08, F054)** Extended `templates.list.*` with the only missing list-page
|
||
leaves surfaced by the audit: `loadFailed` and `deleteFailed`. Re-synced
|
||
`fr/es/de/nl/it/pl`, regenerated pseudo-locales, and re-ran translation validation
|
||
successfully.
|
||
- **(2026-04-08, F054 check)** `node_modules/.bin/eslint
|
||
packages/projects/src/components/project-templates/ProjectTemplatesList.tsx` passes
|
||
with pre-existing warnings only (unused `getTemplateCategories` import, existing
|
||
`loadData` hooks warning; 3 warnings, 0 errors).
|
||
- **(2026-04-08, F055)** Wired `packages/projects/src/components/project-templates/CreateTemplateDialog.tsx`
|
||
to `useTranslation(['features/projects', 'common'])`. Localized the dialog title,
|
||
source-project / template-name / description / category fields, copy-options section,
|
||
create/cancel actions, success toast, and the load/create error-handler copy.
|
||
- **(2026-04-08, F055)** Extended `templates.create.*` with the only missing dialog
|
||
leaves surfaced by the audit: `loadFailed` and `createFailed`. Re-synced
|
||
`fr/es/de/nl/it/pl`, regenerated pseudo-locales, and re-ran translation validation
|
||
successfully.
|
||
- **(2026-04-08, F055 check)** `node_modules/.bin/eslint
|
||
packages/projects/src/components/project-templates/CreateTemplateDialog.tsx` passes
|
||
cleanly with 0 warnings / 0 errors.
|
||
- **(2026-04-08, F056)** Wired `packages/projects/src/components/project-templates/TemplateTaskListView.tsx`
|
||
to `useTranslation(['features/projects', 'common'])`. Localized the responsive table
|
||
headers, hidden-columns alert, empty state, phase/task add CTAs, untitled-phase/task-
|
||
count chrome, phase timing summaries, dependency/checklist/additional-agent tooltips,
|
||
unassigned fallback, status fallback, and edit/delete task action titles.
|
||
- **(2026-04-08, F056)** Reused existing `tasks.*`, `projectPhases.*`,
|
||
`taskDependencies.*`, `projectDetail.*`, and `templates.editor.*` leaves where
|
||
possible. Added only `templates.editor.noPhasesFound`, `untitledPhase`,
|
||
`taskCount_one`, and `taskCount_other` for the template list-view-specific fallbacks.
|
||
Re-synced `fr/es/de/nl/it/pl`, regenerated pseudo-locales, and re-ran translation
|
||
validation successfully.
|
||
- **(2026-04-08, F056 check)** `node_modules/.bin/eslint
|
||
packages/projects/src/components/project-templates/TemplateTaskListView.tsx` passes
|
||
with pre-existing warnings only (unused `taskTypes`, `priorities`, and
|
||
`getAssigneeName`; 6 warnings, 0 errors).
|
||
- **(2026-04-08, F057)** Wired
|
||
`packages/projects/src/components/project-templates/wizard-steps/TemplateReviewStep.tsx`
|
||
to `useTranslation(['features/projects', 'common'])`. Localized the step title and
|
||
intro, template-information labels, status-columns header, task summary cards,
|
||
task-details-by-phase heading, checklist-item count suffix, and the final ready-to-
|
||
create callout.
|
||
- **(2026-04-08, F057)** Extended `templates.wizard.review.*` with only the review-step
|
||
gaps surfaced by the audit: `descriptionLabel` for the summary block and
|
||
`readyDescription` for the final creation callout. Re-synced `fr/es/de/nl/it/pl`,
|
||
regenerated pseudo-locales, and re-ran translation validation successfully.
|
||
- **(2026-04-08, F057 check)** `node_modules/.bin/eslint
|
||
packages/projects/src/components/project-templates/wizard-steps/TemplateReviewStep.tsx`
|
||
passes with pre-existing warnings only (unused `Layers` import and unused `index`
|
||
callback arg; 4 warnings, 0 errors).
|
||
- **(2026-04-08, F058)** Wired
|
||
`packages/projects/src/components/project-templates/wizard-steps/TemplatePhasesStep.tsx`
|
||
to `useTranslation(['features/projects', 'common'])`. Localized the step title and
|
||
intro, empty state, phase-form labels/placeholders/help copy, validation message,
|
||
done/cancel actions, duration/start/task summaries, add-phase buttons, reorder hint,
|
||
recalculate CTAs, and the phase-timing explainer alert.
|
||
- **(2026-04-08, F058)** Extended `templates.wizard.phases.*` for the wizard-step gaps
|
||
only: `intro`, `addFirstPhase`, `phaseNameRequired`, `daysAfterProjectStart`,
|
||
`tasksCount`, reorder/recalculate copy, and the structured about-timing labels/help
|
||
text. Re-synced `fr/es/de/nl/it/pl`, regenerated pseudo-locales, and re-ran
|
||
translation validation successfully.
|
||
- **(2026-04-08, F058 check)** `node_modules/.bin/eslint
|
||
packages/projects/src/components/project-templates/wizard-steps/TemplatePhasesStep.tsx`
|
||
passes cleanly with 0 warnings / 0 errors.
|
||
- **(2026-04-08, F059)** Wired `packages/projects/src/components/project-templates/CreateTemplateForm.tsx`
|
||
to `useTranslation(['features/projects', 'common'])`. Localized the page title,
|
||
field labels/placeholders, create/cancel actions, validation toast, success toast,
|
||
and create-failure error-handler copy.
|
||
- **(2026-04-08, F059)** No new locale keys were required. The existing
|
||
`templates.create.*` subtree fully covered the standalone create page after the earlier
|
||
dialog work, so this item was a pure component-wiring pass.
|
||
- **(2026-04-08, F059 check)** `node_modules/.bin/eslint
|
||
packages/projects/src/components/project-templates/CreateTemplateForm.tsx` passes
|
||
cleanly with 0 warnings / 0 errors.
|
||
- **(2026-04-08, F060)** Wired `packages/projects/src/components/project-templates/TemplateDetail.tsx`
|
||
to `useTranslation(['features/projects', 'common'])`, including the inline `TaskCard`
|
||
helper. Localized the back/use/delete actions, delete confirmation, success/failure
|
||
delete flows, template metadata labels, project-phases sidebar, phase header/timing
|
||
summaries, status-column empty state, status fallback, and the phase-selection empty
|
||
state in the kanban area.
|
||
- **(2026-04-08, F060)** Added one narrow detail-page leaf,
|
||
`templates.detail.selectPhase`, for the kanban empty-state prompt. Re-synced
|
||
`fr/es/de/nl/it/pl`, regenerated pseudo-locales, and re-ran translation validation
|
||
successfully.
|
||
- **(2026-04-08, F060 check)** `node_modules/.bin/eslint
|
||
packages/projects/src/components/project-templates/TemplateDetail.tsx` passes with
|
||
pre-existing warnings only (unused dropdown-menu imports and unused
|
||
`onTemplateUpdated`; 12 warnings, 0 errors).
|
||
- **(2026-04-08, F061)** Wired
|
||
`packages/projects/src/components/project-templates/wizard-steps/TemplateClientPortalStep.tsx`
|
||
to `useTranslation('features/projects')`. Localized the step title, intro paragraph,
|
||
and the explanatory info alert above `ClientPortalConfigEditor`.
|
||
- **(2026-04-08, F061)** Extended `templates.wizard.clientPortal.*` with the only
|
||
missing step leaves: `description` and `aboutDescription`. Re-synced
|
||
`fr/es/de/nl/it/pl`, regenerated pseudo-locales, and re-ran translation validation
|
||
successfully.
|
||
- **(2026-04-08, F061 check)** `node_modules/.bin/eslint
|
||
packages/projects/src/components/project-templates/wizard-steps/TemplateClientPortalStep.tsx`
|
||
passes cleanly with 0 warnings / 0 errors.
|
||
- **(2026-04-08, F062)** Wired `packages/projects/src/components/project-templates/TemplateCreationWizard.tsx`
|
||
to `useTranslation(['features/projects', 'common'])`. Localized the wizard dialog
|
||
title, `WizardProgress` step labels, finish CTA, and the fallback load/validation/
|
||
create error strings surfaced by the shell.
|
||
- **(2026-04-08, F062)** Extended the wizard namespace with only two shell-level leaves:
|
||
`templates.wizard.title` and `templates.wizard.errors.createFailed`. Re-synced
|
||
`fr/es/de/nl/it/pl`, regenerated pseudo-locales, and re-ran translation validation
|
||
successfully.
|
||
- **(2026-04-08, F062 check)** `node_modules/.bin/eslint
|
||
packages/projects/src/components/project-templates/TemplateCreationWizard.tsx` passes
|
||
with pre-existing warnings only (unused exported type imports; 10 warnings, 0 errors).
|
||
- **(2026-04-08, F063)** Wired
|
||
`packages/projects/src/components/project-templates/wizard-steps/TemplateBasicsStep.tsx`
|
||
to `useTranslation('features/projects')`. Localized the template-name/description/
|
||
category labels, placeholders, help copy, and the "What's Next?" info alert.
|
||
- **(2026-04-08, F063)** Extended `templates.wizard.basics.*` with the missing step
|
||
leaves surfaced by the audit: `nameLabel`, `nameHelp`, `descriptionLabel`,
|
||
`descriptionHelp`, `categoryLabel`, `categoryHelp`, and `nextHintDescription`.
|
||
Re-synced `fr/es/de/nl/it/pl`, regenerated pseudo-locales, and re-ran translation
|
||
validation successfully.
|
||
- **(2026-04-08, F063 check)** `node_modules/.bin/eslint
|
||
packages/projects/src/components/project-templates/wizard-steps/TemplateBasicsStep.tsx`
|
||
passes cleanly with 0 warnings / 0 errors.
|
||
- **(2026-04-08, F064)** Confirmed
|
||
`packages/projects/src/components/project-templates/AddTemplateDialog.tsx` is a zero-
|
||
string wrapper around `TemplateCreationWizard`. It introduces no user-visible copy and
|
||
requires no i18n wiring of its own, so this item is N/A by design.
|
||
- **(2026-04-08, F080)** Wired
|
||
`packages/projects/src/components/settings/projects/TenantProjectTaskStatusSettings.tsx`
|
||
to `useTranslation(['features/projects', 'common'])`. Localized the task-status-library
|
||
title/description, create/import CTAs, loading and empty states, closed badge, edit/
|
||
delete actions, create/edit dialog title, status-name/preview/color/icon chrome,
|
||
closed-status checkbox help, submit/cancel actions, and the save/delete/import toast /
|
||
error-handler / confirm-dialog copy.
|
||
- **(2026-04-08, F080)** Extended `settings.statuses.*` with the tenant-library-specific
|
||
gaps surfaced by the audit: task-library description, import button/error copy, dialog
|
||
preview labels, color/icon field labels, closed-status help text, delete-confirmation
|
||
message, and update/save helper copy. Re-synced `fr/es/de/nl/it/pl`, regenerated
|
||
pseudo-locales, and re-ran translation validation successfully.
|
||
- **(2026-04-08, F080 check)** `node_modules/.bin/eslint
|
||
packages/projects/src/components/settings/projects/TenantProjectTaskStatusSettings.tsx`
|
||
passes with pre-existing warnings only (`any` usages in legacy import/conflict code;
|
||
8 warnings, 0 errors).
|
||
- **(2026-04-08, F081)** Wired
|
||
`packages/projects/src/components/settings/projects/ProjectStatusSettings.tsx` to
|
||
`useTranslation(['features/projects', 'common'])`. Localized the table headers, open/
|
||
closed labels and project-status hints, actions-menu SR copy, card title/description,
|
||
add/import buttons, delete-dialog fallback entity name, and the update/delete/import
|
||
toast / error / validation feedback.
|
||
- **(2026-04-08, F081)** Extended `settings.statuses.*` with the small project-status
|
||
page gaps surfaced by the audit: `open`, `order`, project-status description/hints,
|
||
delete-validation failure, and `this_status`. Re-synced `fr/es/de/nl/it/pl`,
|
||
regenerated pseudo-locales, and re-ran translation validation successfully.
|
||
- **(2026-04-08, F081 check)** `node_modules/.bin/eslint
|
||
packages/projects/src/components/settings/projects/ProjectStatusSettings.tsx` passes
|
||
with pre-existing warnings only (legacy non-null assertion and `any` usages; 4
|
||
warnings, 0 errors). Also fixed the new `useCallback(... t ...)` dependency warning
|
||
introduced by the i18n wiring.
|
||
- **(2026-04-08, F082)** Wired `packages/projects/src/components/ProjectTaskStatusEditor.tsx`
|
||
to `useTranslation(['features/projects', 'common'])`. Localized the inline label,
|
||
loading state, customize controls, available-statuses prompt, add/remove/reorder
|
||
feedback, closed badge, empty state, move/remove titles, and the ordering help text.
|
||
- **(2026-04-08, F082)** Extended `settings.statuses.*` with the narrow inline-editor
|
||
leaves surfaced by the audit: task/phase labels, customize/project/phase helper copy,
|
||
available-statuses heading, task-status load/add/remove/reorder errors, and the
|
||
arrange-order hint. Re-synced `fr/es/de/nl/it/pl`, regenerated pseudo-locales, and
|
||
re-ran translation validation successfully.
|
||
- **(2026-04-08, F082 check)** `node_modules/.bin/eslint
|
||
packages/projects/src/components/ProjectTaskStatusEditor.tsx` passes with pre-existing
|
||
warnings only (legacy non-null assertions; 2 warnings, 0 errors).
|
||
- **(2026-04-08, F083)** Wired
|
||
`packages/projects/src/components/CreateTaskFromTicketDialog.tsx` to
|
||
`useTranslation(['features/projects', 'common'])`. Localized the launcher button,
|
||
dialog title, project/phase/status labels and placeholders, link-ticket checkbox, and
|
||
create/cancel actions.
|
||
- **(2026-04-08, F083)** Extended `dialogs.createTaskFromTicket.*` only for the missing
|
||
launcher/field-label leaves: `button`, `projectLabel`, `phaseLabel`, and
|
||
`statusLabel`. Re-synced `fr/es/de/nl/it/pl`, regenerated pseudo-locales, and re-ran
|
||
translation validation successfully.
|
||
- **(2026-04-08, F083 check)** `node_modules/.bin/eslint
|
||
packages/projects/src/components/CreateTaskFromTicketDialog.tsx` passes with a pre-
|
||
existing hooks warning only (`ticket.client_id` effect dependency; 1 warning, 0
|
||
errors).
|
||
- **(2026-04-08, F084)** Wired `packages/projects/src/components/LinkTicketToTaskDialog.tsx`
|
||
to `useTranslation(['features/projects', 'common'])`. Localized the launcher button,
|
||
dialog title, project/phase/task labels and placeholders, cancel/link actions, and the
|
||
success/error link feedback.
|
||
- **(2026-04-08, F084)** Extended `dialogs.linkTicketToTask.*` with the missing launcher
|
||
and field/button leaves: `button`, `projectLabel`, `phaseLabel`, `taskLabel`,
|
||
`linking`, and `confirm`. Re-synced `fr/es/de/nl/it/pl`, regenerated pseudo-locales,
|
||
and re-ran translation validation successfully.
|
||
- **(2026-04-08, F084 check)** `node_modules/.bin/eslint
|
||
packages/projects/src/components/LinkTicketToTaskDialog.tsx` passes with a pre-
|
||
existing hooks warning only (`ticket.client_id` effect dependency; 1 warning, 0
|
||
errors).
|
||
- **(2026-04-08, F085)** Wired `packages/projects/src/components/DeadlineFilter.tsx` to
|
||
`useTranslation(['features/projects', 'common'])`. Localized the trigger placeholder /
|
||
display text, filter-type options, date labels, and clear/apply actions.
|
||
- **(2026-04-08, F085)** No new locale keys were required. The existing
|
||
`filters.deadline.*` subtree fully covered the component, so this item was a pure
|
||
wiring pass.
|
||
- **(2026-04-08, F085 check)** `node_modules/.bin/eslint
|
||
packages/projects/src/components/DeadlineFilter.tsx` passes cleanly with 0 warnings /
|
||
0 errors.
|
||
- **(2026-04-08, F086)** Wired `packages/projects/src/components/settings/ProjectSettings.tsx`
|
||
to `useTranslation('features/projects')`. Localized the page title and the four top-
|
||
level tab labels for project numbering, project statuses, task statuses, and task
|
||
priorities.
|
||
- **(2026-04-08, F086)** Extended `settings.page.*` with a small `tabs.*` group for the
|
||
settings-shell navigation labels. Re-synced `fr/es/de/nl/it/pl`, regenerated pseudo-
|
||
locales, and re-ran translation validation successfully.
|
||
- **(2026-04-08, F086 check)** `node_modules/.bin/eslint
|
||
packages/projects/src/components/settings/ProjectSettings.tsx` passes cleanly with 0
|
||
warnings / 0 errors.
|
||
- **(2026-04-08, F087)** Wired `packages/projects/src/components/ProjectTaskStatusSelector.tsx`
|
||
to `useTranslation(['features/projects', 'common'])`. Localized the label, customize
|
||
helper copy, add-existing/create-new CTAs, available-statuses heading, closed badge,
|
||
move/remove titles, empty-state hint, and ordering help text.
|
||
- **(2026-04-08, F087)** Extended `settings.statuses.*` only for the selector-specific
|
||
CTAs: `add_existing` and `create_new`. Re-synced `fr/es/de/nl/it/pl`, regenerated
|
||
pseudo-locales, and re-ran translation validation successfully.
|
||
- **(2026-04-08, F087 check)** `node_modules/.bin/eslint
|
||
packages/projects/src/components/ProjectTaskStatusSelector.tsx` passes with a pre-
|
||
existing hooks warning only (legacy initialization effect dependencies; 1 warning, 0
|
||
errors).
|
||
- **(2026-04-08, F088)** Wired `packages/projects/src/components/MoveTaskDialog.tsx` to
|
||
`useTranslation(['features/projects', 'common'])`. Localized the dialog title, body
|
||
message, tree-select placeholder, validation toasts, cancel action, and confirm/
|
||
moving button text.
|
||
- **(2026-04-08, F088)** Extended `dialogs.moveTask.*` only for the submit-button copy:
|
||
`moving` and `confirm`. Re-synced `fr/es/de/nl/it/pl`, regenerated pseudo-locales, and
|
||
re-ran translation validation successfully.
|
||
- **(2026-04-08, F088 check)** `node_modules/.bin/eslint
|
||
packages/projects/src/components/MoveTaskDialog.tsx` passes with pre-existing warnings
|
||
only (unused legacy import/state/catch param; 6 warnings, 0 errors).
|
||
|
||
## Commands / Runbooks
|
||
|
||
### The lang-pack loop (run after every namespace edit)
|
||
|
||
```bash
|
||
node scripts/generate-pseudo-locales.cjs && node scripts/validate-translations.cjs
|
||
```
|
||
|
||
- Regenerates `xx/` and `yy/` from English source (NEVER hand-edit pseudo-locales)
|
||
- Validates key parity, `{{variable}}` tokens, pseudo-locale fill patterns, Italian accents
|
||
- Exit code 0 = pass. Keep 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|warning|info)\(['\"]|throw new Error\(['\"]" <file>
|
||
```
|
||
- List all unwired MSP project components (excluding templates):
|
||
```bash
|
||
for f in $(find packages/projects/src/components -type f -name "*.tsx" ! -name "*.test.tsx" ! -path "*/project-templates/*"); do
|
||
grep -qE "useTranslation" "$f" || echo "$f"
|
||
done
|
||
```
|
||
- List all unwired project-template components:
|
||
```bash
|
||
for f in $(find packages/projects/src/components/project-templates -type f -name "*.tsx" ! -name "*.test.tsx"); do
|
||
grep -qE "useTranslation" "$f" || echo "$f"
|
||
done
|
||
```
|
||
- Reference already-wired files (copy the pattern):
|
||
```
|
||
packages/projects/src/components/project-templates/TemplateStatusManager.tsx
|
||
packages/projects/src/components/project-templates/wizard-steps/TemplateStatusColumnsStep.tsx
|
||
packages/projects/src/components/settings/projects/ProjectTaskStatusSettings.tsx
|
||
```
|
||
- Verify a route's namespaces (from packages/core/src/lib/i18n/config.ts):
|
||
```bash
|
||
grep -A 1 "'/msp/projects'" packages/core/src/lib/i18n/config.ts
|
||
```
|
||
|
||
## Links / References
|
||
|
||
- Parent plan: `.ai/translation/MSP_i18n_plan.md` (Batches 2b-21b, 2b-21c)
|
||
- Sibling plan: `ee/docs/plans/2026-04-05-msp-i18n-tickets-migration/` (2b-21a)
|
||
- Shared namespace file: `server/public/locales/en/features/projects.json`
|
||
- Route config: `packages/core/src/lib/i18n/config.ts` (ROUTE_NAMESPACES)
|
||
- Validation script: `scripts/validate-translations.cjs`
|
||
- Pseudo-locale generator: `scripts/generate-pseudo-locales.cjs`
|
||
- Pattern reference (already wired, array form):
|
||
- `packages/projects/src/components/project-templates/TemplateStatusManager.tsx`
|
||
- `packages/projects/src/components/project-templates/wizard-steps/TemplateStatusColumnsStep.tsx`
|
||
- `packages/projects/src/components/settings/projects/ProjectTaskStatusSettings.tsx`
|
||
- Precedent plan (similar wiring-only work): `ee/docs/plans/2026-03-20-msp-i18n-clients-assets-onboarding/`
|
||
- MSP template pages (server-side wiring):
|
||
- `server/src/app/msp/projects/templates/page.tsx`
|
||
- `server/src/app/msp/projects/templates/[templateId]/page.tsx`
|
||
- `server/src/app/msp/projects/templates/create/page.tsx`
|
||
- Global quick-create integration:
|
||
`server/src/components/layout/QuickCreateDialog.tsx`
|
||
- Settings page integration:
|
||
`server/src/components/settings/SettingsPage.tsx`
|
||
|
||
## Open Questions
|
||
|
||
- Does `/msp/projects/templates*` load `features/projects` via best-match to `/msp/projects`?
|
||
**Action:** verify at start of sub-batch B via T108. If not, add explicit
|
||
`ROUTE_NAMESPACES` entries for `/msp/projects/templates`,
|
||
`/msp/projects/templates/create`, `/msp/projects/templates/[templateId]`.
|
||
- `features/documents` loading on `/msp/projects/[id]` — does `TaskDocumentsSimple`
|
||
render translated document strings? **Action:** check if
|
||
`ROUTE_NAMESPACES['/msp/projects']` needs `'features/documents'` added.
|
||
- Task dependency cycle warnings — reuse existing `features/projects` keys or need new
|
||
`taskDependencies.cycle.*`? **Action:** check during F026 implementation.
|
||
- Kanban drag helpers (ARIA labels) — translate or leave English (accessibility-only text
|
||
may follow different conventions)? **Tentative answer:** translate; aria-labels are
|
||
user-facing for screen reader users and should match UI locale.
|
||
|
||
## Implementation Order (recommended)
|
||
|
||
1. **Setup (F001-F005):** Audit gaps, extend `en/features/projects.json`, translate to 6
|
||
non-English locales (IT accent audit), regenerate pseudo-locales via script, verify
|
||
template route namespaces. This unblocks all component wiring.
|
||
2. **Sub-batch A PR (F020-F034):** 15 largest project components. Ship first while
|
||
context is fresh. Start with ProjectDetail + TaskForm as they dominate.
|
||
3. **Sub-batch B PR (F050-F064):** 15 project-template components. Wizard steps + editor.
|
||
4. **Sub-batch C PR (F080-F094):** 30 small/settings components. Quick cleanup pass,
|
||
confirm zero-string components.
|
||
5. **Closeout (F100-F102):** Final lang-pack loop, update parent plan, archive scratchpad.
|
||
|
||
## Risks
|
||
|
||
- **Layout breakage from long translations.** German ~30% longer than English; French
|
||
task/phase labels expand button widths. Mitigation: pseudo-locale (xx/yy expand strings)
|
||
test exercises kanban column widths, task card layouts, wizard nav buttons.
|
||
- **Reused components in multiple contexts.** `ProjectQuickAdd` (global QuickCreate),
|
||
`ProjectSettings` (settings page), `LinkTicketToTaskDialog`/`CreateTaskFromTicketDialog`
|
||
(tickets + projects both). Mitigation: integration tests T110-T111.
|
||
- **Missing templates namespace loading.** If `/msp/projects/templates*` doesn't match
|
||
`/msp/projects` best-match, templates pages render untranslated even with components
|
||
wired. Mitigation: **do F005 before sub-batch B**.
|
||
- **Existing tests asserting English text.** Many components have `.test.tsx` files that
|
||
may assert exact English strings. Mitigation: use `t('key', 'Exact English')` fallback
|
||
so rendered text is identical until locale changes; update tests that break.
|
||
- **Template wizard shared state.** Wizard passes step data via parent context — ensure
|
||
`useTranslation` works in all 5 step components independently without state drift.
|
||
|
||
## Final Status (2026-04-08)
|
||
|
||
**All features implemented (F001–F102).**
|
||
|
||
- **Final `en/features/projects.json` key count:** 1,122 leaf keys (up from 128 at start)
|
||
- **Components wired:** ~63 (15 sub-batch A + 15 sub-batch B + ~33 sub-batch C including
|
||
3 discovered non-zero components)
|
||
- **Confirmed zero-string:** StatusColumn, KanbanBoard, ProjectPage, KanbanZoomControl,
|
||
DonutChart, TaskQuickAdd, TaskEdit, HoursProgressBar, TaskPrioritySettings (9 files)
|
||
- **Discovered non-zero (wired):** TaskCommentThread (~8 strings), TaskCommentForm
|
||
(~3 strings), ProjectActiveToggle (~3 strings) — originally listed as zero-string in PRD
|
||
- **Deferred:** ClientPortalConfigEditor.tsx — has many user-visible strings for configuring
|
||
client portal visibility of project data, but is client-portal config UI. Scope for a
|
||
future pass.
|
||
- **Validation:** `node scripts/generate-pseudo-locales.cjs && node scripts/validate-translations.cjs`
|
||
passes (0 errors, 0 warnings)
|
||
- **Parent plan updated:** `.ai/translation/MSP_i18n_plan.md` — 2b-21b/c marked ✅ DONE
|