docs(14): create phase plan (5 plans, 4 waves)
This commit is contained in:
@@ -0,0 +1,169 @@
|
||||
---
|
||||
phase: 14-rss-email-alert-ingestion-module-rollout
|
||||
plan: 05
|
||||
type: execute
|
||||
wave: 4
|
||||
depends_on: [14-02, 14-03, 14-04]
|
||||
files_modified:
|
||||
- apps/web/src/messages/de.json
|
||||
- apps/web/src/messages/en.json
|
||||
- apps/web/src/messages/tenderRadar-parity.spec.ts
|
||||
- apps/web/src/app/(portal)/modules/tender-radar/page.tsx
|
||||
- apps/web/src/app/(portal)/modules/tender-radar/components/ResultsList.tsx
|
||||
- apps/web/src/app/(portal)/modules/tender-radar/components/FilterPanel.tsx
|
||||
- apps/web/src/app/(portal)/modules/tender-radar/components/SavedSearchBar.tsx
|
||||
- apps/web/src/app/(portal)/modules/tender-radar/components/TenderDetail.tsx
|
||||
- apps/web/src/app/(portal)/modules/tender-radar/components/CoverageBanner.tsx
|
||||
- apps/web/src/app/(portal)/modules/tender-radar/settings/page.tsx
|
||||
- apps/web/src/app/(portal)/modules/tender-radar/settings/components/SourceConfigForm.tsx
|
||||
- apps/web/src/app/(portal)/modules/tender-radar/settings/components/RssFeedListForm.tsx
|
||||
- apps/web/src/app/(portal)/modules/tender-radar/settings/components/EmailAlertConfigForm.tsx
|
||||
autonomous: true
|
||||
requirements: [CONFIG-03]
|
||||
must_haves:
|
||||
truths:
|
||||
- "The entire tender-radar UI (results, filters, saved searches, detail, coverage, settings, RSS/email forms) renders via a tenderRadar i18n namespace, usable in DE and EN"
|
||||
- "de.json and en.json have identical key sets under tenderRadar (no missing translation on either side)"
|
||||
artifacts:
|
||||
- "tenderRadar namespace added to de.json + en.json with EN translations authored by Claude"
|
||||
- "tenderRadar-parity.spec.ts enforcing de/en key-set equality"
|
||||
key_links:
|
||||
- "Every tender-radar component/page reads strings via useTranslations('tenderRadar') — no hardcoded German literals remain"
|
||||
---
|
||||
|
||||
<objective>
|
||||
Complete the bilingual module rollout (CONFIG-03/UI-06 i18n, D-10/D-11): introduce a new `tenderRadar` namespace in de.json + en.json and convert every tender-radar component and page from hardcoded German strings to `useTranslations('tenderRadar')` keys. No visible language switcher (D-11) — locale continues to follow the existing next-intl cookie mechanism. EN translations are authored here with consistent Vergabe-domain terminology.
|
||||
|
||||
Purpose: The module was intentionally built with German MVP-stub strings; this is the point that convention is retired for tender-radar specifically.
|
||||
Output: A complete `tenderRadar` namespace (DE + EN), all components converted, and a de/en key-parity guard test.
|
||||
</objective>
|
||||
|
||||
## Phase Goal (MVP user story)
|
||||
|
||||
**As a** user, **I want to** use the entire Ausschreibungs-Radar UI in German or English, **so that** English-speaking colleagues can work with it without a German-only barrier.
|
||||
|
||||
Slice: Task 1 lands the namespace + parity guard; Task 2 converts the main results/filter components; Task 3 converts the settings + config forms. After Task 3 the whole module honors the selected locale.
|
||||
|
||||
## Artifacts this phase produces
|
||||
|
||||
- New top-level `tenderRadar` namespace in `apps/web/src/messages/de.json` and `en.json` (parallel key sets), covering: page headings, ResultsList, FilterPanel, SavedSearchBar, TenderDetail, CoverageBanner (incl. denylist "manuell beobachten" block from 14-04), settings page + SourceConfigForm + RssFeedListForm (14-02) + EmailAlertConfigForm (14-03).
|
||||
- `apps/web/src/messages/tenderRadar-parity.spec.ts` — structural de/en key-set equality test for the namespace.
|
||||
- All listed components/pages converted to `useTranslations('tenderRadar')`.
|
||||
|
||||
<execution_context>
|
||||
@$HOME/.claude/gsd-core/workflows/execute-plan.md
|
||||
@$HOME/.claude/gsd-core/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<context>
|
||||
@.planning/PROJECT.md
|
||||
@.planning/ROADMAP.md
|
||||
@.planning/STATE.md
|
||||
@.planning/phases/14-rss-email-alert-ingestion-module-rollout/14-CONTEXT.md
|
||||
@.planning/phases/14-rss-email-alert-ingestion-module-rollout/14-RESEARCH.md
|
||||
@apps/web/src/i18n/request.ts
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: tenderRadar namespace (DE + EN) + de/en key-parity spec</name>
|
||||
<files>apps/web/src/messages/de.json, apps/web/src/messages/en.json, apps/web/src/messages/tenderRadar-parity.spec.ts</files>
|
||||
<read_first>
|
||||
- apps/web/src/messages/de.json + en.json (top-level namespace shape; dkvFleet namespace as the sibling template for a module namespace)
|
||||
- apps/web/src/i18n/request.ts (locale from NEXT_LOCALE cookie, default 'de' — no code change needed for D-11)
|
||||
- apps/web/src/app/(portal)/marketplace/components/MarketplaceCard.tsx (useTranslations('marketplace') consumption example)
|
||||
- RESEARCH.md i18n section (D-10 namespace shape, key-parity test recommendation) + D-11 (no switcher)
|
||||
</read_first>
|
||||
<action>
|
||||
Inventory every hardcoded German string across the tender-radar components and pages (page.tsx, ResultsList, FilterPanel, SavedSearchBar, TenderDetail, CoverageBanner, settings/page.tsx, SourceConfigForm, RssFeedListForm, EmailAlertConfigForm). Add a `tenderRadar` object to de.json capturing all of them as keys (group by component, e.g. `results.*`, `filter.*`, `savedSearch.*`, `detail.*`, `coverage.*`, `settings.*`, `rssFeeds.*`, `emailAlerts.*`), preserving the exact current German copy as values. Add the identical key set to en.json with faithful English translations using consistent Vergabe-domain terminology (Ausschreibung → tender, Vergabestelle → contracting authority, Frist → deadline, Auftragswert → estimated value). Create tenderRadar-parity.spec.ts (apps/web, jsdom) that imports both JSON files and asserts the recursively-flattened key sets under `tenderRadar` are identical (fails listing any key present in one locale but not the other). Do NOT add a language switcher (D-11).
|
||||
</action>
|
||||
<verify>
|
||||
<automated>cd apps/web && npx vitest run src/messages/tenderRadar-parity.spec.ts && npx tsc --noEmit</automated>
|
||||
</verify>
|
||||
<acceptance_criteria>
|
||||
- de.json and en.json both contain a `tenderRadar` namespace with identical (recursively-flattened) key sets.
|
||||
- tenderRadar-parity.spec.ts passes and would fail on any de/en key mismatch.
|
||||
- No language-switcher UI element is introduced.
|
||||
</acceptance_criteria>
|
||||
<done>The tenderRadar namespace exists in both locales with enforced key parity and authored EN translations.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: Convert results-side components to useTranslations</name>
|
||||
<files>apps/web/src/app/(portal)/modules/tender-radar/page.tsx, apps/web/src/app/(portal)/modules/tender-radar/components/ResultsList.tsx, apps/web/src/app/(portal)/modules/tender-radar/components/FilterPanel.tsx, apps/web/src/app/(portal)/modules/tender-radar/components/SavedSearchBar.tsx, apps/web/src/app/(portal)/modules/tender-radar/components/TenderDetail.tsx, apps/web/src/app/(portal)/modules/tender-radar/components/CoverageBanner.tsx</files>
|
||||
<read_first>
|
||||
- apps/web/src/app/(portal)/modules/tender-radar/page.tsx (headings)
|
||||
- apps/web/src/app/(portal)/modules/tender-radar/components/{ResultsList,FilterPanel,SavedSearchBar,TenderDetail,CoverageBanner}.tsx (strings to replace)
|
||||
- apps/web/src/app/(portal)/modules/tender-radar/components/ResultsList.test.tsx + SavedSearchBar.test.tsx + TenderDetail.test.tsx + CoverageBanner.test.tsx (existing tests — update next-intl mock so t(key) resolves; mirror marketplace tenant-selector.test.tsx mock)
|
||||
- tenderRadar keys added in Task 1
|
||||
</read_first>
|
||||
<action>
|
||||
Convert page.tsx, ResultsList.tsx, FilterPanel.tsx, SavedSearchBar.tsx, TenderDetail.tsx, and CoverageBanner.tsx to read every user-facing string via `const t = useTranslations('tenderRadar')` and `t('...')` (or `t.rich`/interpolation where the current copy contains values). Replace all German literals with their tenderRadar keys from Task 1 — no hardcoded user-facing German text may remain in these files. Update the affected component tests to provide a next-intl `useTranslations` mock (returning the key or a lookup) so they still pass, mirroring the marketplace test mock convention. Preserve behavior and markup — this is a string-source swap only.
|
||||
</action>
|
||||
<verify>
|
||||
<automated>cd apps/web && npx vitest run "src/app/(portal)/modules/tender-radar/components" && npx tsc --noEmit</automated>
|
||||
</verify>
|
||||
<acceptance_criteria>
|
||||
- `grep -nP "[A-Za-zÄÖÜäöüß]{4,}" ` audit: no user-facing German string literals remain in the six converted files (strings come from t()).
|
||||
- Each converted component imports useTranslations('tenderRadar').
|
||||
- Updated component tests pass; web tsc clean.
|
||||
</acceptance_criteria>
|
||||
<done>The results list, filters, saved searches, detail overlay, coverage banner, and page headings all render via the tenderRadar namespace.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 3: Convert settings + config forms to useTranslations</name>
|
||||
<files>apps/web/src/app/(portal)/modules/tender-radar/settings/page.tsx, apps/web/src/app/(portal)/modules/tender-radar/settings/components/SourceConfigForm.tsx, apps/web/src/app/(portal)/modules/tender-radar/settings/components/RssFeedListForm.tsx, apps/web/src/app/(portal)/modules/tender-radar/settings/components/EmailAlertConfigForm.tsx</files>
|
||||
<read_first>
|
||||
- apps/web/src/app/(portal)/modules/tender-radar/settings/page.tsx (headings + digest section strings)
|
||||
- apps/web/src/app/(portal)/modules/tender-radar/settings/components/SourceConfigForm.tsx (labels, validation/save messages)
|
||||
- apps/web/src/app/(portal)/modules/tender-radar/settings/components/RssFeedListForm.tsx (from Plan 14-02)
|
||||
- apps/web/src/app/(portal)/modules/tender-radar/settings/components/EmailAlertConfigForm.tsx (from Plan 14-03)
|
||||
- apps/web/src/app/(portal)/modules/tender-radar/settings/components/SourceConfigForm.test.tsx (update next-intl mock)
|
||||
- tenderRadar keys added in Task 1
|
||||
</read_first>
|
||||
<action>
|
||||
Convert settings/page.tsx, SourceConfigForm.tsx, RssFeedListForm.tsx, and EmailAlertConfigForm.tsx to `useTranslations('tenderRadar')`, replacing every label, placeholder, button, validation message, and success/error string with its tenderRadar key. For dynamic validation messages that embed numbers (e.g. interval bounds), use next-intl interpolation (`t('...', { min, max })`). Update SourceConfigForm.test.tsx (and any other affected settings test) with the next-intl mock so they pass. No hardcoded user-facing German text may remain in these four files.
|
||||
</action>
|
||||
<verify>
|
||||
<automated>cd apps/web && npx vitest run "src/app/(portal)/modules/tender-radar/settings" src/messages/tenderRadar-parity.spec.ts && npx tsc --noEmit</automated>
|
||||
</verify>
|
||||
<acceptance_criteria>
|
||||
- settings/page.tsx + the three config forms read all user-facing strings via useTranslations('tenderRadar').
|
||||
- No user-facing German literal remains in these four files.
|
||||
- Settings tests pass; parity spec still green (any key added here exists in both locales); web tsc clean.
|
||||
</acceptance_criteria>
|
||||
<done>The entire tender-radar settings surface (source config, RSS feeds, email alerts, digest) is bilingual.</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<threat_model>
|
||||
## Trust Boundaries
|
||||
|
||||
| Boundary | Description |
|
||||
|----------|-------------|
|
||||
| (none new) | i18n string-source swap; no new data flow or input surface |
|
||||
|
||||
## STRIDE Threat Register
|
||||
|
||||
| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan |
|
||||
|-----------|----------|-----------|----------|-------------|-----------------|
|
||||
| T-14-05-01 | Tampering | de/en key drift | low | mitigate | tenderRadar-parity.spec.ts fails the build on any missing/extra key in either locale |
|
||||
| T-14-05-SC | Tampering | npm/pip/cargo installs | low | accept | No new packages — next-intl already installed |
|
||||
</threat_model>
|
||||
|
||||
<verification>
|
||||
- `cd apps/web && npx vitest run "src/app/(portal)/modules/tender-radar" src/messages/tenderRadar-parity.spec.ts && npx tsc --noEmit` — all tender-radar web tests + parity green.
|
||||
- Manual: toggling the NEXT_LOCALE cookie de↔en switches the whole module UI.
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- Entire tender-radar UI usable in DE and EN via the tenderRadar namespace (CONFIG-03, D-10).
|
||||
- de/en key parity enforced by test; no language switcher added (D-11).
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
Create `.planning/phases/14-rss-email-alert-ingestion-module-rollout/14-05-SUMMARY.md` when done.
|
||||
</output>
|
||||
Reference in New Issue
Block a user