--- phase: 37 slug: admin-global-item-management status: approved shadcn_initialized: false preset: none created: 2026-04-19 reviewed_at: 2026-04-19 --- # Phase 37 — UI Design Contract > Visual and interaction contract for Admin — Global Item Management. Generated from CONTEXT.md decisions, RESEARCH.md findings, and codebase pattern analysis. --- ## Design System | Property | Value | |----------|-------| | Tool | none — Tailwind CSS v4 utility-first | | Preset | not applicable | | Component library | none (custom components) | | Icon library | LucideIcon from `src/client/lib/iconData` | | Font | System default (Inter-like sans-serif via browser) | No `components.json` detected. No shadcn. All styling is plain Tailwind utility classes matching the app's existing light/airy aesthetic. --- ## Spacing Scale Standard 8-point scale from existing app patterns: | Token | Value | Usage | |-------|-------|-------| | xs | 4px (`gap-1`, `p-1`) | Icon gaps, inline chip gaps | | sm | 8px (`gap-2`, `p-2`) | Compact table cell padding, tag chips | | md | 16px (`gap-4`, `p-4`) | Default form field spacing, sidebar padding | | lg | 24px (`gap-6`, `p-6`) | Section padding, edit page padding | | xl | 32px (`gap-8`, `p-8`) | Page-level layout gaps | | 2xl | 48px | Major section breaks (used sparingly) | | 3xl | 64px | Not used in admin panel | Exceptions: - Table row height: `py-3` (12px vertical) + `px-4` (16px horizontal) for dense data rows - Sidebar nav items: `px-3 py-2` (12px vertical) — matches Phase 36 admin shell --- ## Typography Matches existing app type scale. No new sizes introduced. | Role | Size | Weight | Line Height | Tailwind | |------|------|--------|-------------|---------| | Body / table cell | 14px | 400 | 1.5 | `text-sm` | | Label / column header | 12px | 600 | 1.4 | `text-xs font-semibold` | | Form field label | 14px | 600 | 1.4 | `text-sm font-semibold` | | Page heading | 18px | 600 | 1.3 | `text-lg font-semibold` | Weights used: 400 (regular) + 600 (semibold). No medium (500), no bold (700). --- ## Color 60/30/10 rule — matches Phase 36 admin shell exactly: | Role | Value | Tailwind | Usage | |------|-------|---------|-------| | Dominant (60%) | #ffffff | `bg-white` | Page backgrounds, table rows, edit form | | Secondary (30%) | #f9fafb | `bg-gray-50` | Main content area, input backgrounds | | Border / divider | #f3f4f6 | `border-gray-100` | Table row dividers, sidebar border | | Text primary | #111827 | `text-gray-900` | Item names, column data | | Text secondary | #4b5563 | `text-gray-600` | Labels, form hints | | Text muted | #9ca3af | `text-gray-400` | Column headers, disabled states | | Accent (10%) | #2563eb | `text-blue-600`, `bg-blue-50` | Save button, active nav link indicator | | Destructive | #dc2626 | `bg-red-600 hover:bg-red-700` | Delete button ONLY | Accent reserved for: - Save/Update primary button - Active sidebar nav link highlight (left border or background) Destructive reserved for: - Delete button in edit page - Confirm delete button inside the delete dialog --- ## Component Inventory ### Admin Items List (`/admin/items`) **Layout:** ``` ┌─────────────────────────────────────────────────┐ │ Heading: "Catalog Items" [search input] │ │ Tag filter chips │ │ "1,247 items" count label │ ├──────┬────────────┬─────┬───────┬───────┬───────┤ │ Brand+Model │ Category │ Wt │ Price │ Tags │ Owners│ ├──────┴────────────┴─────┴───────┴───────┴───────┤ │ row row row ... (50 per page) │ │ [sentinel div — triggers next page] │ └─────────────────────────────────────────────────┘ ``` **Table element specs:** - Wrapper: `w-full overflow-hidden rounded-xl border border-gray-100 bg-white` - ``: `w-full text-sm` - ``: `bg-gray-50 border-b border-gray-100` - Column header cells: `px-4 py-3 text-left text-xs font-semibold text-gray-400 uppercase tracking-wide` - `` rows: `border-b border-gray-50 hover:bg-gray-50 cursor-pointer transition-colors` - Body cells: `px-4 py-3 text-sm text-gray-700` - Brand+Model cell: bold brand (`font-medium text-gray-900`) + muted model (`text-gray-500`) **Tags column:** - ≤ 2 tags: render as inline chips — `text-xs bg-gray-100 text-gray-500 px-2 py-0.5 rounded-full` - > 2 tags: render count badge — `text-xs bg-gray-100 text-gray-500 px-2 py-0.5 rounded-full` displaying "+N" **Owner count column:** - Numeric, right-aligned, `text-gray-500` - 0 owners: `text-gray-300` **Search input:** - `rounded-lg border border-gray-200 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500/20 focus:border-blue-300` - Placeholder: "Search catalog..." - Width: `w-64` **Tag filter:** - Row of clickable chips below search bar - Selected: `bg-blue-50 text-blue-600 border border-blue-200` - Unselected: `bg-gray-100 text-gray-600` - Multi-select allowed (matches existing CatalogSearchOverlay pattern) **Infinite scroll sentinel:** - `
` at bottom of table - IntersectionObserver triggers `fetchNextPage()` when sentinel enters viewport - Loading indicator: `
Loading...
` ### Admin Items Edit Page (`/admin/items/$itemId`) **Layout:** ``` ← Items (back link) Salsa Woodsmoke 700 (page heading: brand + model) 3 users in collection (owner count subtext) ┌──────────────────────────────────────────────────┐ │ [Image preview — GearImage] │ │ [Image URL input] │ ├──────────────────────────────────────────────────┤ │ Brand (Manufacturer) [dropdown] │ │ Model [text input] │ │ Category [text input] │ ├──────────────────────────────────────────────────┤ │ Weight (g) [number input] │ │ Price (€) [number input] │ ├──────────────────────────────────────────────────┤ │ Tags [chip input] │ │ Description [textarea] │ │ Source URL [url input] │ │ Image Credit [text input] │ │ Image Source URL [url input] │ ├──────────────────────────────────────────────────┤ │ [Save Changes] [Delete Item]│ └──────────────────────────────────────────────────┘ ``` **Form wrapper:** `max-w-2xl mx-auto` (consistent with catalog detail page width) **Field groups:** separated by `border-t border-gray-100 pt-6 mt-6` **Input styles (all fields):** - Text/number/url: `w-full rounded-lg border border-gray-200 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500/20 focus:border-blue-300` - Textarea: same + `min-h-[80px] resize-y` - Manufacturer dropdown: `