- FilterPill: drop the inline value text from the active state. Pills now stay the same width whether or not a filter is set; the popover is the canonical place to read the value, and the title attribute surfaces it on hover. - TopBar: remove the search input — search lives in the filter bar now. - FilterBar: add a search input on the left, with the pill cluster centered between it and a flex-shrink-0 Clear-all on the right. - LeftSidebar / HeapsPanel: count badges use a fixed-width slot (h-5 min-w-[24px], tabular-nums) so counts line up in the same visual column across rows. Empty rows reserve the slot. - LeftSidebar: pull section counts (All Photos, Rated, Duplicates, Discarded) from a new useLibraryStatsQuery hook backed by the expanded /library/stats endpoint. Tags count was already wired. - backend/library: stats endpoint returns per-section counts that match the filter the sidebar applies on click. - Stats invalidation hooked into the standard photo-mutation paths. - RightSidebar header: h-12 to match TopBar height. - Timeline sticky date overlay: only show once the natural in-grid header has scrolled OUT of the viewport. Avoids the duplicate-label flash when both labels would be visible. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
19 lines
641 B
TypeScript
19 lines
641 B
TypeScript
import { useQuery } from '@tanstack/react-query'
|
|
import { library, type LibraryStats } from '../services/api'
|
|
|
|
export const LIBRARY_STATS_QUERY_KEY = ['library', 'stats'] as const
|
|
|
|
/**
|
|
* Per-section counts for the LeftSidebar badges (All Photos, Rated,
|
|
* Duplicates, Discarded). Cached briefly so navigating around doesn't
|
|
* re-fetch on every click; invalidated on photo mutations through the
|
|
* standard ['photos'] invalidation in the mutation onSuccess paths.
|
|
*/
|
|
export function useLibraryStatsQuery() {
|
|
return useQuery<LibraryStats>({
|
|
queryKey: LIBRARY_STATS_QUERY_KEY,
|
|
queryFn: library.stats,
|
|
staleTime: 30_000,
|
|
})
|
|
}
|