|
|
@@ -0,0 +1,266 @@
|
|
|
+<!-- --------------------------------------------------------------------------- -->
|
|
|
+
|
|
|
+<!-- Folder: Docs -->
|
|
|
+
|
|
|
+<!-- File: frontend-ui.md -->
|
|
|
+
|
|
|
+<!-- Relative Path: Docs/frontend-ui.md -->
|
|
|
+
|
|
|
+<!-- --------------------------------------------------------------------------- -->
|
|
|
+
|
|
|
+# Frontend UI: App Shell & Routing Scaffold (RHL-019)
|
|
|
+
|
|
|
+This document describes the **frontend routing scaffold** and the **application shell layout** for the RHL Lieferscheine app.
|
|
|
+
|
|
|
+Scope (RHL-019):
|
|
|
+
|
|
|
+- Public `/login` route.
|
|
|
+- Protected application shell for all other routes.
|
|
|
+- Placeholder pages for branch/year/month/day routes and branch search.
|
|
|
+- Minimal session-awareness placeholders (no real auth guard yet).
|
|
|
+- Minimal tests to validate scaffold stability.
|
|
|
+
|
|
|
+Non-goals (out of scope for RHL-019):
|
|
|
+
|
|
|
+- Real login form, auth guard, and session-based redirects.
|
|
|
+- Explorer navigation UI (years/months/days list).
|
|
|
+- Search UI and filters.
|
|
|
+- PDF viewer / file open.
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 1. Route Groups & URL Structure
|
|
|
+
|
|
|
+The app uses Next.js App Router **Route Groups** to separate public and protected UI.
|
|
|
+
|
|
|
+### 1.1 Route groups
|
|
|
+
|
|
|
+- **Public**: `app/(public)`
|
|
|
+
|
|
|
+ - Routes that do **not** show the authenticated app shell.
|
|
|
+ - Current route: `/login`
|
|
|
+
|
|
|
+- **Protected**: `app/(protected)`
|
|
|
+
|
|
|
+ - Routes that render inside the **AppShell**.
|
|
|
+ - RHL-019 intentionally does **not** enforce authentication yet.
|
|
|
+
|
|
|
+### 1.2 URL map (scaffold)
|
|
|
+
|
|
|
+| URL | Purpose | Notes |
|
|
|
+| ---------------------------- | --------------------------- | ------------------------------------------------------ |
|
|
|
+| `/login` | Login placeholder | Public layout (no AppShell) |
|
|
|
+| `/` | Protected entry placeholder | Later redirects based on session |
|
|
|
+| `/:branch` | Branch placeholder | Example: `/NL01` |
|
|
|
+| `/:branch/:year` | Year placeholder | Example: `/NL01/2025` |
|
|
|
+| `/:branch/:year/:month` | Month placeholder | Example: `/NL01/2025/12` |
|
|
|
+| `/:branch/:year/:month/:day` | Day placeholder | Example: `/NL01/2025/12/31` |
|
|
|
+| `/:branch/search` | Search placeholder | Explicit segment so `search` is not treated as `:year` |
|
|
|
+
|
|
|
+Important:
|
|
|
+
|
|
|
+- There is **no** standalone `/search` route. Visiting `/search` matches `/:branch` with `branch = "search"`.
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 2. Layouts
|
|
|
+
|
|
|
+### 2.1 Root layout
|
|
|
+
|
|
|
+File: `app/layout.jsx`
|
|
|
+
|
|
|
+Responsibilities:
|
|
|
+
|
|
|
+- Global CSS imports (`app/globals.css`).
|
|
|
+- Theme provider setup (shadcn/ui + next-themes wrapper).
|
|
|
+- Base HTML/body structure.
|
|
|
+
|
|
|
+### 2.2 Public layout
|
|
|
+
|
|
|
+File: `app/(public)/layout.jsx`
|
|
|
+
|
|
|
+Responsibilities:
|
|
|
+
|
|
|
+- Minimal centered layout for public routes.
|
|
|
+- Intended for `/login` (and potential future public routes).
|
|
|
+
|
|
|
+### 2.3 Protected layout
|
|
|
+
|
|
|
+File: `app/(protected)/layout.jsx`
|
|
|
+
|
|
|
+Responsibilities:
|
|
|
+
|
|
|
+- Wraps all protected pages with the **AppShell**.
|
|
|
+- Intentionally contains **no auth guard** in RHL-019.
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 3. AppShell
|
|
|
+
|
|
|
+The AppShell is the stable frame for authenticated UI.
|
|
|
+
|
|
|
+Folder: `components/app-shell/*`
|
|
|
+
|
|
|
+- `AppShell.jsx`
|
|
|
+
|
|
|
+ - Overall layout container: **TopNav** + content area.
|
|
|
+ - Uses `min-h-screen flex flex-col` so the content area can fill remaining height.
|
|
|
+
|
|
|
+- `TopNav.jsx`
|
|
|
+
|
|
|
+ - Branding.
|
|
|
+ - User status placeholder.
|
|
|
+ - Placeholder buttons for theme and logout (not wired yet).
|
|
|
+
|
|
|
+- `SidebarPlaceholder.jsx`
|
|
|
+
|
|
|
+ - Reserved space for future navigation/filter UI.
|
|
|
+ - Later additions:
|
|
|
+
|
|
|
+ - Admin/dev branch selector
|
|
|
+ - Explorer navigation (year/month/day)
|
|
|
+ - Search filters and shortcuts
|
|
|
+
|
|
|
+- `UserStatus.jsx`
|
|
|
+
|
|
|
+ - Placeholder only.
|
|
|
+ - Later reads session state (via `apiClient.getMe()`).
|
|
|
+
|
|
|
+### 3.1 Responsive behavior
|
|
|
+
|
|
|
+- Sidebar is hidden on small viewports (`md:block`).
|
|
|
+- Main content remains usable on mobile sizes.
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 4. Placeholder Pages
|
|
|
+
|
|
|
+Placeholder pages validate:
|
|
|
+
|
|
|
+- URL structure
|
|
|
+- Layout composition
|
|
|
+- Dynamic route parameter handling
|
|
|
+
|
|
|
+They render via:
|
|
|
+
|
|
|
+- `components/placeholders/PlaceholderPage.jsx`
|
|
|
+
|
|
|
+### 4.1 Dynamic route params (Next.js App Router)
|
|
|
+
|
|
|
+In this project setup, dynamic route `params` can behave like an async value.
|
|
|
+
|
|
|
+Rule of thumb:
|
|
|
+
|
|
|
+- In dynamic routes (`[branch]`, `[year]`, `[month]`, `[day]`), unwrap `params` before using properties.
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 5. File Naming Convention (.js vs .jsx)
|
|
|
+
|
|
|
+To keep the project consistent and avoid test/tooling issues:
|
|
|
+
|
|
|
+- Use **`.jsx`** for files that contain JSX:
|
|
|
+
|
|
|
+ - `app/**/page.jsx`, `app/**/layout.jsx`
|
|
|
+ - React components in `components/**`
|
|
|
+
|
|
|
+- Use **`.js`** for non-JSX files:
|
|
|
+
|
|
|
+ - `lib/**` utilities and helpers
|
|
|
+ - `app/api/**/route.js`
|
|
|
+ - `models/**`
|
|
|
+ - tests that do not contain JSX
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 6. Frontend Route Helpers
|
|
|
+
|
|
|
+File: `lib/frontend/routes.js`
|
|
|
+
|
|
|
+Purpose:
|
|
|
+
|
|
|
+- Centralize URL building so UI code does not scatter hardcoded strings.
|
|
|
+- Encode dynamic segments defensively.
|
|
|
+- Keep navigation consistent across components.
|
|
|
+
|
|
|
+Provided helpers:
|
|
|
+
|
|
|
+- `homePath()` → `/`
|
|
|
+- `loginPath()` → `/login`
|
|
|
+- `branchPath(branch)` → `/:branch`
|
|
|
+- `yearPath(branch, year)` → `/:branch/:year`
|
|
|
+- `monthPath(branch, year, month)` → `/:branch/:year/:month`
|
|
|
+- `dayPath(branch, year, month, day)` → `/:branch/:year/:month/:day`
|
|
|
+- `searchPath(branch)` → `/:branch/search`
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 7. Tests
|
|
|
+
|
|
|
+### 7.1 Unit tests
|
|
|
+
|
|
|
+- `lib/frontend/routes.test.js`
|
|
|
+
|
|
|
+ - Validates URL builder behavior.
|
|
|
+
|
|
|
+- `components/app-shell/AppShell.test.js`
|
|
|
+
|
|
|
+ - Smoke test: server-renders AppShell and asserts key text is present.
|
|
|
+ - `next/link` is mocked to avoid Next runtime dependency.
|
|
|
+
|
|
|
+### 7.2 Running tests
|
|
|
+
|
|
|
+From the repo root:
|
|
|
+
|
|
|
+```bash
|
|
|
+npx vitest run
|
|
|
+```
|
|
|
+
|
|
|
+Optional (recommended) build check:
|
|
|
+
|
|
|
+```bash
|
|
|
+npm run build
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 8. Manual Verification Checklist
|
|
|
+
|
|
|
+### 8.1 Local (Docker)
|
|
|
+
|
|
|
+Follow `Docs/runbook.md`.
|
|
|
+
|
|
|
+Typical command:
|
|
|
+
|
|
|
+```bash
|
|
|
+docker compose -f docker-compose.yml -f docker-compose.local.yml up -d --build
|
|
|
+```
|
|
|
+
|
|
|
+Verify routes:
|
|
|
+
|
|
|
+- `/login` (public layout)
|
|
|
+- `/` (protected app shell)
|
|
|
+- `/:branch` (e.g. `/NL01`)
|
|
|
+- `/:branch/:year/:month/:day` (placeholder)
|
|
|
+- `/:branch/search`
|
|
|
+
|
|
|
+### 8.2 Server
|
|
|
+
|
|
|
+Follow `Docs/runbook.md`.
|
|
|
+
|
|
|
+Verify:
|
|
|
+
|
|
|
+- `curl -s http://127.0.0.1:3000/api/health`
|
|
|
+- Browser:
|
|
|
+
|
|
|
+ - `http(s)://<server>:3000/login`
|
|
|
+ - `http(s)://<server>:3000/NL01/...` (or a real branch)
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 9. Planned Follow-ups
|
|
|
+
|
|
|
+- Add real **auth guard** and session-based redirects (`/` → `/login` or `/:branch`).
|
|
|
+- Replace placeholders with Explorer pages (years/months/days + files).
|
|
|
+- Add Search UI and filters.
|
|
|
+- Add PDF open/view experience.
|