docs(09-02): complete cert-manager frontend shell plan
This commit is contained in:
@@ -0,0 +1,149 @@
|
|||||||
|
---
|
||||||
|
phase: 09-cert-manager-module
|
||||||
|
plan: "02"
|
||||||
|
subsystem: frontend
|
||||||
|
tags: [cert-manager, frontend, i18n, components, shell]
|
||||||
|
requires: []
|
||||||
|
provides: [cert-manager-page-shell, cert-manager-i18n, cert-manager-actions]
|
||||||
|
affects: [apps/web]
|
||||||
|
tech_stack:
|
||||||
|
added: []
|
||||||
|
patterns: [useTranslations, vi.mock next-intl, client-component, conditional-render]
|
||||||
|
key_files:
|
||||||
|
created:
|
||||||
|
- apps/web/src/app/(portal)/modules/cert-manager/page.tsx
|
||||||
|
- apps/web/src/app/(portal)/modules/cert-manager/actions.ts
|
||||||
|
- apps/web/src/app/(portal)/modules/cert-manager/components/DropZone.tsx
|
||||||
|
- apps/web/src/app/(portal)/modules/cert-manager/components/PasswordField.tsx
|
||||||
|
- apps/web/src/app/(portal)/modules/cert-manager/components/InspectTab.tsx
|
||||||
|
- apps/web/src/app/(portal)/modules/cert-manager/components/SplitTab.tsx
|
||||||
|
- apps/web/src/app/(portal)/modules/cert-manager/components/MergeTab.tsx
|
||||||
|
- apps/web/src/app/(portal)/modules/cert-manager/components/ConvertTab.tsx
|
||||||
|
- apps/web/src/app/(portal)/modules/cert-manager/cert-manager.test.tsx
|
||||||
|
modified:
|
||||||
|
- apps/web/src/messages/de.json
|
||||||
|
- apps/web/src/messages/en.json
|
||||||
|
decisions:
|
||||||
|
- "Used vi.mock('next-intl') test pattern per project convention instead of NextIntlClientProvider (matches VehicleTable.test.tsx, sidebar.test.tsx)"
|
||||||
|
- "PasswordField renders null when show=false — no DOM reflow, satisfies T-09-02 threat mitigation"
|
||||||
|
- "File/PEM mutual exclusion enforced in page state: selecting one clears the other"
|
||||||
|
metrics:
|
||||||
|
duration: "~6 minutes"
|
||||||
|
completed: "2026-07-01"
|
||||||
|
tasks_completed: 3
|
||||||
|
files_created: 9
|
||||||
|
files_modified: 2
|
||||||
|
status: complete
|
||||||
|
---
|
||||||
|
|
||||||
|
# Phase 09 Plan 02: Cert-Manager Frontend Shell Summary
|
||||||
|
|
||||||
|
**One-liner:** Cert-manager tab shell with DropZone, conditional PasswordField, shared input card, four tab stubs, downloadBase64/postForm client helpers, and full certManager i18n namespace in de + en.
|
||||||
|
|
||||||
|
## Tasks Completed
|
||||||
|
|
||||||
|
| Task | Name | Commit | Files |
|
||||||
|
|------|------|--------|-------|
|
||||||
|
| 1 | Add certManager i18n namespace (de + en) | 82a80e7 | de.json, en.json |
|
||||||
|
| 2 | Build page shell, components, client helpers | 42a41f7 | page.tsx, actions.ts, 6 components |
|
||||||
|
| 3 | Shell render tests (GREEN) | 2cb01f7 | cert-manager.test.tsx |
|
||||||
|
|
||||||
|
## What Was Built
|
||||||
|
|
||||||
|
### i18n Namespace (Task 1)
|
||||||
|
`certManager` namespace added to both `de.json` and `en.json` with identical key structure:
|
||||||
|
- `title`, `description`
|
||||||
|
- `tabs.{inspect,split,merge,convert}`
|
||||||
|
- `dropZone.{placeholder,formats}`, `paste.placeholder`, `password.label`, `or`
|
||||||
|
- `actions.{inspect,split,merge,convert,download,processing}`
|
||||||
|
- `emptyState.{inspect,inspectBody,split,splitBody,merge,mergeBody,convert,convertBody}`
|
||||||
|
- `error.{generic,wrongPassword,unknownFormat}`
|
||||||
|
|
||||||
|
German copy matches UI-SPEC Copywriting Contract exactly.
|
||||||
|
|
||||||
|
### Page Shell (Task 2)
|
||||||
|
|
||||||
|
**page.tsx** — `'use client'` component with:
|
||||||
|
- State: `activeTab`, `file`, `pemText`, `password`
|
||||||
|
- Layout: `max-w-4xl mx-auto p-6 space-y-6` per UI-SPEC
|
||||||
|
- Shared input card with DropZone + OR divider + PEM textarea + conditional PasswordField
|
||||||
|
- Tab nav: 4 tabs with `border-b-2 border-primary` active indicator
|
||||||
|
- Renders active tab component with `{ file, pemText, password }` props
|
||||||
|
- File/PEM mutual exclusion enforced
|
||||||
|
|
||||||
|
**actions.ts** — Client helpers:
|
||||||
|
- `API_URL` const (`NEXT_PUBLIC_API_URL || 'http://localhost:3001'`)
|
||||||
|
- `downloadBase64(filename, content, mimeType)` — atob → Uint8Array → Blob → URL.createObjectURL
|
||||||
|
- `postForm(endpoint, form)` — fetch with `credentials: 'include'`, throws on !ok
|
||||||
|
|
||||||
|
**DropZone.tsx** — Drag-and-drop + click-to-browse, drag-over highlight (`border-primary bg-primary/5`), file display with clear button, resets `e.target.value` for re-selection.
|
||||||
|
|
||||||
|
**PasswordField.tsx** — `input[type=password]` with inline SVG eye toggle; renders `null` when `show=false` (T-09-02).
|
||||||
|
|
||||||
|
**Tab stubs** — InspectTab, SplitTab, MergeTab, ConvertTab render empty state strings only.
|
||||||
|
|
||||||
|
### Tests (Task 3)
|
||||||
|
7 tests, all GREEN:
|
||||||
|
- Page renders title 'Zertifikat-Manager' and description
|
||||||
|
- All 4 tab labels rendered (Analysieren, Aufteilen, Zusammenfuehren, Konvertieren)
|
||||||
|
- Password field NOT in DOM on initial render (T-09-02 assertion)
|
||||||
|
- Inspect empty state on initial render
|
||||||
|
- Split / Merge / Convert empty states when respective tabs activated
|
||||||
|
|
||||||
|
## Deviations from Plan
|
||||||
|
|
||||||
|
### Auto-applied conventions
|
||||||
|
|
||||||
|
**1. [Convention] vi.mock('next-intl') instead of NextIntlClientProvider**
|
||||||
|
- **Found during:** Task 3
|
||||||
|
- **Issue:** Plan specified NextIntlClientProvider, but every existing test in the project mocks next-intl directly (sidebar.test.tsx, VehicleTable.test.tsx)
|
||||||
|
- **Fix:** Used `vi.mock('next-intl', ...)` pattern for consistency
|
||||||
|
- **Impact:** Tests are simpler and match project convention; no behavior difference
|
||||||
|
|
||||||
|
**2. [Convention] node_modules symlink for worktree test execution**
|
||||||
|
- **Found during:** Task 3
|
||||||
|
- **Issue:** Worktree has no node_modules; vitest not available
|
||||||
|
- **Fix:** Created temporary symlink `apps/web/node_modules -> main repo node_modules` to run tests; symlink is gitignored
|
||||||
|
- **Impact:** Tests pass correctly; symlink does not affect production
|
||||||
|
|
||||||
|
## Threat Model Compliance
|
||||||
|
|
||||||
|
| Threat ID | Status |
|
||||||
|
|-----------|--------|
|
||||||
|
| T-09-02 (password in state/URL) | Mitigated — PasswordField renders null when show=false; password never in URL or console |
|
||||||
|
| T-09-04 (unauthenticated fetch) | Mitigated — postForm uses credentials:'include'; ModuleGuard enforced server-side |
|
||||||
|
| T-09-05 (client accept filter) | Accepted — UX only, server validates |
|
||||||
|
|
||||||
|
## Known Stubs
|
||||||
|
|
||||||
|
| Component | Location | Reason |
|
||||||
|
|-----------|----------|--------|
|
||||||
|
| InspectTab | cert-manager/components/InspectTab.tsx | Empty state only — API wire-up in later plan |
|
||||||
|
| SplitTab | cert-manager/components/SplitTab.tsx | Empty state only — API wire-up in later plan |
|
||||||
|
| MergeTab | cert-manager/components/MergeTab.tsx | Empty state only — API wire-up in later plan |
|
||||||
|
| ConvertTab | cert-manager/components/ConvertTab.tsx | Empty state only — API wire-up in later plan |
|
||||||
|
|
||||||
|
These stubs are intentional. Plan 02 explicitly scopes to the shell; later plans fill in each tab's API logic. The stubs do not prevent the plan goal (visible shell + navigation) from being achieved.
|
||||||
|
|
||||||
|
## Verification Results
|
||||||
|
|
||||||
|
| Check | Status | Notes |
|
||||||
|
|-------|--------|-------|
|
||||||
|
| `pnpm --filter @tessera/web test cert-manager --run` | PASS | 7/7 tests green |
|
||||||
|
| `tsc --noEmit` (cert-manager files) | PASS | No cert-manager type errors |
|
||||||
|
| I18N_OK node verify script | PASS | de.tabs keys correct, en.certManager.actions.download exists |
|
||||||
|
| `grep -q "max-w-4xl"` | PASS | Layout contract met |
|
||||||
|
| `grep -q "credentials: 'include'"` | PASS | T-09-04 mitigation present |
|
||||||
|
| `grep -q "URL.createObjectURL"` | PASS | downloadBase64 helper present |
|
||||||
|
|
||||||
|
## Self-Check: PASSED
|
||||||
|
|
||||||
|
- [x] apps/web/src/app/(portal)/modules/cert-manager/page.tsx — created
|
||||||
|
- [x] apps/web/src/app/(portal)/modules/cert-manager/actions.ts — created
|
||||||
|
- [x] apps/web/src/app/(portal)/modules/cert-manager/components/*.tsx (6 files) — created
|
||||||
|
- [x] apps/web/src/app/(portal)/modules/cert-manager/cert-manager.test.tsx — created
|
||||||
|
- [x] apps/web/src/messages/de.json — certManager namespace added
|
||||||
|
- [x] apps/web/src/messages/en.json — certManager namespace added
|
||||||
|
- [x] Commit 82a80e7 exists (i18n)
|
||||||
|
- [x] Commit 42a41f7 exists (shell + components)
|
||||||
|
- [x] Commit 2cb01f7 exists (tests)
|
||||||
Reference in New Issue
Block a user