feat: entry list at /content/:key

This commit is contained in:
Florian Sylvain committed 2026-10-20 21:05:03 +02:00
1 parent b4cbcb0395
commit 91d916fb74
44 files changed
+4984 -32

No files matched your search

+52
View File
@@ -0,0 +1,52 @@
# QA: Entry list of a custom content type (SPA slice 6)
Runner: Vitest + Vue Test Utils (`task test:web`). No Go test: both endpoints and /v1/user/lookup already exist and the backend does not change. Kit: reuse `views/__tests__/schemaTestKit.ts` (add GET /v1/content-type/{key}, GET .../entry with page/size/sort, and /v1/user/lookup handlers; Events with the 7 fields and 7/37 entries of SchemaFrame.dc.html). Pin the time zone; never freeze the system date.
## Automated
1. Composable (useContentTypes.spec): `getContentType(key)`; `listEntries(key, {page,size,sort})` builds the query, returns {items,total,page,size,sort}, passes status and plain text through (403, 404, 500), NetworkError passes through. `lookupUsers(ids)` sends the distinct ids once and returns display names.
2. URL parsing (it.each): invalid/missing page, size, sort fall back to 1, 25, updated_at:desc and are replaced in the URL; defaults omitted; size and sort change reset page; the URL changes on success only.
3. State 1 Populated: title, subtitle "events · 7 fields", toolbar, headers, first row values, "7 entries", no pager; singular "1 entry".
4. State 2 Paginated (size=5, total 37): "Showing 1–5 of 37", "5 per page", pager; mobile "Page 1 of 8"; Next sends page=2 and updates the URL on success.
5. Reuse of Articles pager behaviours (it.each): last page, loading page (aria-busy, disabled controls, "Loading page 2…"), error keeps rows with Retry, out-of-range redirect with notice, total 0 on out-of-range shows empty.
6. Cells (it.each per field type): truncation, rich text first line, number alignment, Yes/No icon, date without shift, Choice tag, empty "–"; Created and Updated formats.
7. Updated by: one lookup with distinct ids after the entries; "–" before it resolves, on failure and for an unknown id.
8. State 3 Columns: popover on desktop (Reset only), sheet on mobile (Reset and Done); "4 of 6", sentence, rows with icon, label and key; the Created/updated row disabled; live column change with no request; cap at 6 with message and disabled rows; Esc/outside/Done close and return focus.
9. Column persistence (it.each from the feature table): writes, Reset removes, unknown keys dropped, more than 6 truncated, invalid JSON, per-type keys, throwing/undefined localStorage, load never writes.
10. Sort: the three options and the direction button (it.each), accessible names, URL and request, header arrow, sort kept across paging, invalid URL values replaced.
11. State 4: with 14 types the sidebar shows 5 + selected + "Show all 14"; mobile drawer collapsed row "All content types 14" (sidebar logic itself stays in ContentNav.spec).
12. State 5 Loading: skeleton rows (8 / 6), aria-busy, "New entry" disabled.
13. State 6 Empty: copy, "New entry" navigates to /content/events/new, no pager.
14. State 7 Error (500 and network): copy, "Try again" sends the failed request again; type-load failure variant.
15. State 8 Forbidden (entries 403, type 403): copy, name fallback to the key, "Go to Home" goes to /home.
16. State 9 Not found (type 404, entries 404): copy, breadcrumb "Content", no current sidebar row; deleted-type scenario.
17. Navigation: New entry, first-cell links, mobile card link, "Edit content type" to /schema/events; hidden for a non-owner/admin and on mobile; mobile floating button hidden in loading and error.
18. 401 redirect; stale answer dropped after a sort change; switching type resets page/size/sort and columns.
19. Accessibility: caption text per sort, accessible names, keyboard on the picker and the select.
20. French: every new string has an entry (i18n completeness spec passes).
## Visual (task visual:check -- content)
Register the group "5 Content and entry list" in `extraSources` if not registered. Every capture within 10 %, desktop and mobile, light and dark. Nothing is clock-sensitive. Mock every /v1 call including the sidebar's GET /v1/content-type and /v1/user/lookup.
| Board state | Setup in web/visual/states.ts | Board sample data |
|---|---|---|
| 1 Populated | /content/events, 7 entries (ROWS of SchemaFrame.dc.html), owner session | "Lime plaster workshop", "Marta Alves", "events · 7 fields", "7 entries" |
| 2 Paginated, 5 per page | /content/events?size=5, total 37, 5 items | "Showing 1–5 of 37", "Page 1 of 8" (mobile) |
| 3 Columns menu | /content/events, step: click "Columns" | "4 of 6", Title, Starts on, Ends on, Online checked |
| 4 Many types | /content/events with 14 types (CT list of the board); mobile: open the drawer and expand the group | "Frequently asked questions and answers", "Show all 14" |
| 5 Loading | entries `hang: true` | skeleton rows |
| 6 Empty | entries total 0 | "No entries yet" |
| 7 Error | entries 500 | "Couldn't load entries" |
| 8 Forbidden | entries 403 | "You don't have access to Events" |
| 9 Type not found | /content/unknown, type 404 content_type_not_found | "Content type not found" |
Board caveats to report, never hide: the mobile card shows a Category tag that the default 4 columns do not include; the desktop picker is drawn without Done; the mobile frames hide "Edit content type", so mobile has no path to the builder (design follow-up).
## Manual
- Real backend, owner and a non-owner/admin if one exists: list a type with 37 entries; sort, page, change size; reload keeps the URL state.
- Columns: choose 6, reload, switch type and back; clear localStorage and see the default.
- Click "New entry" and a row: the 404 page is expected until the editor slice; Back returns to the list with its URL.
- Keyboard: tab order toolbar (select, direction, Columns, Edit), table links, pager; the picker and the sheet focus order; hover tooltip on truncated text.
- Delete a type in another tab, then reload /content/<key>: not found page.