Browse Source

fix(docs): update search provider configuration and filter rules for clarity and usability

Code_Uwe 2 weeks ago
parent
commit
2d39b85c68
4 changed files with 231 additions and 5 deletions
  1. 76 2
      Docs/api.md
  2. 39 1
      Docs/frontend-api-usage.md
  3. 40 2
      Docs/frontend-ui.md
  4. 76 0
      Docs/runbook.md

+ 76 - 2
Docs/api.md

@@ -1,5 +1,15 @@
 <!-- --------------------------------------------------------------------------- -->
 
+<!-- Ordner: Docs -->
+
+<!-- Datei: api.md -->
+
+<!-- Relativer Pfad: Docs/api.md -->
+
+<!-- --------------------------------------------------------------------------- -->
+
+<!-- --------------------------------------------------------------------------- -->
+
 <!-- Ordner: docs -->
 
 <!-- Datei: api.md -->
@@ -32,6 +42,56 @@ Optional environment variables:
 
 - `SESSION_COOKIE_SECURE` — override for the cookie `Secure` flag (`true`/`false`).
 
+### 1.1 Search provider configuration (RHL-016)
+
+The Search API can run with different provider backends.
+
+- `SEARCH_PROVIDER` (optional)
+
+  - Allowed values: `fs` | `qsirch`
+  - Default: `fs`
+
+  Notes:
+
+  - `fs` is a local/test fallback that traverses the NAS-like folder structure directly.
+  - `qsirch` is the intended production provider (indexed search on QNAP).
+
+If `SEARCH_PROVIDER=qsirch`, these variables are required:
+
+- `QSIRCH_BASE_URL` — base URL of the Qsirch service (must be reachable from inside the app container).
+
+  - Example: `http://192.168.0.22:8080`
+
+- `QSIRCH_ACCOUNT` — QTS/Qsirch account used for server-to-server search.
+
+- `QSIRCH_PASSWORD` — password for the account.
+
+- `QSIRCH_PATH_PREFIX` — path prefix that contains the branch folders.
+
+  - Example: `/Niederlassungen`
+
+Optional Qsirch tuning:
+
+- `QSIRCH_DATE_FIELD`
+
+  - Allowed values: `modified` | `created` (case-insensitive)
+  - Default: `modified`
+
+- `QSIRCH_MODE`
+
+  - Allowed values: `sync` | `async` | `auto` (case-insensitive)
+  - Default: `sync`
+
+  Notes:
+
+  - The current implementation is **sync-first**.
+  - `auto` currently behaves like `sync` (placeholder for a later async implementation).
+
+**Normalization rule (A.1)**
+
+- `lib/config/validateEnv.js` accepts these values case-insensitively.
+- Runtime normalizes Qsirch values (trim + lowercase for `QSIRCH_DATE_FIELD` / `QSIRCH_MODE`, and trim for `QSIRCH_PATH_PREFIX`) so behavior matches validation.
+
 The environment can be validated via:
 
 - `lib/config/validateEnv.js`
@@ -610,9 +670,9 @@ The endpoint returns _search hits_ with enough metadata to:
 
 **Query params**
 
-- `q` (required)
+- `q` (optional)
 
-  - Search query string.
+  - Text query string.
 
 - `scope` (optional)
 
@@ -644,6 +704,20 @@ The endpoint returns _search hits_ with enough metadata to:
   - Pagination cursor returned by the previous response.
   - Treat as **opaque**.
 
