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
35 KiB
35 KiB
Scratchpad -- MSP i18n: Contract Lines Sub-batch
- Plan slug:
2026-04-09-msp-i18n-contract-lines - Created:
2026-04-09
Decisions
- (2026-04-09) Namespace name:
msp/contract-lines-- scoped to the contract-line management UI specifically, not the broader billing section. This keeps the file manageable at ~350-400 keys. - (2026-04-09) Billing constants NOT translated here:
BILLING_FREQUENCY_OPTIONS,PLAN_TYPE_OPTIONS,BILLING_FREQUENCY_DISPLAY,PLAN_TYPE_DISPLAY,CONTRACT_LINE_TYPE_DISPLAY, andBILLING_TIMING_OPTIONSare defined in@alga-psa/billing/constants/billing. They supply option labels consumed by multiple areas (contracts, invoices, billing dashboard). Translating them belongs in a sharedmsp/billingnamespace or the constants themselves need to return translation keys. For this batch, the components will translate the surrounding labels but leave the constant-driven option values as-is. - (2026-04-09) BILLING_TIMING_OPTIONS: The two options ("Arrears -- invoice after the period closes", "Advance -- invoice at the start of the period") are defined locally in both
ContractLineDialog.tsxandFixedContractLineConfiguration.tsx. These SHOULD be translated inline in this batch since they are not shared constants. - (2026-04-09) User type options: The
userTypeOptionsarray (Technician, Engineer, Consultant, Project Manager, Administrator) is defined locally inServiceHourlyConfigForm.tsxand both hourly configuration files. These should be translated in this namespace. - (2026-04-09) Cadence owner options:
CADENCE_OWNER_OPTIONSinFixedContractLineConfiguration.tsxare local and should be translated. They include bothlabelanddescriptionfields. - (2026-04-09) Currency symbol ($): The
$prefix shown in rate input fields is rendered as a static visual indicator (not a formatted currency value). It should remain hardcoded since it represents the input prefix, not a translated string. Actual currency formatting should useuseFormatters()where full monetary values are displayed. - (2026-04-09) Duplicate strings across files: Many contract-line and preset configuration files share identical strings (e.g., "Contract Line Basics", "Reset", "Save Changes", "Saving...", "Please correct the following:"). Use shared keys under
common.*in the namespace to avoid duplication.
Discoveries / Constraints
Component Structure
- (2026-04-09) The 21 files split into two parallel hierarchies: contract-line configurations (operating on actual contract lines) and preset configurations (operating on reusable templates). The UI text differs slightly ("Contract Line" vs "Contract Line Preset"), requiring separate but similar keys.
- (2026-04-09)
ContractLineTypeRouter.tsxandContractLinePresetTypeRouter.tsxare thin routing components with only 3-4 translatable strings each (loading text, error messages). They could potentially share a commonrouter.*key group. - (2026-04-09)
ContractLineDialog.tsxis the largest file (1292 lines) and contains render functions for Fixed/Hourly/Usage configs, each with its own info alerts, service lists, labels, and empty states. This needs careful key organization to avoid a flat wall of keys. - (2026-04-09)
ContractLines.tsxis the legacy top-level view that manages both contract lines AND their services in a two-panel layout. It has its own set of column headers and action menus distinct fromContractLinesOverview.tsx. - (2026-04-09)
GenericContractLineServicesList.tsxusesBILLING_METHOD_OPTIONSdefined locally as a const -- these labels ("Fixed Price", "Hourly", "Usage Based") should be translated.
Interpolation Patterns
- (2026-04-09) Service index in validation:
Service ${index + 1}: Please select a service-- needs{{index}}interpolation. - (2026-04-09) Card title pattern:
Edit Contract Line: ${plan?.contract_line_name || '...'} (Hourly)-- needs{{name}}interpolation and the type suffix. - (2026-04-09) Tier overlap messages:
Tier ${i + 1} overlaps with Tier ${i + 2}-- needs{{tier1}}and{{tier2}}interpolation. - (2026-04-09) Rate summary:
${rate} / ${unit}-- needs{{rate}}and{{unit}}interpolation. - (2026-04-09) Services heading:
Services for ${contractLines.find(...)?.contract_line_name}-- needs{{name}}interpolation. - (2026-04-09) Delete toast:
Contract line deleted successfully-- simple string, no interpolation. - (2026-04-09) "Add Selected (N) Services" button has count interpolation: needs
{{count}}. - (2026-04-09) Non-hourly service message:
This service (Billing Method: ${billing_method}) cannot be configured...-- needs{{method}}interpolation.
Shared Components (Out of Scope)
- (2026-04-09)
ServiceCatalogPicker-- used in ContractLineDialog for selecting services. Has its own translatable strings but belongs to a shared component namespace. - (2026-04-09)
BucketOverlayFields-- used for bucket configuration in hourly/usage presets. Shared with contracts. - (2026-04-09)
ServiceConfigurationPanel-- used by ContractLineServiceForm to render the full config UI. Separate sub-batch. - (2026-04-09)
DeleteEntityDialog-- shared UI component with its own translation needs. - (2026-04-09)
ConfirmationDialog-- shared UI component used by preset service lists for unsaved changes warning. - (2026-04-09)
DataTable-- column definitions pass translated strings, but the DataTable component itself is shared.
Potential Gotchas
- (2026-04-09)
ServiceBucketConfigForm.tsxdoes dynamic pluralization (pluralizeUnit()) by appending 's' to unit names. This naive pluralization will NOT work for non-English languages. The translation keys should use ICU plural syntax or separate singular/plural keys. For this batch, keep the pluralization in the key name pattern and let translators handle it. - (2026-04-09) Several files use
toast.success()andhandleError()with hardcoded English messages. These need translation but the toast calls happen in async handlers, not in JSX. Thet()function fromuseTranslationis available in the component scope and can be used in callbacks. - (2026-04-09)
FixedContractLineServicesListandFixedContractLinePresetServicesListare imported from the parentbilling-dashboard/directory (not thecontract-lines/subdirectory). They are OUT OF SCOPE for this batch per the file list.
Execution Order (Recommended)
- Create
msp/contract-lines.jsonEnglish namespace file (F036) - Start with smaller/simpler files: routers (F034-F035), type selector (F033), edit quantity dialog (F032)
- Form sub-components: ServiceTierEditor (F029), ServiceUsageConfigForm (F030), ServiceBucketConfigForm (F031), ServiceHourlyConfigForm (F024-F025)
- Service list components: GenericContractLineServicesList (F016-F017), preset service lists (F012-F015)
- Configuration components: Fixed (F020-F023), Usage (F006-F009), Hourly (F004-F005, F010-F011)
- Top-level components: ContractLineServiceForm (F026), ContractLines (F018-F019), ContractLinesOverview (F027-F028)
- ContractLineDialog (F001-F003) -- largest file, do last
- Generate translations (F037), pseudo-locales (F038), validate (F039), update routes (F040)
Commands / Runbooks
- Validate translations:
node scripts/validate-translations.cjs - Generate pseudo-locales:
npx ts-node scripts/generate-pseudo-locale.ts --locale xx --fill "1111" - Count keys:
node -e "const o=JSON.parse(require('fs').readFileSync('server/public/locales/en/msp/contract-lines.json'));const c=(o,p='')=>{let n=0;for(const[k,v]of Object.entries(o)){if(typeof v==='object'&&v!==null)n+=c(v,p+k+'.');else n++}return n};console.log(c(o))" - Visual QA: Enable
msp-i18n-enabledflag locally, switch toxxlocale, navigate to Billing > Contract Lines and exercise all configuration screens
Progress Log
- (2026-04-14)
F001completed inContractLineDialog.tsxby wiringuseTranslation('msp/contract-lines')and translating dialog title, basics labels/placeholders, and validation prefix usingt(..., { defaultValue }). - (2026-04-14) Preemptively translated additional
ContractLineDialog.tsxstrings forF002/F003in the same pass to minimize repeated churn in a 1k+ LOC file; feature flags will still be advanced one-by-one in checklist order. - (2026-04-14) Validation errors in
validateForm()now use interpolation-based i18n keys (for exampleService {{index}}...) to align with acceptance criteria for dynamic validation text. - (2026-04-14) Verification command run:
npx eslint packages/billing/src/components/billing-dashboard/ContractLineDialog.tsx(warnings only, no errors). - (2026-04-14)
F002covered by the sameContractLineDialog.tsxtranslation pass: billing model chooser heading/description, fixed/hourly/usage card title+description strings, billing timing option labels, and fixed vs non-fixed billing timing helper text are nowt()backed. - (2026-04-14)
F003covered inContractLineDialog.tsx: fixed/hourly/usage config headings, alerts, service labels/placeholders, rate/unit/proration copy, bucket recommendation labels, empty states, indexed service labels, and footer action labels (Cancel, submit variants, saving state) are now translated with defaults. - (2026-04-14)
F004implemented incontract-lines/HourlyContractLineConfiguration.tsx: addeduseTranslation('msp/contract-lines'); translated plan basics card title with{{name}}, basics heading/description/labels/placeholders, plan-wide hourly settings accordion trigger text, overtime labels + tooltip + helper copy, and after-hours labels + tooltip + helper copy. - (2026-04-14) Validation strings for plan-wide hourly settings (
overtimeRate,overtimeThreshold,afterHoursMultiplier) now use i18n keys; interpolation paths are ready for locale coverage. - (2026-04-14) Verification command run:
npx eslint packages/billing/src/components/billing-dashboard/contract-lines/HourlyContractLineConfiguration.tsx(warnings only, no errors). - (2026-04-14)
F005covered incontract-lines/HourlyContractLineConfiguration.tsx: translated service rates card title, fallback service name (Service ID: {{id}}), non-hourly configurability message with{{method}}, reset/save labels, sticky save button/loading text, manage services card title, empty states, and save error fallbacks. - (2026-04-14)
F006implemented incontract-lines/UsageContractLineConfiguration.tsx: wireduseTranslation('msp/contract-lines'), translated usage plan basics card title with{{name}} (Usage), basics section heading/description/labels/placeholders, and reset/save button labels including saving state. - (2026-04-14) Plan basics validation/save fallback strings in the same component now use translated defaults.
- (2026-04-14) Verification command run:
npx eslint packages/billing/src/components/billing-dashboard/contract-lines/UsageContractLineConfiguration.tsx(warnings only, no errors). - (2026-04-14)
F007covered incontract-lines/UsageContractLineConfiguration.tsx: translated service pricing card title, accordion summaries (Tiered Pricing ({{count}} tiers),Not Set,Loading...,{{rate}} / {{unit}},Loading configuration...), save-all CTA/loading text, manage-services card title, empty-state helper copy, and all save/validation error strings. - (2026-04-14)
F008implemented incontract-lines/UsageContractLinePresetConfiguration.tsx: wireduseTranslation('msp/contract-lines'), translated preset basics card title with{{name}} (Usage),Contract Line Preset Basicsheading, preset-name label/placeholder, description copy, billing-frequency label/placeholder, and reset/save button labels with saving state. - (2026-04-14) Verification command run:
npx eslint packages/billing/src/components/billing-dashboard/contract-lines/UsageContractLinePresetConfiguration.tsx(warnings only, no errors). - (2026-04-14)
F009covered incontract-lines/UsageContractLinePresetConfiguration.tsx: translated service-pricing card title, accordion summaries (Tiered Pricing,Not Set,Loading..., rate/unit summary), loading configuration message, save-all CTA, manage preset services card title, and save/validation errors includingNo changes detected to saveandCannot save, validation errors exist in the modified services. - (2026-04-14)
F010implemented incontract-lines/HourlyContractLinePresetConfiguration.tsx: wireduseTranslation('msp/contract-lines'), translated preset basics card title with{{name}} (Hourly), basics section labels/placeholders/descriptions, minimum billable + round-up labels/help copy, and reset/save actions (including saving text). - (2026-04-14) Verification command run:
npx eslint packages/billing/src/components/billing-dashboard/contract-lines/HourlyContractLinePresetConfiguration.tsx(warnings only, no errors). - (2026-04-14)
F011covered incontract-lines/HourlyContractLinePresetConfiguration.tsx: translated manage preset services card title plus all required error states (Invalid plan type or plan not found,Contract line not found or invalid type,Failed to load plan configuration) and shared save/validation error messages. - (2026-04-14)
F012implemented incontract-lines/UsageContractLinePresetServicesList.tsx: wireduseTranslation('msp/contract-lines'); translated usage-preset services list headings/empty-state/loading text, metadata lines, action-menu screen-reader + remove action text, rate/unit labels, bucket switch label, and add-services section heading/copy. - (2026-04-14) Billing method display labels in local
BILLING_METHOD_OPTIONSnow resolve through translation keys (Fixed Price,Hourly,Usage Based). - (2026-04-14) Verification command run:
npx eslint packages/billing/src/components/billing-dashboard/contract-lines/UsageContractLinePresetServicesList.tsx(warnings only, no errors). - (2026-04-14)
F013covered incontract-lines/UsageContractLinePresetServicesList.tsx: translated add-service metadata/interpolation strings,Add Selected ({{count}}) ServicesCTA, bucket overlay defaults/labels, save/reset controls, unsaved-change banner + confirmation dialog labels/messages, success toast, and error fallbacks. - (2026-04-14)
F014implemented incontract-lines/HourlyContractLinePresetServicesList.tsx: wireduseTranslation('msp/contract-lines'); translated hourly-preset services list heading/empty/loading copy, service metadata line, action-menu screen-reader + remove label, hourly rate label, bucket switch label, and add-services section heading/empty-state text. - (2026-04-14) Local billing method labels (
Fixed Price,Hourly,Usage Based) now flow through i18n keys in this component as well. - (2026-04-14) Verification command run:
npx eslint packages/billing/src/components/billing-dashboard/contract-lines/HourlyContractLinePresetServicesList.tsx(warnings only, no errors). - (2026-04-14)
F015covered incontract-lines/HourlyContractLinePresetServicesList.tsx: translated add-service metadata with interpolation,Add Selected ({{count}}) ServicesCTA, save/reset controls, unsaved-change banner + confirmation dialog text, success toast, and save error fallbacks. - (2026-04-14)
F016implemented incontract-lines/GenericContractLineServicesList.tsx: wireduseTranslation('msp/contract-lines'); translated all primary table columns (service name/type, billing method, derived config type, quantity, unit, custom rate, actions), billing-method display labels, config badge text (Billing mismatch,Default), and action-menu screen-reader text. - (2026-04-14) Verification command run:
npx eslint packages/billing/src/components/billing-dashboard/contract-lines/GenericContractLineServicesList.tsx(warnings only, no errors). - (2026-04-14)
F017covered incontract-lines/GenericContractLineServicesList.tsx: translated add-services section heading, empty states (No services currently associated...,All available services...,Loading services...), service-detail interpolation text, custom-rate placeholder (Enter rate),Add Selected ({{count}}) Services, currency-mismatch copy (No {{currency}} price), and add/remove error messages. - (2026-04-14)
F018implemented inContractLines.tsx: addeduseTranslation('msp/contract-lines'); translated contract-line table column headers (Contract Line Name,Billing Frequency,Contract Line Type,Is Custom,Actions), Yes/No display values, row action labels (Edit,Delete), menu screen-reader text, page heading, andAdd Contract Linebutton text. - (2026-04-14) Verification command run:
npx eslint packages/billing/src/components/billing-dashboard/ContractLines.tsx(warnings only, no errors). - (2026-04-14)
F019covered inContractLines.tsx: translated plan-services heading,Services for {{name}}subheading interpolation, empty state (Select a contract line to manage its services), plan-services table headers, service remove action label, service selector placeholder,Add Servicebutton, and related add/update/remove/fetch error strings plus delete-success toast. - (2026-04-14)
F020implemented incontract-lines/FixedContractLineConfiguration.tsx: wireduseTranslation('msp/contract-lines'); translated card title with{{name}}, contract-line basics section heading/description/labels/placeholders, billing-timing option labels + helper text, and cadence-owner section labels/descriptions with translated radio options. - (2026-04-14) Verification command run:
npx eslint packages/billing/src/components/billing-dashboard/contract-lines/FixedContractLineConfiguration.tsx(warnings only, no errors). - (2026-04-14)
F021covered incontract-lines/FixedContractLineConfiguration.tsx: translated fixed-fee settings heading/description, base-rate label/help, partial-period label/description, billing-cycle-alignment label/options/placeholder, associated-services card title, reset/save actions, and fixed-form validation/error messages. - (2026-04-14)
F022implemented incontract-lines/FixedContractLinePresetConfiguration.tsx: wireduseTranslation('msp/contract-lines'); translated preset card title with{{name}} (Fixed),Contract Line Preset Basicsheading/description, name and billing-frequency labels/placeholders, plus billing-timing label/helper text in the preset settings area. - (2026-04-14) Verification command run:
npx eslint packages/billing/src/components/billing-dashboard/contract-lines/FixedContractLinePresetConfiguration.tsx(warnings only, no errors). - (2026-04-14)
F023covered incontract-lines/FixedContractLinePresetConfiguration.tsx: translated fixed-fee settings heading/description,Recurring Base Rate (Optional)and override helper text, proration label/description, added + translated billing-cycle-alignment selector with options (Start of Billing Cycle,End of Billing Cycle,Proportional Coverage), associated-services card title, reset/save actions, and preset validation/error messages. - (2026-04-14)
F024implemented incontract-lines/ServiceHourlyConfigForm.tsx: wireduseTranslation('msp/contract-lines'); translated hourly-rate/minimum-billable/round-up labels, tooltip content, minute placeholders, and money placeholders while preserving existing validation rendering paths. - (2026-04-14) Verification command run:
npx eslint packages/billing/src/components/billing-dashboard/contract-lines/ServiceHourlyConfigForm.tsx(warnings only, no errors). - (2026-04-14)
F025covered incontract-lines/ServiceHourlyConfigForm.tsx: translated user-type-specific-rates heading/tooltip, existing-rate display suffix,Add New Ratelabel, user-type select placeholder,Addbutton, user-type option labels (Technician,Engineer,Consultant,Project Manager,Administrator), and validation messages for missing/duplicate user type rate setup. - (2026-04-14)
F026implemented incontract-lines/ContractLineServiceForm.tsx: wireduseTranslation('msp/contract-lines')and translated dialog title, loading state, and required error messages (Missing plan or service information,Failed to load service configuration,Failed to update service). - (2026-04-14) Verification command run:
npx eslint packages/billing/src/components/billing-dashboard/contract-lines/ContractLineServiceForm.tsx(warnings only, no errors). - (2026-04-14)
F027implemented incontract-lines/ContractLinesOverview.tsx: wireduseTranslation('msp/contract-lines'); translated page heading,Add Contract Line Presetbutton, presets table column headers, action menu labels (Edit,Delete), and screen-readerOpen menutext. - (2026-04-14) Verification command run:
npx eslint packages/billing/src/components/billing-dashboard/contract-lines/ContractLinesOverview.tsx(warnings only, no errors). - (2026-04-14)
F028covered incontract-lines/ContractLinesOverview.tsx: translated filter/search placeholders,All typesoption, reset-filters button text, loading indicator text, delete-success toast, and delete/fetch error messages. - (2026-04-13) F029 complete (
ServiceTierEditor.tsx): addeduseTranslation('msp/contract-lines')and replaced all user-visible strings witht()defaults, including card title, Add Tier button, translated empty state, column headers with{{unit}}interpolation, helper text,Unlimitedplaceholder, and tier-indexed aria labels (Tier {{tier}} ...,Remove Tier {{tier}}). - (2026-04-13) F029 verification:
npx eslint packages/billing/src/components/billing-dashboard/contract-lines/ServiceTierEditor.tsx(pass, no errors). - (2026-04-13) F030 complete (
ServiceUsageConfigForm.tsx): wireduseTranslation('msp/contract-lines')and translated labels/tooltips/placeholders for default rate/unit/minimum usage, required field indicator, and tiered pricing switch with{{serviceName}}interpolation. - (2026-04-13) F030 verification:
npx eslint packages/billing/src/components/billing-dashboard/contract-lines/ServiceUsageConfigForm.tsx(pass; pre-existing warnings only:anytype and unused handler). - (2026-04-13) F031 complete (
ServiceBucketConfigForm.tsx): wireduseTranslation('msp/contract-lines')and translated bucket labels/tooltips/placeholders with unit interpolation ({{unit}}/{{units}}), while preserving dynamic unit pluralization viapluralizeUnit()for runtime unit labels. - (2026-04-13) F031 verification:
npx eslint packages/billing/src/components/billing-dashboard/contract-lines/ServiceBucketConfigForm.tsx(pass; pre-existinganywarning only). - (2026-04-13) F032 complete (
EditContractLineServiceQuantityDialog.tsx): wireduseTranslation('msp/contract-lines'); translated dialog title, quantity heading, quantity/unit-price labels, helper text, validation error (Quantity must be greater than zero), fallback update error, and Cancel/Save actions. - (2026-04-13) F032 verification:
npx eslint packages/billing/src/components/billing-dashboard/contract-lines/EditContractLineServiceQuantityDialog.tsx(pass; pre-existinganywarning only). - (2026-04-13) F033 complete (
ContractLineTypeSelector.tsx): wireduseTranslation('msp/contract-lines'); translatedContract Line Typelabel, dropdown placeholder, and fixed/hourly/usage description copy used in cards and dropdown option descriptions. - (2026-04-13) F033 verification:
npx eslint packages/billing/src/components/billing-dashboard/contract-lines/ContractLineTypeSelector.tsx(pass, no errors). - (2026-04-13) F034 complete (
ContractLineTypeRouter.tsx): wireduseTranslation('msp/contract-lines'); translated loading text, not-found message with{{id}}, load-failed error, and unsupported-type error with{{type}}interpolation. - (2026-04-13) F034 verification:
npx eslint packages/billing/src/components/billing-dashboard/contract-lines/ContractLineTypeRouter.tsx(pass, no errors). - (2026-04-13) F035 complete (
ContractLinePresetTypeRouter.tsx): wireduseTranslation('msp/contract-lines'); translated loading text, not-found message with{{id}}, load-failed error, and unsupported preset type message with{{type}}interpolation. - (2026-04-13) F035 verification:
npx eslint packages/billing/src/components/billing-dashboard/contract-lines/ContractLinePresetTypeRouter.tsx(pass, no errors). - (2026-04-13) F036 complete: created
server/public/locales/en/msp/contract-lines.jsonwith 509 keys extracted from alluseTranslation('msp/contract-lines')component calls (including statict()defaults pluslabelKey/descriptionKeydynamic maps and type-selector description variants). - (2026-04-13) F036 verification runbook:
- Extraction/build script (AST-based, local one-off) to assemble key/default pairs from 21 contract-line components.
- Coverage check script confirmed
missing 0for static/dynamic key references againsten/msp/contract-lines.json.
- (2026-04-13) F037 complete: generated production locale files for
msp/contract-lines:server/public/locales/fr/msp/contract-lines.jsonserver/public/locales/es/msp/contract-lines.jsonserver/public/locales/de/msp/contract-lines.jsonserver/public/locales/nl/msp/contract-lines.jsonserver/public/locales/it/msp/contract-lines.jsonserver/public/locales/pl/msp/contract-lines.json
- (2026-04-13) F037 runbook: used a local Node script to translate from
en/msp/contract-lines.jsonvia Google Translate endpoint with placeholder protection for{{...}}interpolation tokens and JSON structure preservation. - (2026-04-13) F037 verification: key/shape/interpolation parity check against English returned, per locale,
missing: 0,extra: 0,phMismatch: 0,keys: 509. - (2026-04-13) F038 complete: added pseudo-locale namespace files:
server/public/locales/xx/msp/contract-lines.json(fill1111)server/public/locales/yy/msp/contract-lines.json(fill5555)
- (2026-04-13) F038 implementation note: used the same leaf-string replacement semantics as
scripts/generate-pseudo-locale.ts, preserving interpolation tokens ({{...}}) while replacing text payloads. - (2026-04-13) F039 complete: ran
node scripts/validate-translations.cjsafter addingmsp/contract-lineslocale files. - (2026-04-13) F039 result:
PASSEDwithErrors: 0andWarnings: 0across production locales (de/es/fr/it/nl/pl) and pseudo-locales (xx/yy). - (2026-04-13) F040 complete: updated
ROUTE_NAMESPACES['/msp/billing']inpackages/core/src/lib/i18n/config.tsto includemsp/contract-lines, ensuring contract-line namespace is preloaded for billing dashboard navigation. - (2026-04-13) F040 verification:
npx eslint packages/core/src/lib/i18n/config.ts(pass, no errors). - (2026-04-13) T001 complete: added
packages/billing/tests/billing-dashboard/ContractLinesSubbatch.i18n.test.ts(52 test cases covering T001-T052) and verifiedT001dialog-title i18n wiring through key assertions inContractLineDialog.tsx. - (2026-04-13) Test verification run:
cd packages/billing && npx vitest run tests/billing-dashboard/ContractLinesSubbatch.i18n.test.ts->52 passed. - (2026-04-13) T002 complete: covered by and verified in the passing 52-test run.
- (2026-04-13) T003 complete: covered by and verified in the passing 52-test run.
- (2026-04-13) T004 complete: covered by and verified in the passing 52-test run.
- (2026-04-13) T005 complete: covered by and verified in the passing 52-test run.
- (2026-04-13) T006 complete: covered by and verified in the passing 52-test run.
- (2026-04-13) T007 complete: covered by and verified in the passing 52-test run.
- (2026-04-13) T008 complete: covered by and verified in the passing 52-test run.
- (2026-04-13) T009 complete: covered by and verified in the passing 52-test run.
- (2026-04-13) T010 complete: covered by and verified in the passing 52-test run.
- (2026-04-13) T011 complete: covered by and verified in the passing 52-test run.
- (2026-04-13) T012 complete: covered by and verified in the passing 52-test run.
- (2026-04-13) T013 complete: covered by and verified in the passing 52-test run.
- (2026-04-13) T014 complete: covered by and verified in the passing 52-test run.
- (2026-04-13) T015 complete: covered by and verified in the passing 52-test run.
- (2026-04-13) T016 complete: covered by and verified in the passing 52-test run.
- (2026-04-13) T017 complete: covered by and verified in the passing 52-test run.
- (2026-04-13) T018 complete: covered by and verified in the passing 52-test run.
- (2026-04-13) T019 complete: covered by and verified in the passing 52-test run.
- (2026-04-13) T020 complete: covered by and verified in the passing 52-test run.
- (2026-04-13) T021 complete: covered by and verified in the passing 52-test run.
- (2026-04-13) T022 complete: covered by and verified in the passing 52-test run.
- (2026-04-13) T023 complete: covered by and verified in the passing 52-test run.
- (2026-04-13) T024 complete: covered by and verified in the passing 52-test run.
- (2026-04-13) T025 complete: covered by and verified in the passing 52-test run.
- (2026-04-13) T026 complete: covered by and verified in the passing 52-test run.
- (2026-04-13) T027 complete: covered by and verified in the passing 52-test run.
- (2026-04-13) T028 complete: covered by and verified in the passing 52-test run.
- (2026-04-13) T029 complete: covered by and verified in the passing 52-test run.
- (2026-04-13) T030 complete: covered by and verified in the passing 52-test run.
- (2026-04-13) T031 complete: covered by and verified in the passing 52-test run.
- (2026-04-13) T032 complete: covered by and verified in the passing 52-test run.
- (2026-04-13) T033 complete: covered by and verified in the passing 52-test run.
- (2026-04-13) T034 complete: covered by and verified in the passing 52-test run.
- (2026-04-13) T035 complete: covered by and verified in the passing 52-test run.
- (2026-04-13) T036 complete: covered by and verified in the passing 52-test run.
- (2026-04-13) T037 complete: covered by and verified in the passing 52-test run.
- (2026-04-13) T038 complete: covered by and verified in the passing 52-test run.
- (2026-04-13) T039 complete: covered by and verified in the passing 52-test run.
- (2026-04-13) T040 complete: covered by and verified in the passing 52-test run.
- (2026-04-13) T041 complete: covered by and verified in the passing 52-test run.
- (2026-04-13) T042 complete: covered by and verified in the passing 52-test run.
- (2026-04-13) T043 complete: covered by and verified in the passing 52-test run.
- (2026-04-13) T044 complete: covered by and verified in the passing 52-test run.
- (2026-04-13) T045 complete: covered by and verified in the passing 52-test run.
- (2026-04-13) T046 complete: covered by and verified in the passing 52-test run.
- (2026-04-13) T047 complete: covered by and verified in the passing 52-test run.
- (2026-04-13) T048 complete: covered by and verified in the passing 52-test run.
- (2026-04-13) T049 complete: covered by and verified in the passing 52-test run.
- (2026-04-13) T050 complete: covered by and verified in the passing 52-test run.
- (2026-04-13) T051 complete: covered by and verified in the passing 52-test run.
- (2026-04-13) T052 complete: covered by and verified in the passing 52-test run.
Follow-up: Enum labels from @alga-psa/billing/constants/billing
- (2026-04-14) Deferred gap now has a pattern. The 2026-04-09 Decisions entry called out that
BILLING_FREQUENCY_OPTIONS,PLAN_TYPE_OPTIONS,BILLING_FREQUENCY_DISPLAY,PLAN_TYPE_DISPLAY,CONTRACT_LINE_TYPE_DISPLAY, andBILLING_TIMING_OPTIONSwould be left as-is because they are shared across contracts, invoices, and the billing dashboard and there was no agreed-upon shared approach. Pseudo-locale QA onyyduring post-batch verification confirmed the gap: the billing-frequency dropdown inContractLineDialogrendersMonthly / Quarterly / Annuallywhile every surrounding string shows1111. The audit suite (ContractLinesSubbatch.i18n.test.ts) andvalidate-translations.cjsboth pass because not()call references these strings. - (2026-04-14) Adopted pattern: Localized option-hook pattern. Strip labels from the constant file (values + TS types only), publish a
useXOptions()/useFormatX()hook colocated with the constant, and migrate every consumer to the hook. Keys live infeatures/billing.jsonunderenums.billingFrequency.*andenums.contractLineType.*. Full recipe, reviewer checklist, and migration steps in.ai/translation/enum-labels-pattern.md. Architecture-level reference indocs/architecture/i18n.md(section "Enum labels from shared constants"). - (2026-04-14) In-scope consumers from this batch that need a follow-up migration (13 call sites across 9 files):
ContractLineDialog.tsx:1256(BILLING_FREQUENCY_OPTIONS)ContractLines.tsx:245, 250(BILLING_FREQUENCY_DISPLAY, PLAN_TYPE_DISPLAY in table renderers)ContractLinesOverview.tsx:114, 128, 133(CONTRACT_LINE_TYPE_DISPLAY filter + BILLING_FREQUENCY_DISPLAY + PLAN_TYPE_DISPLAY in columns)ContractLineTypeSelector.tsx:68, 120(PLAN_TYPE_OPTIONS in cards and dropdown — card title viaplanLabel)FixedContractLineConfiguration.tsx:326,FixedContractLinePresetConfiguration.tsx:294,HourlyContractLineConfiguration.tsx:871,HourlyContractLinePresetConfiguration.tsx:690,UsageContractLineConfiguration.tsx:609, 740,UsageContractLinePresetConfiguration.tsx:605, 729(BILLING_FREQUENCY_OPTIONS)
- (2026-04-14) Out-of-scope consumers with the same leak (not part of this batch but share the same constants — schedule into the next billing-area sub-batch so one migration covers everything):
ContractForm.tsx:142,ContractDialog.tsx:588, 773,ContractDetail.tsx:1510,ContractTemplateDetail.tsx:709, 790,TemplateContractBasicsStep.tsx:81,ContractBasicsStep.tsx:260(wizard-steps),AddContractLinesDialog.tsx:392,CreateCustomContractLineDialog.tsx:854,BillingFrequencyOverrideSelect.tsx:24, 27, 72,BucketServiceConfigPanel.tsx:116. - (2026-04-14, amendment) Also carried over (missed in the first audit pass):
wizard-steps/ReviewContractStep.tsx:22, 148, 303, 387, 429— imports bothBILLING_FREQUENCY_OPTIONS(for.find(...).labelin the main basics block) andBILLING_FREQUENCY_DISPLAY(for three override-display blocks under fixed/hourly/usage review sections). Owned by themsp-i18n-contractsbatch; already added to its PRD "Shared enum label migration" table. Migration:useBillingFrequencyOptions()for the.findlookup,useFormatBillingFrequency()for the three renderers. - (2026-04-14) Dead-code cleanup found during the audit (safe to delete in the follow-up):
HourlyContractLineConfiguration.tsx:553-559declaresuserTypeOptionsbut never references it;HourlyContractLinePresetConfiguration.tsx:561-567has the identical dead declaration. The real rendering path isServiceHourlyConfigForm.tsx:187-191, which already wires labels throught('forms.hourlyConfig.userTypeRates.options.*'). - (2026-04-14) Other constant in the same anti-pattern (out of billing scope but worth flagging for the next owner):
packages/billing/src/components/billing-dashboard/contracts/contractsTabs.ts—CONTRACT_SUBTAB_LABELS(Templates / Client Contracts / Drafts) is used both as display text and as an identifier inCONTRACT_LABEL_TO_SUBTAB. Migration needs to separate the two concerns first before applying the hook pattern.