Add parseListQuery helper (todo §1); read a list URL into { q, filters, sort, page, pageSize }, defaults+clamp, zero-throw

This commit is contained in:
2026-06-15 13:38:34 +02:00
parent c2bcce9845
commit 20f49c1df7
4 changed files with 127 additions and 1 deletions

View File

@@ -35,7 +35,7 @@ everything via Docker.
- [x] Form-field partials (input/label/hint/error) + auth-card partial. → `views/partials/field.ejs`: data-driven `.field` — label (+ inline `link`/`Optional`), optional icon input (`has-ico`), `hint`, server-driven `error` (string | {text} | {html}) wiring `aria-invalid` + `aria-describedby`; added one CSS rule `.field.has-error .field-error{display:flex}` so a rendered field shows its own error. `views/partials/auth-card.ejs`: the `<form class="auth-card">` shell — head (back/title/sub), optional `sso` providers (text logo or icon, link or button) + divider, `body` slot (fields + submit), `alt` footer. `field.test.ts`/`auth-card.test.ts` cover the matrix + escaping + defaults.
- [x] Menu/popover + theme-switch partials (pure CSS `details`/`summary`). → `views/partials/menu.ejs`: data-driven `<details>` popover — `trigger` (icon/text/raw-html, `class:""` ⇒ bare kebab), `align`/`up` positioning, `width`; `items` = head · sep · link/button (icon, danger) · check-`group` (the columns/“more filters” menus filter-bar deferred here). `views/partials/theme-switch.ejs`: Light/Auto/Dark radiogroup with the fixed `theme-light/auto/dark` ids `styles.css` keys its `:has()` swaps off. Added `.menu-pop.up` (replaces the mockup's inline up-positioning); `shell.ejs` now reuses both partials. `menu.test.ts`/`theme-switch.test.ts` cover the matrix + escaping + defaults.
- [x] Helper `composeNav(fragments, override, roles)` → merged, permission-filtered tree. → `src/nav.ts`: pure, I/O-free. Flattens plugin fragments, applies the central override (rename → group → order → hide, all keyed by node `id`), then role-filters — a node shows iff it has no `permission` or `roles` includes it; a gated header drops its whole subtree, an emptied pure header is dropped. Emits clean nodes (no `id`/`permission`, absent fields omitted) ready for `nav-tree.ejs`. Filter runs last so everything above is per-deployment. `NavNode`/`NavOverride`/`NavGroupSpec` types exported; `nav.test.ts` covers merge/filter/empties/override matrix.
- [ ] Helper `parseListQuery(url)``{ q, filters, sort, page, pageSize }`.
- [x] Helper `parseListQuery(url)``{ q, filters, sort, page, pageSize }`.`src/list-query.ts`: pure, never throws; inverse of the filter-bar GET form + sort/pagination links. Accepts `URL`/`URLSearchParams`/string. `q` trimmed; `filters` = every non-reserved param as `string[]` (multi-value chips kept, empties dropped); `sort` = `{field,dir}` with `-field` ⇒ desc (lone `-`/empty ⇒ null); `page` a positive int (else 1); `pageSize` defaults 25, clamped to [1, max 100]. Reserved names + page-size bounds overridable via options. `list-query.test.ts` covers the full/default/clamp/custom-name matrix.
- [ ] Helper `paginate(total, page, pageSize)` → page model.
- [ ] Replace placeholder `index` with the app-shell dashboard.
- [ ] Check the full system in Playwright and make screenshots and compare to the static original design in html-css-foundation to make sure we're showing the correct graphics.