+**Filter rule (important)**
+
+To avoid accidental "match everything" queries (especially dangerous in `scope=all`), the backend requires:
+
+- at least one of:
+
+  - `q`
+  - `from`
+  - `to`
+
+If all three are missing, the API returns:
+
+- `400 VALIDATION_SEARCH_MISSING_FILTER`
+
 **Response 200 (example)**
 
 ```json

+ 39 - 1
Docs/frontend-api-usage.md

@@ -1,5 +1,15 @@
 <!-- --------------------------------------------------------------------------- -->
 
+<!-- Ordner: Docs -->
+
+<!-- Datei: frontend-api-usage.md -->
+
+<!-- Relativer Pfad: Docs/frontend-api-usage.md -->
+
+<!-- --------------------------------------------------------------------------- -->
+
+<!-- --------------------------------------------------------------------------- -->
+
 <!-- Ordner: docs -->
 
 <!-- Datei: frontend-api-usage.md -->
@@ -436,7 +446,7 @@ In the frontend, prefer the dedicated wrapper:
 
 Query params:
 
-- `q` (required)
+- `q` (optional)
 
 Optional filters:
 
@@ -447,6 +457,16 @@ Optional filters:
 - `limit`: page size (default `100`, allowed `50..200`)
 - `cursor`: pagination cursor returned by the previous response
 
+Filter rule:
+
+- The backend requires **at least one filter** to avoid accidental “match everything” searches:
+
+  - `q` OR `from` OR `to`
+
+If all three are missing, the API returns:
+
+- `400 VALIDATION_SEARCH_MISSING_FILTER`
+
 Response shape:
 
 ```json
