# Phase 37: Admin — Global Item Management - Context

**Gathered:** 2026-04-19
**Status:** Ready for planning

<domain>
## Phase Boundary

Build 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.

</domain>

<decisions>
## Implementation Decisions

### 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)

</decisions>

<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 here
- `src/server/routes/global-items.ts` — existing GET/POST endpoints (public); admin DELETE will go through `/api/admin/items` route

### Admin Infrastructure (Phase 36)
- `src/server/routes/admin.ts` — admin route stub; extend with item sub-routes here
- `src/server/middleware/auth.ts` — `requireAdmin` middleware already in place
- `src/client/routes/admin.tsx` — admin shell layout with `<Outlet />`; "Items" nav item needs to be enabled (remove `cursor-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) and `admin/items/$itemId.tsx` (edit)

### Database Schema
- `src/db/schema.ts` — `globalItems`, `manufacturers`, `tags`, `globalItemTags` tables

### Existing Reusable Hooks / Patterns
- `src/client/hooks/` — React Query hooks pattern; add `useAdminGlobalItems` (infinite query) and `useAdminGlobalItem`
- `src/client/lib/api.ts` — `apiGet`, `apiDelete`, `apiPut` fetch wrappers
- `src/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
- `getGlobalItemWithOwnerCount` service function: already returns `ownerCount` — reuse for delete confirmation and edit page header
- `upsertGlobalItem` service: handles create and update via `onConflictDoUpdate`; admin edit will call this directly (PUT /api/admin/items/:id)
- `ImageUpload` component (`src/client/components/`) — reuse for image field on edit page
- `useFormatters()` 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.invalidateQueries` with 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 styling
- `src/server/index.ts`: Register `adminItemRoutes` under `/api/admin/items`
- `src/server/routes/admin.ts`: Mount item sub-router

</code_context>

<specifics>
## 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

</specifics>

<deferred>
## Deferred Ideas

- 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

</deferred>

---

*Phase: 37-admin-global-item-management*
*Context gathered: 2026-04-19*
