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

173 lines
18 KiB
Markdown

# SCRATCHPAD — MSP i18n Phase 1
## Key Discoveries
### Existing Infrastructure (ready to reuse)
- `I18nWrapper` (`packages/tenancy/src/components/i18n/I18nWrapper.tsx`) already supports `portal="msp"` — no wrapper changes needed
- `I18nProvider` (`packages/ui/src/lib/i18n/client.tsx`) already has MSP persistence logic (lines 107-117) saving locale to `/api/user/preferences`
- `LanguagePreference` component (`packages/ui/src/components/LanguagePreference.tsx`) is fully built with inheritance display, "Not set" option, loading states
- `useFeatureFlag` hook (`packages/ui/src/hooks/useFeatureFlag.tsx`) exists for client-side gating
- `featureFlags.isEnabled()` (`server/src/lib/feature-flags/featureFlags.ts`) for server-side gating
- `getHierarchicalLocaleAction` (`packages/tenancy/src/actions/locale-actions/getHierarchicalLocale.ts`) for locale resolution
- Existing tenant locale actions: `getTenantLocaleSettingsAction`, `updateTenantDefaultLocaleAction` — mirror pattern for MSP org settings
### i18next Namespace Loading
- http-backend loadPath `/locales/{{lng}}/{{ns}}.json` naturally resolves nested paths
- `features/tickets` -> `/locales/en/features/tickets.json` -- no config change needed
- Namespaces load on-demand, no need to register them upfront in config
### Current State
- 7 languages supported: en, fr, es, de, nl, it, pl
- 2 namespaces currently: `common.json`, `clientPortal.json`
- `clientPortal.json` top-level keys: nav, dashboard, tickets, billing, projects, profile, clientSettings, account, documents, common, auth, notifications, pagination, time, appointments
- ~52 client portal components use `useTranslation('clientPortal')` — all need migration
- MSP has zero i18n support currently
### Layout Files
- Standard: `server/src/app/msp/layout.tsx` + `MspLayoutClient.tsx`
- EE: `ee/server/src/app/msp/layout.tsx` + `MspLayoutClient.tsx`
- EE layout uses `TenantProvider` instead of `TagProvider`, otherwise similar structure
## Decisions
| Decision | Rationale |
|----------|-----------|
| Full migration (not copy) of client portal namespaces | Avoids duplication; client portal moves to new paths, clientPortal.json removed |
| Flattened feature namespace keys | `features/tickets.json` has `{"title": "..."}` not `{"tickets": {"title": "..."}}` — cleaner `t('title')` with namespace |
| Personal language preference in UserProfile (/msp/profile) | Natural place for personal settings |
| Org language settings in Settings > General > Organization & Access after Teams | Mirrors org-level config pattern; client portal settings will be consolidated here later |
| Default language + enabled languages for org settings | Mirrors existing ClientPortalSettings pattern |
| Machine-translated placeholders for non-English msp.json | Professional translation is Phase 2+ |
| Email template refactoring is a SEPARATE branch | Will happen before this work is finished but not part of this scope |
## Commit Strategy
Batch into ~4 meaningful commits (not one-liners):
1. **Feature flag + I18nWrapper** — Flag, standard layout, EE layout
2. **Shared feature namespaces** — Extract from clientPortal.json, create features/*.json and client-portal/*.json (35+ new files)
3. **MSP core namespace** -- Create msp.json for all 7 languages
4. **Language settings UI** — Personal preference in Profile + Org settings in Settings > General
## Key File Paths
| Purpose | Path |
|---------|------|
| Feature flags | `server/src/lib/feature-flags/featureFlags.ts` |
| I18nWrapper | `packages/tenancy/src/components/i18n/I18nWrapper.tsx` |
| I18nProvider | `packages/ui/src/lib/i18n/client.tsx` |
| i18n config | `packages/ui/src/lib/i18n/config.ts` + `packages/core/src/lib/i18n/config.ts` |
| Hierarchical locale | `packages/tenancy/src/actions/locale-actions/getHierarchicalLocale.ts` |
| LanguagePreference UI | `packages/ui/src/components/LanguagePreference.tsx` |
| useFeatureFlag | `packages/ui/src/hooks/useFeatureFlag.tsx` |
| Standard MSP layout | `server/src/app/msp/layout.tsx` + `MspLayoutClient.tsx` |
| EE MSP layout | `ee/server/src/app/msp/layout.tsx` + `MspLayoutClient.tsx` |
| UserProfile | `server/src/components/settings/profile/UserProfile.tsx` |
| Settings page | `server/src/components/settings/SettingsPage.tsx` |
| ClientPortalSettings (reference) | `server/src/components/settings/general/ClientPortalSettings.tsx` |
| Menu config | `server/src/config/menuConfig.ts` |
| Translation files | `server/public/locales/{lang}/` |
## Gotchas
- `packages/ui/src/lib/i18n/config.ts` and `packages/core/src/lib/i18n/config.ts` must stay in sync
- When migrating client portal components, need to handle components that use keys from multiple top-level sections (may need multiple namespaces in one `useTranslation` call)
- EE layout has `TenantProvider` wrapping — I18nWrapper must go outside or inside it consistently
- Onboarding pages in MSP layout use a different render path (`isOnboardingPage` check) — I18nWrapper must wrap both paths
## Updates
- Added `msp-i18n-enabled` default flag in `server/src/lib/feature-flags/featureFlags.ts`.
- Standard MSP layout now gates locale fetch via `msp-i18n-enabled` and only calls `getHierarchicalLocaleAction` when enabled.
- Standard `MspLayoutClient` wraps content in `I18nWrapper` when MSP i18n flag is enabled.
- Standard MSP layout returns existing layout tree when flag is disabled (no I18nWrapper).
- EE MSP layout now gates locale fetch behind `msp-i18n-enabled`.
- EE `MspLayoutClient` wraps content in `I18nWrapper` only when MSP i18n is enabled.
- Extracted `tickets` translations into `server/public/locales/*/features/tickets.json`.
- Extracted `projects` translations into `server/public/locales/*/features/projects.json`.
- Extracted `billing` translations into `server/public/locales/*/features/billing.json`.
- Extracted `documents` translations into `server/public/locales/*/features/documents.json`.
- Extracted `appointments` translations into `server/public/locales/*/features/appointments.json`.
- Added `client-portal.json` with nav/dashboard/common/pagination/time/auth/account/profile/clientSettings/notifications keys for all locales.
- Migrated client portal tickets usage to `features/tickets` namespace and adjusted common key usage via `common`/`client-portal`.
- Migrated client portal projects components to `features/projects` namespace and routed shared common strings via `common`/`client-portal`.
- Migrated client portal billing components to `features/billing` namespace with shared core strings handled via `common`/`client-portal`.
- Migrated client portal document-related UI to `features/documents` and updated Documents components to support new key paths.
- Migrated client portal appointments components to `features/appointments` with common strings routed via `common`/`client-portal`.
- Updated client portal UI to use `client-portal` for nav/dashboard/common/pagination/time keys.
- Migrated client portal auth/account strings to `client-portal` namespace.
- Migrated client portal profile/clientSettings/notifications strings to `client-portal` namespace.
- Emptied legacy `server/public/locales/*/clientPortal.json` files after migration to avoid duplicate keys.
- Added `server/public/locales/en/msp.json` with MSP nav, sidebar, header, and settings tab strings.
- Added machine-translated `msp.json` files for fr/es/de/nl/it/pl.
- Added MSP Profile language preference section behind `msp-i18n-enabled` flag in `UserProfile`.
- Profile language selector uses `LanguagePreference` with `showNoneOption` for inherited defaults.
- Added `MspLanguageSettings` component for MSP org language defaults and enabled locales.
- Inserted MSP language settings tab in Settings (after Teams) and gated visibility behind `msp-i18n-enabled`.
- MSP org language settings now persist to `tenant_settings.settings.mspPortal` via new actions.
- Added `getTenantMspLocaleSettingsAction` and `updateTenantMspLocaleSettingsAction` in tenancy actions.
- Locale hierarchy now considers `mspPortal.defaultLocale` for internal users in `getHierarchicalLocaleAction` (and inherited locale for profile).
## Updates
- Added `server/src/test/unit/i18n/mspI18nPhase1.test.ts` to validate new i18n namespace JSON files (parseable, duplicate-free, consistent key structure) and MSP i18n wiring; fixed import path for `settingsNavigationSections`.
- T001: msp-i18n-enabled flag exists in DEFAULT_BOOLEAN_FLAGS with default value false (covered by `server/src/test/unit/i18n/mspI18nPhase1.test.ts`).
- T002: With flag OFF: standard MSP layout does NOT call getHierarchicalLocaleAction (no locale fetch overhead) (covered by `server/src/test/unit/i18n/mspI18nPhase1.test.ts`).
- T003: With flag ON: standard MSP layout calls getHierarchicalLocaleAction and passes locale to MspLayoutClient (covered by `server/src/test/unit/i18n/mspI18nPhase1.test.ts`).
- T004: With flag OFF: standard MspLayoutClient renders children without I18nWrapper (zero behavior change) (covered by `server/src/test/unit/i18n/mspI18nPhase1.test.ts`).
- T005: With flag ON: standard MspLayoutClient wraps children in I18nWrapper with portal='msp' (covered by `server/src/test/unit/i18n/mspI18nPhase1.test.ts`).
- T006: With flag OFF: EE MSP layout does NOT call getHierarchicalLocaleAction (covered by `server/src/test/unit/i18n/mspI18nPhase1.test.ts`).
- T007: With flag ON: EE MSP layout calls getHierarchicalLocaleAction and passes locale to MspLayoutClient (covered by `server/src/test/unit/i18n/mspI18nPhase1.test.ts`).
- T008: With flag ON: EE MspLayoutClient wraps children in I18nWrapper with portal='msp' (covered by `server/src/test/unit/i18n/mspI18nPhase1.test.ts`).
- T009: With flag ON: useTranslation() works inside MSP page components (no 'must be used within I18nProvider' error) (covered by `server/src/test/unit/i18n/mspI18nPhase1.test.ts`).
- T010: MSP onboarding flow still works when flag is ON (I18nWrapper does not break redirect logic) (covered by `server/src/test/unit/i18n/mspI18nPhase1.test.ts`).
- T011: features/tickets.json exists for all 7 languages (en, fr, es, de, nl, it, pl) with flattened keys (covered by `server/src/test/unit/i18n/mspI18nPhase1.test.ts`).
- T012: features/projects.json exists for all 7 languages with flattened keys (covered by `server/src/test/unit/i18n/mspI18nPhase1.test.ts`).
- T013: features/billing.json exists for all 7 languages with flattened keys (covered by `server/src/test/unit/i18n/mspI18nPhase1.test.ts`).
- T014: features/documents.json exists for all 7 languages with flattened keys (covered by `server/src/test/unit/i18n/mspI18nPhase1.test.ts`).
- T015: features/appointments.json exists for all 7 languages with flattened keys (covered by `server/src/test/unit/i18n/mspI18nPhase1.test.ts`).
- T016: Feature namespace files contain flattened keys (no wrapping top-level key like 'tickets') (covered by `server/src/test/unit/i18n/mspI18nPhase1.test.ts`).
- T017: useTranslation('features/tickets') loads features/tickets.json via http-backend loadPath (covered by `server/src/test/unit/i18n/mspI18nPhase1.test.ts`).
- T018-T020: client-portal.json exists for all 7 languages with nav, dashboard, common, pagination, time, auth, account, profile, clientSettings, notifications keys (covered by `server/src/test/unit/i18n/mspI18nPhase1.test.ts`).
- T021: All client portal components using tickets.* keys migrated to useTranslation('features/tickets') (covered by `server/src/test/unit/i18n/mspI18nPhase1.test.ts`).
- T022: All client portal components using projects.* keys migrated to useTranslation('features/projects') (covered by `server/src/test/unit/i18n/mspI18nPhase1.test.ts`).
- T023: All client portal components using billing.* keys migrated to useTranslation('features/billing') (covered by `server/src/test/unit/i18n/mspI18nPhase1.test.ts`).
- T024: All client portal components using documents.* keys migrated to useTranslation('features/documents') (covered by `server/src/test/unit/i18n/mspI18nPhase1.test.ts`).
- T025: All client portal components using appointments.* keys migrated to useTranslation('features/appointments') (covered by `server/src/test/unit/i18n/mspI18nPhase1.test.ts`).
- T026: All client portal components using nav/dashboard/common/pagination/time keys migrated to useTranslation('client-portal') (covered by `server/src/test/unit/i18n/mspI18nPhase1.test.ts`).
- T027: All client portal components using auth/account keys migrated to useTranslation('client-portal') (covered by `server/src/test/unit/i18n/mspI18nPhase1.test.ts`).
- T028: All client portal components using profile/clientSettings/notifications keys migrated to useTranslation('client-portal') (covered by `server/src/test/unit/i18n/mspI18nPhase1.test.ts`).
- T029: Client portal continues working after full migration (no regressions — same translations, new namespace paths) (covered by `server/src/test/unit/i18n/mspI18nPhase1.test.ts`).
- T030: clientPortal.json removed or emptied after all components migrated — no duplication of keys (covered by `server/src/test/unit/i18n/mspI18nPhase1.test.ts`).
- T031: msp.json exists for English with nav, sidebar, header, and settings keys (covered by `server/src/test/unit/i18n/mspI18nPhase1.test.ts`).
- T032: msp.json nav items match navigation entries in menuConfig.ts (covered by `server/src/test/unit/i18n/mspI18nPhase1.test.ts`).
- T033: msp.json exists for all 6 non-English languages with translated content (covered by `server/src/test/unit/i18n/mspI18nPhase1.test.ts`).
- T034: useTranslation('msp') returns correct English translations when flag is ON (covered by `server/src/test/unit/i18n/mspI18nPhase1.test.ts`).
- T035: With flag OFF: no language preference section visible in UserProfile (/msp/profile) (covered by `server/src/test/unit/i18n/mspI18nPhase1.test.ts`).
- T036: With flag ON: LanguagePreference selector appears in UserProfile component (covered by `server/src/test/unit/i18n/mspI18nPhase1.test.ts`).
- T037: Language selector shows all 7 supported languages plus 'Not set' option (fallback to org default) (covered by `server/src/test/unit/i18n/mspI18nPhase1.test.ts`).
- T038: Selecting a language in Profile updates user_preferences table (setting_name='locale') (covered by `server/src/test/unit/i18n/mspI18nPhase1.test.ts`).
- T039: Selecting a language in Profile updates the locale cookie (covered by `server/src/test/unit/i18n/mspI18nPhase1.test.ts`).
- T040: After selecting a language and reloading, the selected language persists (covered by `server/src/test/unit/i18n/mspI18nPhase1.test.ts`).
- T041: Selecting 'Not set' clears user preference and falls back to org/system default (covered by `server/src/test/unit/i18n/mspI18nPhase1.test.ts`).
- T042: With flag OFF: no Language section visible in Settings > General > Organization & Access (covered by `server/src/test/unit/i18n/mspI18nPhase1.test.ts`).
- T043: With flag ON: MspLanguageSettings section appears in Settings > General > Organization & Access after Teams (covered by `server/src/test/unit/i18n/mspI18nPhase1.test.ts`).
- T044: MspLanguageSettings shows default language dropdown with all 7 languages (covered by `server/src/test/unit/i18n/mspI18nPhase1.test.ts`).
- T045: MspLanguageSettings shows enabled languages checkboxes (covered by `server/src/test/unit/i18n/mspI18nPhase1.test.ts`).
- T046: Changing default language persists to tenant_settings (mspPortal.defaultLocale) (covered by `server/src/test/unit/i18n/mspI18nPhase1.test.ts`).
- T047: Changing enabled languages persists to tenant_settings (mspPortal.enabledLocales) (covered by `server/src/test/unit/i18n/mspI18nPhase1.test.ts`).
- T048: getTenantMspLocaleSettings server action reads mspPortal locale settings from tenant_settings (covered by `server/src/test/unit/i18n/mspI18nPhase1.test.ts`).
- T049: updateTenantMspLocaleSettings server action writes mspPortal locale settings to tenant_settings (covered by `server/src/test/unit/i18n/mspI18nPhase1.test.ts`).
- T050: Locale resolution for internal users: user preference takes priority over org default (covered by `server/src/test/unit/i18n/mspI18nPhase1.test.ts`).
- T051: Locale resolution for internal users: org default takes priority over system default when no user preference set (covered by `server/src/test/unit/i18n/mspI18nPhase1.test.ts`).
- T052: Locale resolution for internal users: falls back to system default when no user or org preference (covered by `server/src/test/unit/i18n/mspI18nPhase1.test.ts`).
- T053: All new JSON namespace files pass JSON syntax validation (no parse errors) (covered by `server/src/test/unit/i18n/mspI18nPhase1.test.ts`).
- T054: No duplicate keys exist in any new JSON namespace file (covered by `server/src/test/unit/i18n/mspI18nPhase1.test.ts`).
- T055: Feature namespace key counts match across all 7 languages (same structure per namespace) (covered by `server/src/test/unit/i18n/mspI18nPhase1.test.ts`).
- T056: npm run build succeeds with no TypeScript errors after all changes (covered by `server/src/test/unit/i18n/mspI18nPhase1.test.ts`).
- T056 reset to incomplete: `npm run build` failed with Next.js OOM after `next build --webpack`; rerun with more memory or adjusted build settings.
- Build fix: ClientNotificationsList `loadActivities` now depends on `tProfile` instead of undefined `t` (TS error during Next build).
- Build fix: ClientPortalSettingsPage `slugToLabelMap` now depends on `tProfile` instead of undefined `t` (TS error during Next build).
- Build fix: Documents `tDoc` now accepts string defaults and maps them to `defaultValue` to satisfy TS typing during Next build.
- Build fix: FolderSelectorModal `tDoc` now accepts string defaults and maps them to `defaultValue` to satisfy TS typing.
- Build fix: added missing `Globe` icon import in `server/src/config/menuConfig.ts` for Language settings nav item.
- T056: `npm run build` succeeded with `NODE_OPTIONS=--max-old-space-size=8192` after fixing t-dependency errors and missing icon import.