import { useState, useMemo, useCallback } from 'react' import { Star, ArrowLeft, Loader2 } from 'lucide-react' import clsx from 'clsx' import { photos as photosApi } from '../../services/api' import { useFilterStore } from '../../store/filterStore' import { usePhotosQuery } from '../../hooks/usePhotosQuery' import { useCardGridNav } from '../../hooks/useCardGridNav' import { Timeline } from '../timeline/Timeline' import type { Photo } from '../../types/photo' interface RatingGroup { rating: number label: string count: number representative: Photo | null } /** * Rated view — two states: * 1. Grid of rating-level cards (default) — arrow keys + Enter to browse * 2. Detail view showing a rating level's photos in the full Timeline — Esc to go back */ export function RatedView() { const { data: allPhotos = [], isLoading } = usePhotosQuery() const setRatingMin = useFilterStore((s) => s.setRatingMin) const setRatingMax = useFilterStore((s) => s.setRatingMax) const [selectedGroup, setSelectedGroup] = useState(null) const groups = useMemo(() => { const buckets = new Map() for (const photo of allPhotos) { if (photo.rating > 0) { const arr = buckets.get(photo.rating) ?? [] arr.push(photo) buckets.set(photo.rating, arr) } } // Highest rating first const result: RatingGroup[] = [] for (let r = 5; r >= 1; r--) { const photos = buckets.get(r) ?? [] if (photos.length === 0) continue result.push({ rating: r, label: '★'.repeat(r), count: photos.length, representative: photos[0], }) } return result }, [allPhotos]) const enterDetail = useCallback( (group: RatingGroup) => { setRatingMin(group.rating) setRatingMax(group.rating) setSelectedGroup(group) }, [setRatingMin, setRatingMax] ) const exitDetail = useCallback(() => { // Restore the section preset: ratingMin=1 (all rated), no max setRatingMin(1) setRatingMax(0) setSelectedGroup(null) }, [setRatingMin, setRatingMax]) const { activeIndex, gridRef } = useCardGridNav({ items: groups, inDetail: selectedGroup !== null, onEnter: enterDetail, onExit: exitDetail, }) if (selectedGroup) { return (

{selectedGroup.label}

) } if (isLoading) { return (
Loading ratings...
) } if (groups.length === 0) { return (

No rated photos yet

Rate photos with 1–5 stars and they will appear here grouped by rating.

) } return (
{groups.length} rating {groups.length === 1 ? 'level' : 'levels'}
{groups.map((group, i) => (
enterDetail(group)} >
{group.representative ? ( {group.label} ) : (
)} {group.count}

{group.label}

))}
) }