5.9 KiB
5.9 KiB
Phase 37: Admin — Global Item Management - Context
Gathered: 2026-04-19 Status: Ready for planning
## Phase BoundaryBuild the /admin/items list and /admin/items/$id edit page, wired into the Phase 36 admin shell. Admins can browse all global catalog items, open and edit any item's full field set, and delete items with an impact-aware confirmation. No new capabilities beyond browse/edit/delete.
List View
- D-01: Data table layout — dense rows with sortable columns. No card grid. No existing table component; implement with plain Tailwind-styled HTML table.
- D-02: Columns: Brand + Model | Category | Weight | Price | Tags (chip or count) | Owner Count | Actions.
- D-03: Server-side infinite scroll pagination — load next page as admin scrolls down. No explicit next/prev buttons. Consistent with IntersectionObserver scroll detection. 50 items per page is a sensible default.
Edit Workflow
- D-04: Dedicated edit page at
/admin/items/$itemId— full-page form, bookmarkable. Consistent with the item/catalog detail page pattern already in the app. - D-05: Delete lives on the edit page only (not on the list). Admin must open an item to delete it.
Brand / Manufacturer Field
- D-06: Dropdown of existing manufacturers (fetched from
/api/manufacturers). Admin picks from the list — no free-text brand creation in this phase. Prevents accidental duplicate manufacturers.
Delete Confirmation
- D-07: Impact-aware confirmation dialog: show item name + owner count before confirming. Example: "Delete Salsa Woodsmoke 700? 3 users have this in their collection. This cannot be undone." Owner count already available from
getGlobalItemWithOwnerCount.
Claude's Discretion
- Exact column sort order and default sort (brand asc is reasonable)
- Whether tags column shows chips or a count badge when many tags exist
- Form field layout on the edit page (single column vs. two-column grid for compact fields)
- How the infinite scroll trigger is implemented (IntersectionObserver sentinel div)
- Styling of the delete button (destructive red, positioned at bottom of edit form)
<canonical_refs>
Canonical References
Downstream agents MUST read these before planning or implementing.
Existing Service Layer
src/server/services/global-item.service.ts—searchGlobalItems,getGlobalItemWithOwnerCount,upsertGlobalItem; DELETE function needs to be added heresrc/server/routes/global-items.ts— existing GET/POST endpoints (public); admin DELETE will go through/api/admin/itemsroute
Admin Infrastructure (Phase 36)
src/server/routes/admin.ts— admin route stub; extend with item sub-routes heresrc/server/middleware/auth.ts—requireAdminmiddleware already in placesrc/client/routes/admin.tsx— admin shell layout with<Outlet />; "Items" nav item needs to be enabled (removecursor-not-allowed, add active link)
Client Routes
src/client/routes/admin/index.tsx— admin index placeholder; stays as-is- TanStack Router file-based routing: create
admin/items.tsx(list) andadmin/items/$itemId.tsx(edit)
Database Schema
src/db/schema.ts—globalItems,manufacturers,tags,globalItemTagstables
Existing Reusable Hooks / Patterns
src/client/hooks/— React Query hooks pattern; adduseAdminGlobalItems(infinite query) anduseAdminGlobalItemsrc/client/lib/api.ts—apiGet,apiDelete,apiPutfetch wrapperssrc/server/routes/manufacturers.ts(or similar) — GET /api/manufacturers for the brand dropdown
Requirements
.planning/REQUIREMENTS.md— ADMN-02, ADMN-03, ADMN-04
</canonical_refs>
<code_context>
Existing Code Insights
Reusable Assets
getGlobalItemWithOwnerCountservice function: already returnsownerCount— reuse for delete confirmation and edit page headerupsertGlobalItemservice: handles create and update viaonConflictDoUpdate; admin edit will call this directly (PUT /api/admin/items/:id)ImageUploadcomponent (src/client/components/) — reuse for image field on edit pageuseFormatters()hook — reuse for weight/price display in the table- LucideIcon
"package"already in the admin sidebar "Items" entry — just enable it
Established Patterns
- Infinite scroll: Phase 26 discovery feed used cursor pagination + React Query's
useInfiniteQuery— same pattern here - Edit forms: item/catalog detail pages use controlled form state with
apiPut - Destructive confirmation: confirm modals exist in the app (setup delete, etc.) — reuse the same modal pattern
- React Query invalidation: mutations call
queryClient.invalidateQuerieswith relevant query keys
Integration Points
src/client/routes/admin.tsx: Change "Items" sidebar entry from<div cursor-not-allowed>to<Link to="/admin/items">with active stylingsrc/server/index.ts: RegisteradminItemRoutesunder/api/admin/itemssrc/server/routes/admin.ts: Mount item sub-router
</code_context>
## Specific Ideas- Table row click → navigate to
/admin/items/$itemId - Edit page has a back link "← Items" to return to the list
- Delete button at bottom of edit form, styled destructive red, triggers impact-aware modal
- Infinite scroll sentinel: a
<div ref={sentinelRef}>at bottom of table; IntersectionObserver triggers next page fetch
- Manufacturer creation from within the admin panel — admin can only pick existing manufacturers in this phase; creating new ones is out of scope
- Bulk delete / bulk edit from the list — admin-only single-item workflow for now
- Column sorting — mentioned as a potential feature but not in ADMN-02/03/04 success criteria; Claude's discretion whether to include basic sort
Phase: 37-admin-global-item-management Context gathered: 2026-04-19