managarten/apps/mana/apps
Till JS 5a49bcbf02 feat(wardrobe): garments UI — /wardrobe + /wardrobe/garment/[id] (M2)
M2 of docs/plans/wardrobe-module.md — the first interactive surface on
top of the M1 data layer. Users can now upload photos, browse their
garment grid filtered by category, and edit/archive/delete individual
items. Outfits (M3) and Try-On (M4) are still placeholders.

Route:
- /wardrobe — grid view with active-space badge in the intro card
  (identical pattern to /profile/me-images since the pool IS per-
  space). Category tabs across the top: "Alle" + eleven categories
  with live counts. Dropping files while a category tab is active
  creates garments with that category preselected; dropping on
  "Alle" defaults to `other` and the user edits on the detail page.
- /wardrobe/garment/[id] — detail view. Renders the primary photo
  + metadata card; a pencil toggles into GarmentForm for inline
  edit. Three actions: "Heute getragen" (bumps wearCount + stamps
  lastWornAt, prominent primary button), Archive, and Delete with
  confirm. The route wraps DetailGarmentView in `{#key id}` so
  navigating between different garments cleanly remounts the
  liveQuery + form state.

Components:
- CategoryTabs — horizontal pill row with per-category count
  badges. Stays compact on mobile via overflow-x-auto.
- GarmentCard — tile with primary photo + name + brand + wear-
  count hint; click navigates to detail.
- GarmentForm — inline edit sheet (name, category, brand, color,
  size, material, tags comma-separated, notes, price+currency).
  Comma→array for tags because that's how most users think about
  them; the store normalizes on save.
- GridView — orchestrates queries, filter tabs, drop zone (reuses
  MeImageUploadZone from profile since it's already generic about
  what "files" mean), and the empty states (no garments at all vs.
  no garments in this category).

Small conveniences:
- api/upload.ts wraps the M1 POST /api/v1/wardrobe/garments/upload
  endpoint with fetchWithAuth; same shape as profile's me-images
  client (mediaId/storagePath/publicUrl/thumbnailUrl).
- api/media-url.ts — tiny mediaId → URL resolver using the same
  inline PUBLIC_MANA_MEDIA_URL pattern wallpaper and invoices/
  pdf/logo already use. Worth a shared helper later but premature
  while three call sites disagree on which variant to default to.
- constants.ts — CATEGORY_ORDER / CATEGORY_LABELS plus
  OCCASION_LABELS and SEASON_LABELS for M3 to pick up.

Svelte 5 note: GarmentForm's `$state(garment.xxx)` initializers
trip the state_referenced_locally check, but the intent is
correct — the parent uses `{#key id}` to remount on navigation,
so the captures are a feature, not a bug. Suppressed per-line
with `svelte-ignore` and a comment pointing at the remount
mechanism.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 18:37:38 +02:00
..
landing docs: add Apr 16 devlog, remove duplicate devlogs, update MODULE_REGISTRY 2026-04-17 15:27:00 +02:00
web feat(wardrobe): garments UI — /wardrobe + /wardrobe/garment/[id] (M2) 2026-04-23 18:37:38 +02:00