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

238 lines
12 KiB
Markdown

# PRD — MSP i18n Batches: Dispatch, Reports, Admin, Time Entry
- Slug: `2026-03-20-msp-i18n-dispatch-reports-admin-time`
- Date: `2026-03-20`
- Status: Draft
## Summary
Translate four MSP feature areas in priority order:
| Batch | Namespace | Strings | Files | Location |
|-------|-----------|---------|-------|----------|
| 2b-5 | `msp/dispatch` | ~120 | 13 | `packages/scheduling/src/components/technician-dispatch/` |
| 2b-7 | `msp/reports` | ~99 | 4 | `packages/billing/src/components/billing-dashboard/reports/` + `packages/ui/src/pages/Reports.tsx` |
| 2b-8 | `msp/admin` | ~123 | 4 | `packages/ui/src/components/settings/admin/` + `packages/integrations/src/components/email/admin/` |
| 2b-3 | `msp/time-entry` | ~161 | 33 | `packages/scheduling/src/components/time-management/` |
**Total: ~503 strings across 54 files, creating 4 new namespaces.**
Each batch: extract hardcoded strings → create English namespace JSON → wire `useTranslation()` → generate 6 non-English translations + 2 pseudo-locales → update `ROUTE_NAMESPACES` → visual QA.
## Problem
These four MSP feature areas display hundreds of hardcoded English strings. Users with non-English locale preferences see translated shell/dashboard/settings chrome but English content on these pages. Dispatch, reports, admin, and time entry are all high-traffic pages for MSP operators.
## Goals
1. Create 4 new namespace files: `msp/dispatch.json`, `msp/reports.json`, `msp/admin.json`, `msp/time-entry.json`
2. Wire all ~54 component files to consume translations via `useTranslation()`
3. Generate translations for all 7 production languages + 2 pseudo-locales (9 locale files per namespace = 36 new files)
4. Register all 4 namespaces in `ROUTE_NAMESPACES`
5. Zero regressions with `msp-i18n-enabled` flag OFF
## Non-goals
- Translating strings in components outside the 4 listed areas
- Translating console.log/console.error messages (developer-facing, not user-facing)
- Changing component behavior or refactoring
- Adding new languages
## Users and Primary Flows
**Primary user**: MSP portal operators using non-English locales.
**Flows**:
- Technician dispatch: scheduling technicians, viewing/filtering work items, drag-and-drop scheduling
- Reports: viewing contract revenue, expiration, bucket hours, profitability reports
- Admin: configuring telemetry settings, email providers, inbound ticket defaults, M365 diagnostics
- Time entry: logging time, managing timesheets, submitting for approval, approval workflow
## UX / UI Notes
- No visual changes — translated text appears in same locations
- German/Dutch text 30-50% longer — verify no overflow in: dispatch filter dropdowns, report table columns, admin form labels, timesheet column headers
- Status badges (Submitted, Approved, Changes Requested, etc.) appear in multiple components — use shared keys within each namespace to avoid inconsistency
- Time units ("h", "m", "hrs") need locale-appropriate formatting
- Pagination strings ("Page X of Y", "Showing X of Y items") need interpolation
## Requirements
### Batch 2b-5: msp/dispatch (~120 strings, 13 files)
#### D-FR1: WorkItemListPanel.tsx (11 strings)
| String | Key |
|--------|-----|
| "Work Items" | `workItems.title` |
| "Search work items..." | `workItems.searchPlaceholder` |
| "Filter by status..." | `workItems.filterPlaceholder` |
| "Unscheduled" | `workItems.status.unscheduled` |
| "Scheduled" | `workItems.status.scheduled` |
| "Previous" / "Next" | `workItems.pagination.previous` / `.next` |
| "Page {{current}} of {{total}}" | `workItems.pagination.pageInfo` |
| "Showing {{count}} of {{total}} items" | `workItems.pagination.showing` |
#### D-FR2: ScheduleViewPanel.tsx (11 strings)
| String | Key |
|--------|-----|
| "Technician Dispatch" | `page.title` |
| "Show Inactive Users" | `schedule.showInactive` |
| "< Prev" / "Next >" | `schedule.prev` / `.next` |
| "Today" | `schedule.today` |
| "Day" / "Week" | `schedule.viewDay` / `.viewWeek` |
| "Previous {{mode}}" / "Next {{mode}}" (aria) | `schedule.prevAria` / `.nextAria` |
#### D-FR3: WorkItemDetailsDrawer.tsx (~31 strings)
All field labels (Service, Status, Client, Contact, etc.), section headings (Appointment Request Details, Approval Information), error toasts, and fallback text ("N/A", "Error loading content").
#### D-FR4: TechnicianDispatchDashboard.tsx (~28 strings)
Error/success toasts, permission denied messages, filter options ("All Open", "All Closed"), private event title ("Busy"), access denied message.
#### D-FR5: ScheduleEvent.tsx + WeeklyScheduleEvent.tsx (~15 strings)
"View Details", "Delete", "Delete schedule entry", "Busy", "Unknown", "Unassigned", "Untitled" fallbacks.
#### D-FR6: WeeklyTechnicianScheduleGrid.tsx (~8 strings)
"Compare All", "Clear All", "Compare", "Stop Comparing", "View Week" tooltips and aria-labels.
#### D-FR7: WorkItemCard.tsx (2 strings)
"Needs dispatch for: {{agents}}", "Needs Dispatch" badge.
#### D-FR8: TimeHeader.tsx (2 strings)
" AM" / " PM" time suffixes.
#### D-FR9: ROUTE_NAMESPACES update
```
'/msp/technician-dispatch': ['common', 'msp/core', 'msp/dispatch']
```
---
### Batch 2b-7: msp/reports (~99 strings, 4 files)
#### R-FR1: ContractReports.tsx (~54 strings)
Tab labels ("Contract Revenue", "Expiration", "Bucket Hours", "Profitability"), tab descriptions, summary cards ("Total MRR", "YTD Revenue", "Active Contracts"), all column headers for 4 report tables, empty state messages, unit labels ("hrs", "days", "%").
#### R-FR2: ContractPerformance.tsx (~19 strings)
Metric labels ("Total Clients", "Active Clients", "Total Plans", "Avg. Plans Per Client", "Total Revenue"), table headers, empty states, "Select contract..." placeholder.
#### R-FR3: ContractUsageReport.tsx (~17 strings)
"Contract Usage Report" heading, column headers, "Summary" section, metric labels, status labels, empty states.
#### R-FR4: Reports.tsx (~9 strings)
"Reports" heading, 4 card titles ("Time Utilization", "Project Progress", "Revenue by Client", "Team Performance"), placeholder texts.
#### R-FR5: ROUTE_NAMESPACES update
```
'/msp/billing': ['common', 'msp/core', 'features/billing', 'msp/reports']
```
(Reports load on the billing route since they're in the billing dashboard)
---
### Batch 2b-8: msp/admin (~123 strings, 4 files)
#### A-FR1: TenantTelemetrySettings.tsx (~25 strings)
Section headings, toggle labels ("Enable Telemetry", "Allow User Opt-Out"), descriptions, anonymization options ("No Anonymization", "Partial", "Full"), compliance notes, "What We Collect" / "What We DON'T Collect" lists, buttons.
#### A-FR2: EmailSettings.tsx (~48 strings)
Tab labels ("Inbound Email", "Outbound Email"), provider options ("SMTP", "Resend"), form labels (Host, Port, Username, Password, From Address), placeholders, domain verification statuses ("Verified", "Failed", "Pending"), general settings labels, error messages.
#### A-FR3: Microsoft365DiagnosticsDialog.tsx (~18 strings)
Dialog title/description, diagnostic status badges ("Pass", "Warn", "Fail", "Skip"), "Running diagnostics...", "Copy Support Bundle", "Recommendations", notes/warnings.
#### A-FR4: InboundTicketDefaultsManager.tsx (~32 strings)
Section heading, field labels ("Board:", "Status:", "Priority:", "Entered By:"), badges ("Active", "Inactive"), empty states, "How It Works" section with bullet points, dialog titles, action buttons.
#### A-FR5: ROUTE_NAMESPACES update
Admin components load on settings routes:
```
'/msp/settings': ['common', 'msp/core', 'msp/settings', 'msp/admin', 'features/projects']
```
---
### Batch 2b-3: msp/time-entry (~161 strings, 33 files)
#### T-FR1: TimePeriodList.tsx (~18 strings)
"Select a Time Period", "Manage Time Periods", column headers (Period, Status, Hours Entered, Days Logged, Last Entry, Actions), status badges (In Progress, Submitted, Approved, Changes Requested, Current).
#### T-FR2: TimeEntryEditForm.tsx (~21 strings)
Form labels (Service, Date, Start Time, End Time, Duration, Billable, Notes), placeholders, validation messages ("Start time must be earlier than end time", "Duration must be at least 1 minute", "Service is required"), "Unsaved changes", "Delete Time Entry".
#### T-FR3: WorkItemPicker.tsx (~25 strings)
"Create Ad-hoc Entry", search placeholder, filter labels ("Assigned to", "Assigned to me", "All Types"), type options (Tickets, Project Tasks, Interactions, Ad-hoc Entries), date range labels, validation, empty/loading states, bundled ticket info.
#### T-FR4: WorkItemList.tsx (~20 strings)
Assignment text ("Unassigned", "1 user assigned", "{{count}} users assigned"), contact/due date labels, type badges (Ticket, Billable, Project Task, Ad-hoc Entry, Interaction), pagination, empty state.
#### T-FR5: TimeSheetHeader.tsx (~15 strings)
Status badges (reuse from T-FR1), heading template ("Time Sheet for {{name}}"), navigation aria-labels, "Show intervals", "Submit Time Sheet", "Reopen for edits", pagination.
#### T-FR6: TimeSheetApproval.tsx (~14 strings)
"Time Entry Details", field labels (Work Item, Duration, Billable, Notes), "Entry Change Suggestion", placeholder, "Approve" / "Request Changes" buttons.
#### T-FR7: ApprovalActions.tsx (~10 strings)
"Approve", "Reject", "Request Changes" buttons, dialog titles, "Rejection Reason" label, confirm buttons.
#### T-FR8: TimeSheetComments.tsx (~7 strings)
"Approver" / "Employee" labels, comment placeholders, "Respond to Changes" / "Add Comment" buttons.
#### T-FR9: Remaining files (~31 strings across ~20 files)
- `ManagerApprovalDashboard.tsx` (3): team lead access message
- `TimeEntryChangeRequestFeedback.tsx` (4): feedback states
- `WorkItemDrawer.tsx` (3): error states
- `TimeSheetClient.tsx` (2): delegation disabled, reopen toast
- `TimeSheetListView.tsx` (2): loading/empty states
- `SelectedWorkItem.tsx` (3): ad-hoc entry text, buttons
- `ContractInfoBanner.tsx` (4): contract line info messages
- `BillableLegend.tsx` (3): legend heading/description
- `IntervalItem.tsx` (3): "Now", "Auto-closed", "Active"
- `AddWorkItemDialog.tsx` (2): dialog title/description
- `TimeEntryReadOnly.tsx` (3): fallback texts
- Other files (minimal): TimeEntryProvider, IntervalSection, IntervalManagement
#### T-FR10: ROUTE_NAMESPACES update
```
'/msp/time-entry': ['common', 'msp/core', 'msp/time-entry']
'/msp/time-sheet-approvals': ['common', 'msp/core', 'msp/time-entry']
'/msp/time-management': ['common', 'msp/core', 'msp/time-entry']
```
### Non-functional Requirements
- Follow established key naming convention (page/sections/fields/actions/table/dialogs/errors/validation/messages)
- All `t()` calls use `{ defaultValue: '...' }` for English fallback
- Console.log/console.error messages stay in English (developer-facing)
- Feature flag `msp-i18n-enabled` OFF = forced English
## Rollout / Migration
- Behind existing `msp-i18n-enabled` feature flag
- No database changes
- Purely additive: new JSON files + component wiring
## Open Questions
1. **Time units**: Should "h"/"m"/"hrs" be translation keys, or handled by `useFormatters()` duration formatting? (Recommendation: translation keys for now — duration formatters can be added later.)
2. **Shared status badges**: Time entry uses status badges (Submitted, Approved, etc.) in multiple components. Should these be in `common.json` or `msp/time-entry.json`? (Recommendation: `msp/time-entry.json` with a shared `statuses.*` section within the namespace.)
3. **Reports namespace loading**: Reports are within the billing page. Should `msp/reports` load on `/msp/billing` alongside other billing namespaces? (Recommendation: yes, add to the billing route.)
4. **Admin namespace loading**: Admin components render inside settings tabs. Should `msp/admin` load on `/msp/settings`? (Recommendation: yes.)
## Acceptance Criteria (Definition of Done)
### Per batch
- [ ] English namespace JSON created with all keys
- [ ] All component files wired with `useTranslation('<namespace>')`
- [ ] `ROUTE_NAMESPACES` updated
- [ ] All 7 production locale files created with translations
- [ ] Pseudo-locale files created (xx, yy)
- [ ] Italian accent audit passes
- [ ] `msp-i18n-enabled` OFF: English text, no regressions
- [ ] `msp-i18n-enabled` ON + locale `xx`: all strings show `11111`
### Cross-cutting
- [ ] All 36 new locale files are valid JSON
- [ ] All `{{variables}}` preserved across all languages
- [ ] German translations don't overflow in tables, dropdowns, form labels
- [ ] Build passes with no TypeScript errors