Files
2026-06-30 23:23:50 +02:00

3.5 KiB

Tech Stack Decisions — Unit 3: frontend-cms-page

Existing stack — no changes

Layer Technology Notes
UI components Shadcn/UI Dialog, Table, Select, Badge, DropdownMenu — all already present
Styling Tailwind CSS opacity-50 for Inactive rows (BR-FE-04)
Data fetching TanStack Query v5 useQuery + useMutation pattern already used by useUsers
Form state react-hook-form + zodResolver Used in InviteUserDialog, LoginPage, SetupPage
Schema validation Zod z.string().url() for URL field (NFR-FE-13)
Routing TanStack Router /cms route already exists; RoleGuard already wired
i18n react-i18next New cms.* keys added to EN + NL locales
Toasts Sonner toast.success() / toast.error() — already used throughout
Testing Vitest + React Testing Library + MSW All already configured
HTTP client Custom api-client (NetworkError, ProblemDetailsError) Same error types used across all existing hooks

Decisions made in this unit

TSD-01 — URL validation: z.string().url() (strict)

Decision: Use Zod's built-in z.string().url() for the URL field.
Rationale: Q1: A. Users must enter a fully-qualified URL with protocol. The backend validates anyway, but client-side strict validation stops obviously malformed input early and improves error feedback.
Implication: Users entering 192.168.1.5:8080 will see a validation error. They must enter http://192.168.1.5:8080.

TSD-02 — Test scope: hooks + page + component tests

Decision: Three test layers:

  1. Hook unit tests (useCmsInstances.test.ts, useAddCmsInstance.test.ts, useUpdateCmsInstanceStatus.test.ts) using renderHook + MSW server
  2. Page integration test (CmsPage.test.tsx) covering load, empty state, add flow, status flow
  3. Component tests (AddCmsInstanceDialog.test.tsx, SetStatusDialog.test.tsx) for error states and conditional logic

Rationale: Q2: C. Matches InviteUserDialog.test.tsx precedent. Dialog component tests are necessary to cover FormErrorBanner on HTTP 400 and the DisableMessage conditional show/hide (BR-FE-08) without a full page mount.

TSD-03 — ApiKey field: PasswordField with show/hide toggle

Decision: Reuse the existing PasswordField component (src/components/ui/PasswordField.tsx) for the apiKey input.
Rationale: Q3: A. Hides the credential from shoulder-surfing while allowing the user to reveal it for verification. No new component needed — PasswordField already implements the show/hide pattern.

TSD-04 — Toast content: differentiated on slaveContactSuccess

Decision: Two distinct toast messages for PUT /api/v1/CmsInstances/{id}/status:

  • slaveContactSuccess === truet('cms.setStatus.successContactedToast') = "Status updated — cliënt confirmed"
  • slaveContactSuccess === falset('cms.setStatus.successUnreachableToast') = "Status saved — cliënt unreachable"

Rationale: Q3 FD: B + user terminology preference (no "slave" in UI). Both cases are HTTP 200; the distinction is surfaced via the UpdateStatusResult.slaveContactSuccess field from the backend.

TSD-05 — FormErrorBanner component name

The project has both FormBannerError (src/components/ui/FormBannerError.tsx) and FormErrorBanner (src/components/ui/FormErrorBanner.tsx). The dialogs use FormErrorBanner (as used in InviteUserDialog). The AddCmsInstanceDialog and SetStatusDialog will import from @/components/ui/FormErrorBanner.