feat: media page T1

This commit is contained in:
fsylvain committed 2026-10-01 01:36:35 +02:00
1 parent 67d456baf6
commit 008b0dfbb9
46 files changed
+5742 -204

No files matched your search

+12 -25
View File
@@ -14,11 +14,9 @@ Feature: Media page, part 1: browse and manage
breadcrumb reads "Media" (mobile title "Media")
And this slice is SPA only: the API from slice A already exists and is used unchanged, and
no Go code, use case or repository changes
And this slice covers browse and manage only. Upload, multi-select, the bulk bar, the
Duplicates tab content, duplicate merge, drag-and-drop and the rate-limited state are slice
B2, and this slice renders none of their UI
And the visual states of slice B2 carry skip 'pending' in web/visual/states.ts, print PENDING
and are never counted as passing
And upload, multi-select, the bulk bar, the Duplicates tab content, duplicate merge,
drag-and-drop and the rate-limited state are specified in
features/media_upload_selection_duplicates.feature
And useMedia is a composable over useApi. It exposes the list state (items, counts, loading,
error), the request functions for list, usage, rename and delete, and the copy helper, and
it maps ApiError and NetworkError to the states below
@@ -30,9 +28,8 @@ Feature: Media page, part 1: browse and manage
means grid); a value outside these sets is dropped and the default applies; the names
mirror the API's own q, filter and sort parameters
And the header shows the title "Media", the subtitle "Every image you've uploaded, ready to
use in any article." and a primary "Upload" button (icon upload) on the right; in this slice
that button is rendered disabled and does nothing, and on mobile it is the full-width 48 px
button pinned to the bottom, disabled the same way
use in any article." and a primary "Upload" button (icon upload) on the right; on mobile it is
the full-width 48 px button pinned to the bottom
And the toolbar holds the tabs "All", "Unused" and "Duplicates" with a count each, then the
search field (placeholder "Search by filename"), the sort menu (options "Newest", "Oldest",
"Name", "Largest", sent as sort=newest, oldest, name, largest) and the grid/list toggle
@@ -40,10 +37,7 @@ Feature: Media page, part 1: browse and manage
And the tab counts come from the response's counts object (all, unused, duplicates). The
Duplicates count is a number of groups, drawn as an info-tinted badge only while it is
above 0. The All and Unused counts are drawn as plain muted mono numbers, as in the board
And the Duplicates tab is rendered and disabled in this slice (aria-disabled, not
focusable, no navigation), so it keeps its place and its count badge but leads nowhere;
slice B2 enables it and replaces the content area with the groups view. A ?filter=duplicates
in the URL is treated as an unknown value and falls back to All
And the Duplicates tab selects the groups view and ?filter=duplicates is a valid value
And the grid is 5 columns on desktop and 2 on mobile, with a 16 px gap (10 px on mobile),
and each card is one button holding MediaThumb (4:3, a checkerboard behind PNG and SVG),
the filename (medium weight, ellipsis), "size · dimensions" and the MediaBadges; an SVG's
@@ -65,9 +59,8 @@ Feature: Media page, part 1: browse and manage
trailing chevron-right); in the table the "Used in" cell is an underlined count that
opens the detail panel, and reads the Unused badge when used_in is 0
And the checkbox column of the table (36 px), the 44 px checkbox column of the card list and
the "Select all" row above the grid keep their space in the layout but show no checkbox
and no "Select all" text in this slice (the row shows only "12 images"); slice B2 fills
them
the "Select all" row above the grid are specified in
features/media_upload_selection_duplicates.feature
And the detail panel is a USlideover: 460 px on the right on desktop (a close button x),
full screen on mobile with an arrow-left back button; its title is the filename; the body
holds the 16:10 preview (4:3 on mobile), the actions "Copy URL" (icon link), "Rename"
@@ -107,7 +100,7 @@ Feature: Media page, part 1: browse and manage
Duplicates count drawn as an info-tinted badge
And the grid shows the 12 images in exactly the fixture's order, 5 per row on desktop and 2
on mobile, each card with its thumbnail, filename, "size · dimensions" and badges
And the "Select all" row above the grid shows only "12 images"
And the "Select all" row above the grid reads "Select all" and "12 images"
And the toolbar shows "Search by filename", the sort "Newest" and the grid toggle selected
Scenario Outline: Each card's badges and accessible name follow the image
@@ -175,11 +168,6 @@ Feature: Media page, part 1: browse and manage
Then the URL gains sort=largest and the list is requested again
And selecting "All" removes filter from the URL, "Newest" removes sort, and the grid icon removes view
Scenario: The disabled Duplicates tab does nothing
Given the Default state has loaded
When the user clicks or focuses the "Duplicates 2" tab
Then the tab does not become selected, the URL does not change and no request is sent
Scenario: Loading, skeleton cards after 150 ms
Given GET /v1/media/ has not yet resolved
When the user navigates to /media
@@ -195,7 +183,7 @@ Feature: Media page, part 1: browse and manage
Given GET /v1/media/ resolves 200 with zero items and counts { all: 0, unused: 0, duplicates: 0 } and no q
When the user navigates to /media
Then the toolbar and the tabs are hidden
And one panel shows the images icon, title "No images yet", text "Upload your first image. You can use it in any article.", a primary "Upload an image" button (disabled in this slice), and under it "PNG, JPEG, WEBP, SVG · max 10 MB each"
And one panel shows the images icon, title "No images yet", text "Upload your first image. You can use it in any article.", a primary "Upload an image" button, and under it "PNG, JPEG, WEBP, SVG · max 10 MB each"
Scenario: No results, search and filter
Given the Default state has loaded
@@ -216,8 +204,7 @@ Feature: Media page, part 1: browse and manage
When the user presses Enter on a card
Then the detail panel opens for that image
When the user presses Space on a card
Then nothing happens and no panel opens (Space is selection, which is slice B2)
And no checkbox is shown on hover or on focus in this slice
Then the card's selection toggles and no panel opens
Scenario: Detail panel, in use
Given the Default state has loaded and GET /v1/media/1/usage resolves 200 with [{ id: 10, title: "How we price a bathroom renovation", published: true }, { id: 11, title: "Winter checklist for timber windows", published: false }]
@@ -239,7 +226,7 @@ Feature: Media page, part 1: browse and manage
Given the Default state has loaded, GET /v1/media/?hash=<sha256 of id 12> resolves 200 with ids 12, 5 and 6, and GET /v1/media/12/usage resolves 200
When the user activates the "floorboards.jpg" card
Then an info alert reads "Identical to 2 other images" over "floorboards-copy.jpg and IMG_2208.jpg have the same content." with an action "Review duplicates" (icon arrow-right)
And the action is rendered disabled in this slice, because the Duplicates view ships in slice B2
And the action is enabled and selects the Duplicates tab
Scenario: The Used in count in list view opens the panel
Given the Default state has loaded with ?view=list at md width or more