|
|
@@ -1,14 +1,14 @@
|
|
|
<!-- --------------------------------------------------------------------------- -->
|
|
|
|
|
|
-<!-- Ordner: docs -->
|
|
|
+<!-- Folder: docs -->
|
|
|
|
|
|
-<!-- Datei: frontend-ui.md -->
|
|
|
+<!-- File: frontend-ui.md -->
|
|
|
|
|
|
-<!-- Relativer Pfad: docs/frontend-ui.md -->
|
|
|
+<!-- Relative Path: docs/frontend-ui.md -->
|
|
|
|
|
|
<!-- --------------------------------------------------------------------------- -->
|
|
|
|
|
|
-# Frontend UI: App Shell, Routing, Auth/RBAC, Explorer, and Search (RHL-019 / RHL-020 / RHL-021 / RHL-022 / RHL-023 / RHL-024)
|
|
|
+# Frontend UI: App Shell, Routing, Auth/RBAC, Explorer, and Search (RHL-019 / RHL-020 / RHL-021 / RHL-022 / RHL-023 / RHL-024 / RHL-037)
|
|
|
|
|
|
This document describes the **frontend routing scaffold**, the **application shell layout**, and the core UI modules for the RHL Lieferscheine app.
|
|
|
|
|
|
@@ -20,6 +20,7 @@ Timeline:
|
|
|
- **RHL-022**: Explorer v2 (Year → Month → Day → Files) + shadcn Breadcrumbs with dropdowns.
|
|
|
- **RHL-023**: Explorer file action “Open PDF” using the binary PDF endpoint (opens in a new tab).
|
|
|
- **RHL-024**: Search UI v1 (URL-driven `q`, scopes, cursor pagination, open PDF + jump to day).
|
|
|
+- **RHL-037**: Search scope UX improvements (TopNav deep-link branch switching + Single combobox + Multi selection UX + deterministic URL state).
|
|
|
|
|
|
> **Language policy**
|
|
|
>
|
|
|
@@ -31,7 +32,7 @@ Timeline:
|
|
|
|
|
|
## 1. Scope
|
|
|
|
|
|
-### 1.1 Implemented (as of RHL-024)
|
|
|
+### 1.1 Implemented (as of RHL-037)
|
|
|
|
|
|
- **Public** `/login` route with a functional login form (shadcn/ui primitives).
|
|
|
|
|
|
@@ -95,7 +96,7 @@ Timeline:
|
|
|
- Clicking “Öffnen” opens the selected PDF **in a new browser tab**.
|
|
|
- URL construction is centralized in `lib/frontend/explorer/pdfUrl.js`.
|
|
|
|
|
|
-- **Search UI v1 (RHL-024)**
|
|
|
+- **Search UI (RHL-024) + Scope UX improvements (RHL-037)**
|
|
|
|
|
|
- Route: `/:branch/search` (protected).
|
|
|
|
|
|
@@ -103,31 +104,37 @@ Timeline:
|
|
|
|
|
|
- `q` (search query)
|
|
|
|
|
|
- - scope params for admin/dev:
|
|
|
+ - scope semantics:
|
|
|
|
|
|
- - single: `branch=NLxx`
|
|
|
- - multi: `scope=multi&branches=NL06,NL20`
|
|
|
- - all: `scope=all`
|
|
|
+ - **Single**: route branch is the scope (`/:branch/search`). No `branch=` query parameter is required.
|
|
|
+ - **Multi**: `scope=multi&branches=NL06,NL20` (deterministic order, unique list)
|
|
|
+ - **All**: `scope=all`
|
|
|
|
|
|
- - `limit` (optional): `50 | 100 | 200` (default 100; only included in URL when non-default)
|
|
|
+ - `limit` (optional): `50 | 100 | 200` (default 100)
|
|
|
+
|
|
|
+ - `from/to` are carried through the URL but date-range UI is still planned for later.
|
|
|
|
|
|
- Cursor-based pagination (`nextCursor`) is **not stored in the URL**.
|
|
|
|
|
|
- - Results support:
|
|
|
+ - Admin/dev UX:
|
|
|
+
|
|
|
+ - **TopNav branch switch navigates** (deep-link branch switching):
|
|
|
+
|
|
|
+ - preserves the current deep path (Explorer and Search)
|
|
|
+ - preserves shareable Search query params (`q`, `scope`, `branches`, `limit`, `from`, `to`)
|
|
|
+ - keeps Single scope consistent with the route branch.
|
|
|
|
|
|
- - Open PDF in a new tab (“Öffnen”)
|
|
|
- - Navigate to the Explorer day route (“Zum Tag”)
|
|
|
+ - **Single scope branch selection** uses a shadcn combobox to switch the _route branch_.
|
|
|
|
|
|
- - Search results UI includes:
|
|
|
+ - **Multi scope branch selection** uses a checkbox grid (optimized for large branch counts) and provides an **“Alle abwählen”** action.
|
|
|
|
|
|
- - Empty/Loading/Error states
|
|
|
- - Sorting dropdown (Relevanz / Datum / Dateiname)
|
|
|
- - “Mehr laden” button (cursor pagination)
|
|
|
- - “x von y” progress indicator when `total` is available
|
|
|
+ - Branch list for admin/dev is fetched via `GET /api/branches` (fail-open).
|
|
|
+
|
|
|
+---
|
|
|
|
|
|
### 1.2 Still out of scope / planned
|
|
|
|
|
|
-- Date range UI for Search (`from` / `to`) and URL sync (planned follow-up after Search UI v1).
|
|
|
+- Date range UI for Search (`from` / `to`) and URL sync (planned follow-up after Search scope UX).
|
|
|
|
|
|
- Optional Search UX improvements:
|
|
|
|
|
|
@@ -251,12 +258,31 @@ Purpose:
|
|
|
Behavior:
|
|
|
|
|
|
- Branch users: QuickNav uses the user’s `branchId`.
|
|
|
+
|
|
|
- Admin/dev users:
|
|
|
|
|
|
- QuickNav loads branches via `GET /api/branches`.
|
|
|
- Provides a branch dropdown.
|
|
|
- Stores the last selected branch in `localStorage` for convenience.
|
|
|
|
|
|
+RHL-037 navigation rule:
|
|
|
+
|
|
|
+- When admin/dev selects a branch in QuickNav, the app **navigates** to the same section while replacing the path branch segment:
|
|
|
+
|
|
|
+ - Explorer deep paths are preserved:
|
|
|
+
|
|
|
+ - `/NL32/2025/12/31` → `/NL20/2025/12/31`
|
|
|
+
|
|
|
+ - Search route is preserved and shareable query params are preserved:
|
|
|
+
|
|
|
+ - `/NL32/search?q=x&scope=multi&branches=NL06,NL20&limit=200` → `/NL20/search?q=x&scope=multi&branches=NL06,NL20&limit=200`
|
|
|
+
|
|
|
+ - Single scope is kept consistent with the route branch (no divergence between UI selection and URL).
|
|
|
+
|
|
|
+Implementation note:
|
|
|
+
|
|
|
+- Helper logic for deep-path branch switching is centralized under `lib/frontend/quickNav/branchSwitch.js`.
|
|
|
+
|
|
|
Responsive behavior:
|
|
|
|
|
|
- QuickNav is hidden on small screens by default (`md` and up only) to keep the header compact.
|
|
|
@@ -469,7 +495,7 @@ Implementation notes:
|
|
|
|
|
|
---
|
|
|
|
|
|
-## 8. Search UI v1 (RHL-024)
|
|
|
+## 8. Search UI (RHL-024) + Scope UX improvements (RHL-037)
|
|
|
|
|
|
### 8.1 Route and state model
|
|
|
|
|
|
@@ -477,7 +503,13 @@ Route:
|
|
|
|
|
|
- `/:branch/search`
|
|
|
|
|
|
-State policy:
|
|
|
+Single Source of Truth rule (RHL-037):
|
|
|
+
|
|
|
+- The **path segment** `/:branch/search` is the source of truth for the **current branch context**.
|
|
|
+- Single-scope search uses the **route branch**.
|
|
|
+- Shareable URLs for search do not need to carry `branch=` for Single.
|
|
|
+
|
|
|
+URL-driven state policy:
|
|
|
|
|
|
- Search state is **URL-driven** to support shareable links.
|
|
|
- Cursor-based pagination state is **not shareable** and is kept in client state.
|
|
|
@@ -488,9 +520,18 @@ Shareable params (first page identity):
|
|
|
|
|
|
- Scope params:
|
|
|
|
|
|
- - single branch: `branch=NLxx`
|
|
|
- - multi: `scope=multi&branches=NL06,NL20`
|
|
|
- - all: `scope=all`
|
|
|
+ - **Single**: no `scope` param required; route branch defines the branch.
|
|
|
+
|
|
|
+ - Example: `/NL01/search?q=reifen`
|
|
|
+
|
|
|
+ - **Multi**: `scope=multi&branches=NL06,NL20`
|
|
|
+
|
|
|
+ - branches list is deterministic:
|
|
|
+
|
|
|
+ - unique
|
|
|
+ - stable ordering
|
|
|
+
|
|
|
+ - **All**: `scope=all`
|
|
|
|
|
|
- `limit`:
|
|
|
|
|
|
@@ -498,6 +539,11 @@ Shareable params (first page identity):
|
|
|
- default: `100`
|
|
|
- only written to URL when non-default to keep URLs readable
|
|
|
|
|
|
+- `from/to`:
|
|
|
+
|
|
|
+ - carried through the URL to prepare for the date-range UI
|
|
|
+ - date-range UI is still planned
|
|
|
+
|
|
|
Pagination:
|
|
|
|
|
|
- `nextCursor` is kept in client state.
|
|
|
@@ -514,15 +560,28 @@ Admin/dev users:
|
|
|
|
|
|
- Scope selector:
|
|
|
|
|
|
- - “Diese Niederlassung” (single branch route context)
|
|
|
+ - “Diese Niederlassung” (single: route branch)
|
|
|
- “Mehrere Niederlassungen” (multi)
|
|
|
- “Alle Niederlassungen” (all)
|
|
|
|
|
|
+- Single branch selection:
|
|
|
+
|
|
|
+ - shadcn combobox
|
|
|
+ - selecting a branch **navigates** to `/:branch/search` while preserving shareable query params (`q`, scope params, `limit`, `from/to`).
|
|
|
+
|
|
|
- Multi branch selection:
|
|
|
|
|
|
- - simple checkbox list
|
|
|
- - branch list is fetched from `GET /api/branches`
|
|
|
- - fail-open behavior: if branch list cannot be loaded, the rest of the Search UI remains usable
|
|
|
+ - checkbox grid optimized for large branch counts
|
|
|
+ - 5 columns on desktop (`md:grid-cols-5`)
|
|
|
+ - optional “Alle abwählen” button to reset selection
|
|
|
+
|
|
|
+- Branch list:
|
|
|
+
|
|
|
+ - loaded via `GET /api/branches`
|
|
|
+ - fail-open UI behavior:
|
|
|
+
|
|
|
+ - if branch list fails, Search UI remains usable
|
|
|
+ - (optional fallback) allow manual NLxx input for selection
|
|
|
|
|
|
### 8.3 Result list and actions
|
|
|
|
|
|
@@ -575,6 +634,10 @@ Key outcomes:
|
|
|
- `VALIDATION_*` → show a user-friendly German validation message
|
|
|
- network/unknown → generic German error + retry
|
|
|
|
|
|
+UX note (RHL-037):
|
|
|
+
|
|
|
+- Multi scope with a query but zero branches selected is treated as “not ready” and shows a friendly hint.
|
|
|
+
|
|
|
---
|
|
|
|
|
|
## 9. UI primitives (shadcn/ui)
|
|
|
@@ -593,6 +656,12 @@ Required components for the current scope:
|
|
|
- `skeleton`
|
|
|
- `table`
|
|
|
|
|
|
+Additional primitives used for Search scope UX:
|
|
|
+
|
|
|
+- `popover`
|
|
|
+- `command`
|
|
|
+- `badge`
|
|
|
+
|
|
|
---
|
|
|
|
|
|
## 10. File Naming Convention (.js vs .jsx)
|
|
|
@@ -645,6 +714,10 @@ Search helper tests:
|
|
|
- `lib/frontend/search/searchApiInput.test.js`
|
|
|
- `lib/frontend/search/resultsSorting.test.js`
|
|
|
|
|
|
+QuickNav helper tests:
|
|
|
+
|
|
|
+- `lib/frontend/quickNav/branchSwitch.test.js`
|
|
|
+
|
|
|
Component SSR smoke test:
|
|
|
|
|
|
- `components/app-shell/AppShell.test.js`
|
|
|
@@ -717,13 +790,19 @@ Search checks:
|
|
|
- admin/dev:
|
|
|
|
|
|
- scope switching (single/multi/all)
|
|
|
- - multi selection (checkboxes)
|
|
|
+ - single branch selection via combobox
|
|
|
+ - multi selection via checkbox grid + “Alle abwählen”
|
|
|
- limit switching (50/100/200)
|
|
|
|
|
|
- pagination:
|
|
|
|
|
|
- “Mehr laden” appends results when `nextCursor` exists
|
|
|
|
|
|
+- TopNav QuickNav:
|
|
|
+
|
|
|
+ - switching branch updates the URL and keeps the current deep path
|
|
|
+ - switching branch on Search keeps shareable query params
|
|
|
+
|
|
|
### 12.2 Server
|
|
|
|
|
|
Deploy and verify on the server URL.
|
|
|
@@ -737,6 +816,7 @@ Verify:
|
|
|
- limit selection
|
|
|
- total count (“x von y geladen”)
|
|
|
- open PDF / jump to day
|
|
|
+ - TopNav branch switching keeps deep links
|
|
|
|
|
|
---
|
|
|
|