Foundation — Pages¶
Scope note: Foundation owns only two system pages — dashboard placeholder and forbidden. The interesting content of this doc is the patterns these pages establish (composition of AppShell, semantic buttons, display primitives, error UX) — every later feature page reuses them.
Login was deleted. The pre-existing
src/routes/login.tsxwas vibe-coded against no real auth contract and gets removed in the M0 cleanup ticket. Login and account screens land back with identity's first ticket.
Page: Dashboard placeholder¶
| Field | Value |
|---|---|
| Route | / |
| File | src/routes/_auth/index.tsx (exists) |
| Layer | Inside AppShell (_auth group) |
| Owner ticket | Touched by M2 (AppShell ticket) — currently empty; gets a placeholder card "Welcome — modules load here as they ship". |
| Permissions | None today (no auth wiring). Once identity ships, this page renders for any authenticated user. |
Surface. Inside AppShell. Single welcome card with the logged-in user's name (placeholder string until identity ships) and a one-line "your modules will appear in the sidebar as they go live" message. Intentionally bare — this page will be redesigned per a future requirement once 3+ modules exist (KPI cards, recent activity, etc.).
Components used.
- AppShell (M2)
- PageContainer variant=detail (M2)
- Card, PageHeader (M1)
Empty / error states. Renders the same regardless of role. No data fetched.
Prototype. ux-tests/prototypes/action-flow-prototypes/01-home.html — referenced for chrome only (the dashboard content itself is intentionally minimal).
Page: Forbidden¶
| Field | Value |
|---|---|
| Route | /forbidden |
| File | src/routes/_auth/forbidden.tsx (new — M2 ticket) |
| Layer | Inside AppShell — user is authenticated but lacks the permission for what they tried to access. |
| Owner ticket | M2 — AppShell ticket. Triggered by the error parser when it receives 403 FORBIDDEN_INSUFFICIENT_PERMISSION. |
| Permissions | Any authenticated user (this is the page they land on when they fail a permission check). |
Surface. Centered card with shield icon, large title t('common:errors.forbiddenTitle') ("Brak dostępu"), one-line body t('common:errors.forbiddenBody') ("Skontaktuj się z administratorem jeśli uważasz, że powinieneś mieć dostęp do tej strony."), one [Wróć do panelu] button linking to routes.dashboard.path.
Components used.
- Card, Button (variant=default) from components/ui/.
Empty / error states. This is an error state. No fetch.
Prototype. None.
Composition patterns established here¶
These patterns are referenced by every future feature page plan:
- AppShell wraps every route under
_auth/. Public routes (login, etc.) live outside_auth/when they're added by identity. PageContainerwithlistordetailvariant owns the page body's max-width, padding, and background.PageHeaderat the top of every authenticated page — owns the page title (from the route registry'si18nKey), optional breadcrumbs, and the right-aligned primary actions slot.- Forms use
components/forms/field wrappers (M4), never raw<input>or shadcn<Input>directly. The wrappers handle label + error + i18n. - Errors surface in three places per
../../standards.md§ Error Envelope: - Field errors → inline under the field via
applyFieldErrorsToForm. - Non-field errors with a known code →
useApiErrorToast()(helper added in M3 errors ticket). - 403 → navigate to
/forbidden. 409 → refresh-and-retry inline. 5xx → "Spróbuj ponownie później" toast. - No permission gates run today — the
requiredPermissionprops on buttons are wired but always pass. Identity ticket flips the switch.