Skip to content

Add drawers (closet categorization) with at-scale pagination and picker search - #121

Open
guiu-rocafort wants to merge 5 commits into
Lazztech:mainfrom
guiu-rocafort:feature/drawers-categorization
Open

Add drawers (closet categorization) with at-scale pagination and picker search#121
guiu-rocafort wants to merge 5 commits into
Lazztech:mainfrom
guiu-rocafort:feature/drawers-categorization

Conversation

@guiu-rocafort

@guiu-rocafort guiu-rocafort commented Jul 23, 2026

Copy link
Copy Markdown

Fixes #36

Summary

  • Adds drawers: named garment groupings, with create/edit/delete and wardrobe filtering by drawer
  • Fixes UX and performance regressions that surfaced when testing with hundreds to a thousand garments:
    • The drawer garment picker (new + edit) is now server-driven, paginated, and searchable instead of rendering every garment as an unbounded checkbox grid (was 7k+ DOM nodes / 1.5MB HTML at 1000 garments)
    • /wardrobe and drawer-detail pages are paginated (48/page) instead of rendering the full catalog in one response (was 8k+ DOM nodes / 1.13MB HTML at 1000 garments, unbounded growth)
    • Drawer index cards cap thumbnail previews to 6 + an overflow badge instead of rendering every garment in the drawer

Testing

  • Seeded a local database with 10/100/1000 garments and measured HTTP payload size and DOM node counts before/after
  • Verified pagination, search/filter, thumbnail caps, and selection persistence in the picker at 1000-garment scale, at both mobile (390x844) and desktop (1440x900) viewports, with screenshots
  • Ran a full regression pass on garment/drawer create/edit and wardrobe filtering
  • npm test: 18/18 passing; npm run lint: 0 errors (pre-existing warnings only)

Notes for reviewers

  • No new auth/permission logic -- all changed queries remain scoped to the existing userId-scoped read paths
  • Garments in local testing were seeded without photos to isolate layout/DOM scaling from image weight; real-world payload with photos attached will be higher than the numbers above

This change was written with AI assistance.

guiu-rocafort and others added 5 commits July 22, 2026 23:54
Implements the "shelves"-style categorization requested in
Lazztech#36 — users can group garments into named,
manually-curated "drawers" and filter the wardrobe grid by drawer via
the existing filter modal, in addition to a dedicated /drawers section
for creating/editing/deleting drawers.

- New Drawer entity (many-to-many with Garment) plus sqlite/postgres
  migrations
- DrawerService/DrawerController with standard CRUD routes and views
  mirroring the existing Outfit feature's structure
- Wardrobe search gains a drawerId filter, wired into the filter modal
- Fixed a MikroORM Collection + Handlebars interaction: Collection#length
  is a prototype getter that Handlebars' proto-access guard silently
  blocks, so drawer views pass plain arrays (via getItems()) instead of
  raw Collections

Co-Authored-By: Paperclip <noreply@paperclip.ing>
Add gap-2 to the flex row so the heading and "View in wardrobe" link
never run together when their combined text width nears the max-w-lg
container. Also demote the link's visual weight (text-xs, muted,
shrink-0) so it reads as secondary to the h2 rather than competing
with it.

Fixes LIB-7.
LIB-5's drawers feature broke down at 100-1000 garments (LIB-8): the
picker had no search, drawer cards rendered every thumbnail uncapped,
and /wardrobe had no pagination. At 1000 garments this was 1.13MB of
HTML / 8196 DOM nodes on /wardrobe alone.

- GarmentService.findAllPaginated (page/limit, 48/page default) backs
  /wardrobe and /drawers/:id, keeping DOM size roughly constant
  regardless of catalog size (1000 garments: 8196 -> 584 DOM nodes).
- Drawer index cards cap thumbnails to 6 + "+N more" badge instead of
  rendering every garment in the drawer.
- Drawer new/edit picker gets a client-side search + category filter
  (hyperscript) scoped to the checkbox grid, so finding one garment
  among hundreds doesn't require manual scrolling.
- lazy-load garment photos below the fold.

Verified locally with 1000 seeded garments (Node 22 sqlite) via
Playwright at 390x844 and 1440x900: search narrows the picker grid,
category filter and checkbox selection survive re-filtering, and DOM
node counts stay bounded across page 1 -> last page.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
The category <select> had no width constraint in the shared flex row,
so it took its intrinsic size (~300px) and squeezed #picker-search
(flex-1) down to ~48px on mobile viewports, clipping the search text.
Constrain the select to a fixed width (w-28/sm:w-40, shrink-0) and let
the search input take the remaining space (flex-1 min-w-0).
…scale

LIB-10's UX review of the previous picker fix (a8dfaa8/9bd91c9) found
the client-side hidden-class filter still rendered every garment
server-side regardless of search/category — 1.6MB HTML / 1001 DOM
nodes at N=1000, worse than the pre-LIB-8 baseline.

- New GET /drawers/picker fragment endpoint backs the picker grid:
  keyword/category filtering and pagination now happen server-side via
  GarmentService.findAllPaginated (already used by /wardrobe and
  /drawers/:id), returned as an htmx-swapped partial
  (views/partials/garment_picker_grid.hbs) capped at 48 items/page.
- Selection state is tracked via hidden inputs synced by a small script
  so checking a garment, then searching/paginating it out of view,
  still submits it — checkboxes in the grid are no longer the
  form-submitting elements.
- drawer.controller.ts's newForm/editForm now paginate instead of
  loading the full catalog with findAll().

Verified against a 1000-garment seeded sqlite db: /drawers/new dropped
from 1.6MB/1001 DOM nodes to ~92KB/455 nodes; search "Denim" narrows
21 pages to 2 via real server-side filtering (confirmed via curl and
Playwright at 390x844/1440x900); create/edit/resave round-trips
correctly persist and drop selected garments.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
@gianlazz

Copy link
Copy Markdown
Member

Thank you for the contribution! However just to set expectations, this is a very sizable PR and will take us some time to review it.

Will keep you posted.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature Request: "drawers" in the closet, or some kind of categorizing

2 participants