@@ -481,6 +501,7 @@ import { search, ApiClientError } from "@/lib/frontend/apiClient";
 
 export async function searchDeliveryNotesExample() {
 	try {
+		// Text search example:
 		const res = await search({
 			q: "bridgestone",
 			branch: "NL20",
@@ -504,6 +525,23 @@ export async function searchDeliveryNotesExample() {
 }
 ```
 
+Date-range-only example (no `q`, allowed as long as at least one of `from/to` is present):
+
+```js
+import { search } from "@/lib/frontend/apiClient";
+
+export async function searchByDateRangeOnly() {
+	const res = await search({
+		scope: "all",
+		from: "2025-12-01",
+		to: "2025-12-31",
+		limit: 100,
+	});
+
+	return res;
+}
+```
+
 Using a hit to navigate/open:
 
 - Navigate to the day folder:

+ 40 - 2
Docs/frontend-ui.md

@@ -1,5 +1,15 @@
 <!-- --------------------------------------------------------------------------- -->
 
+<!-- Ordner: Docs -->
+
+<!-- Datei: frontend-ui.md -->
+
+<!-- Relativer Pfad: Docs/frontend-ui.md -->
+
+<!-- --------------------------------------------------------------------------- -->
+
+<!-- --------------------------------------------------------------------------- -->
+
 <!-- Folder: docs -->
 
 <!-- File: frontend-ui.md -->
@@ -243,6 +253,21 @@ AppShell is the stable frame for all protected pages:
 - The sidebar is currently a placeholder (reserved for future navigation/filter UI).
 - Route pages render inside the main content area.
 
+#### 4.1.1 Width policy and sidebar docking (2xl+)
+
+On very wide screens the UI should remain readable.
+
+Current strategy (implemented in `AppShell.jsx` + `TopNav.jsx`):
+
+- Use a centered content column (~60% of the viewport at `2xl+`).
+- Keep the Explorer/Search content inside that centered column.
+- Render the sidebar in the left gutter and align it to the right edge so it “docks” to the centered content **without consuming the centered width**.
+
+Below `2xl`:
+
+- Use full width for usability.
+- Hide the sidebar placeholder to avoid shrinking the main content area.
+
 ### 4.2 Quick navigation (quality-of-life)
 
 File: `components/app-shell/QuickNav.jsx`
@@ -265,6 +290,13 @@ Behavior:
   - Provides a branch dropdown.
   - Stores the last selected branch in `localStorage` for convenience.
 
+Implementation notes:
+
+- Branch list loading is intentionally **not** tied to the dropdown selection.
+
+  - The list is fetched once for the authenticated admin/dev user (or when the user changes).
+  - This avoids unnecessary refetches when switching branches in the UI.
+
 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:
@@ -572,7 +604,8 @@ Admin/dev users:
 - Multi branch selection:
 
   - checkbox grid optimized for large branch counts
-  - 5 columns on desktop (`md:grid-cols-5`)
+  - “selectable card” label wrapper highlights checked items (border + background)
+  - pointer cursor + hover affordances improve discoverability
   - optional “Alle abwählen” button to reset selection
 
 - Branch list:
@@ -604,11 +637,16 @@ Actions:
 
   - navigates to `/:branch/:year/:month/:day` using `dayPath(...)`
 
+Implementation note:
+
+- In the Search results table, the actions are intentionally kept in a fixed-width column.
+- Buttons may render side-by-side (desktop) and can wrap on very small widths.
+
 Sorting:
 
 - “Relevanz” (backend order)
 - “Datum (neueste zuerst)”
-- “Dateiname (A–Z)”
+- “Niederlassung” (NLxx ascending; with stable tie-breakers by date and filename)
 
 Totals:
 

+ 76 - 0
Docs/runbook.md

@@ -1,5 +1,15 @@
 <!-- --------------------------------------------------------------------------- -->
 
+<!-- Ordner: Docs -->
+
+<!-- Datei: runbook.md -->
+
+<!-- Relativer Pfad: Docs/runbook.md -->
+
+<!-- --------------------------------------------------------------------------- -->
+
+<!-- --------------------------------------------------------------------------- -->
+
 <!-- --------------------------------------------------------------------------- -->
 
 <!-- Ordner: Docs -->
@@ -86,6 +96,48 @@ Then edit `.env.docker`:
 SESSION_COOKIE_SECURE=false
 ```
 
+#### 2.2.1 Optional: Search provider configuration (RHL-016)
+
+The Search backend supports two providers:
+
+- `SEARCH_PROVIDER=fs` (default)
+
+  - Local/test fallback.
+  - Traverses the NAS-like folder structure directly.
+  - Not intended to be fast on real NAS scale.
+
+- `SEARCH_PROVIDER=qsirch`
+
+  - Production/intended provider.
+  - Uses QNAP Qsirch for indexed search.
+
+If you set `SEARCH_PROVIDER=qsirch`, these env variables are required:
+
+```env
+SEARCH_PROVIDER=qsirch
+QSIRCH_BASE_URL=http://<nas-ip>:8080
+QSIRCH_ACCOUNT=<qsirch-user>
+QSIRCH_PASSWORD=***
+QSIRCH_PATH_PREFIX=/Niederlassungen
+```
+
+Optional Qsirch tuning:
+
+```env
+# Allowed: modified | created (case-insensitive)
+QSIRCH_DATE_FIELD=modified
+
+# Allowed: sync | async | auto (case-insensitive)
+# Current implementation is sync-first.
+# - "auto" currently behaves like "sync" (future-proof placeholder).
+QSIRCH_MODE=sync
+```
+
+Notes:
+
+- `scripts/validate-env.mjs` enforces these keys when `SEARCH_PROVIDER=qsirch`.
+- Runtime normalizes Qsirch values (`trim`, `lowercase` for `QSIRCH_DATE_FIELD` / `QSIRCH_MODE`, and `trim` for `QSIRCH_PATH_PREFIX`) so behavior matches env validation.
+
 ### 2.3 Create local NAS fixtures
 
 Create a minimal NAS tree:
@@ -109,6 +161,9 @@ If you prefer running in the background:
 docker compose -f docker-compose.yml -f docker-compose.local.yml up -d --build
 ```
 
+> Note: The `app` container runs `node scripts/validate-env.mjs` automatically before `npm run start` (see `docker-compose.yml` `command:`).
+> If required env vars are missing/invalid, the container fails fast and logs the validation error.
+
 ### 2.5 Verify health
 
 ```bash
@@ -273,6 +328,27 @@ This is required because most clients will not send `Secure` cookies over HTTP.
 > If the application is served over **plain HTTP** (no TLS), many clients will not send `Secure` cookies back.
 > In that case, logins will appear to “work” (Set-Cookie is present), but subsequent requests will still be unauthenticated.
 
+#### 3.3.1 Optional: Search provider configuration (RHL-016)
+
+If the server should use Qsirch for search:
+
+```env
+SEARCH_PROVIDER=qsirch
+QSIRCH_BASE_URL=http://<nas-ip>:8080
+QSIRCH_ACCOUNT=<qsirch-user>
+QSIRCH_PASSWORD=***
+QSIRCH_PATH_PREFIX=/Niederlassungen
+
+# Optional:
+QSIRCH_DATE_FIELD=modified
+QSIRCH_MODE=sync
+```
+
+Operational notes:
+
+- `QSIRCH_BASE_URL` must be reachable **from inside the app container**.
+- Values are treated case-insensitively by validation; runtime normalizes the same way.
+
 ### 3.4 Start the stack on the server
 
 Use the base compose file only (no local override):