feat(web): full-width sortable/filterable objects table with URL state (#44)

Replace the narrow ObjectList with a full-width ObjectsTable whose state
(sort/order/q/visibility/limit/offset) lives entirely in the URL via
useSearchParams. Sortable headers toggle sort+dir with aria-sort, a
debounced quick-filter and visibility chips mirror the search-panel
pattern, and a pagination footer offers prev/next + page-size select.
Rows deep-link to /objects/:id preserving the query string.

useObjectsPage now takes an ObjectListParams object (sort/order/
visibility/q) with keepPreviousData. ObjectsPage renders the table as
the full-width landing view, surfacing the nested <Outlet/> detail as a
simple right-side panel only when a :id child route is active (Phase 3
makes this responsive). object-list.tsx and its test are removed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-06 23:34:13 +02:00
parent 98c00d3732
commit 49f694d1fb
10 changed files with 553 additions and 169 deletions
+1 -1
View File
@@ -2,7 +2,7 @@
"app": { "name": "Samling" },
"nav": { "objects": "Föremål", "vocabularies": "Vokabulär", "authorities": "Auktoriteter", "fields": "Fält", "search": "Sök" },
"auth": { "email": "E-post", "password": "Lösenord", "signIn": "Logga in", "signOut": "Logga ut", "invalid": "Fel e-post eller lösenord", "networkError": "Kunde inte nå servern" },
"objects": { "title": "Föremål", "empty": "Inga föremål ännu", "loadError": "Kunde inte ladda föremål", "selectPrompt": "Välj ett föremål för att se detaljer", "notFound": "Föremålet hittades inte", "prev": "Föregående", "next": "Nästa", "of": "av", "new": "Nytt föremål" },
"objects": { "title": "Föremål", "empty": "Inga föremål ännu", "loadError": "Kunde inte ladda föremål", "selectPrompt": "Välj ett föremål för att se detaljer", "notFound": "Föremålet hittades inte", "prev": "Föregående", "next": "Nästa", "of": "av", "new": "Nytt föremål", "filter": "Filtrera föremål…", "pageSize": "Per sida", "columns": { "number": "Föremålsnr", "name": "Namn", "visibility": "Synlighet", "location": "Plats", "count": "Antal", "updated": "Uppdaterad" } },
"fieldsLabels": { "objectNumber": "Föremålsnummer", "objectName": "Namn", "count": "Antal föremål", "briefDescription": "Kort beskrivning", "currentLocation": "Nuvarande plats", "currentOwner": "Nuvarande ägare", "recorder": "Registrerad av", "recordingDate": "Registreringsdatum", "visibility": "Synlighet", "flexible": "Katalogfält" },
"visibility": { "draft": "Utkast", "internal": "Intern", "public": "Publik" },
"form": { "selectPlaceholder": "— välj —", "create": "Skapa föremål", "save": "Spara", "cancel": "Avbryt", "visibility": "Synlighet", "draft": "Utkast", "internal": "Intern", "required": "Fältet är obligatoriskt", "rejected": "Servern avvisade ändringarna — kontrollera obligatoriska och refererade fält", "fieldRejected": "Fältet \"{{field}}\" avvisades — kontrollera värdet", "flexibleHeading": "Katalogfält" },