From a55839d9a216571bad6b26fe25fc6b991cf0ce54 Mon Sep 17 00:00:00 2001 From: dtoro Date: Wed, 8 Apr 2026 20:22:23 +0200 Subject: [PATCH] =?UTF-8?q?fix:=20more=20audit=20findings=20=E2=80=94=20pe?= =?UTF-8?q?rf,=20types,=20and=20a11y=20polish?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - backend/photos: collapse the per-tag subquery loop in the tag filter into a single GROUP BY ... HAVING COUNT(DISTINCT) = N subquery so the cost is independent of how many tags the user is filtering on. - useFilterUrlSync: type the parseUrl return value as Partial & { currentSection?: string } so the section field doesn't need an (out as any) cast. - Timeline sticky header: bump opacity, padding, and border so it reads more clearly against the underlying grid. - FilterPill clear: convert the nested + ) : ( )} diff --git a/frontend/src/components/layout/RightSidebar.tsx b/frontend/src/components/layout/RightSidebar.tsx index 7107737..7273ace 100644 --- a/frontend/src/components/layout/RightSidebar.tsx +++ b/frontend/src/components/layout/RightSidebar.tsx @@ -97,6 +97,7 @@ export function RightSidebar() { onClick={clearSelection} className="rounded p-1 text-text-muted hover:bg-surface-2 hover:text-text" title="Clear selection" + aria-label="Clear selection" > diff --git a/frontend/src/components/timeline/Timeline.tsx b/frontend/src/components/timeline/Timeline.tsx index 6a551d2..713a31c 100644 --- a/frontend/src/components/timeline/Timeline.tsx +++ b/frontend/src/components/timeline/Timeline.tsx @@ -422,7 +422,7 @@ export function Timeline() { * positioned children so it isn't affected by translateY transforms. * Updates as the user scrolls past month boundaries. */} {stickyLabel && ( -
+

{stickyLabel}

diff --git a/frontend/src/hooks/useFilterUrlSync.ts b/frontend/src/hooks/useFilterUrlSync.ts index fe27541..51b7109 100644 --- a/frontend/src/hooks/useFilterUrlSync.ts +++ b/frontend/src/hooks/useFilterUrlSync.ts @@ -28,9 +28,14 @@ const ALLOWED_SORT_FIELDS: SortField[] = [ ] const ALLOWED_SORT_ORDERS: SortOrder[] = ['asc', 'desc'] -function parseUrl(): Partial { +// What parseUrl returns: a partial filter state, plus the optional +// section id (which lives on the store but isn't part of FilterState +// itself). The hydrate action accepts this exact shape. +type HydratePayload = Partial & { currentSection?: string } + +function parseUrl(): HydratePayload { const sp = new URLSearchParams(window.location.search) - const out: Partial = {} + const out: HydratePayload = {} const q = sp.get('q') if (q) out.q = q @@ -83,7 +88,7 @@ function parseUrl(): Partial { if (groupBy === 'date' || groupBy === 'tag') out.groupBy = groupBy const section = sp.get('section') - if (section) (out as any).currentSection = section + if (section) out.currentSection = section const sortBy = sp.get('sort') if (sortBy && ALLOWED_SORT_FIELDS.includes(sortBy as SortField)) {