import { useEffect, useMemo, useState } from 'react' import { useQuery, keepPreviousData } from '@tanstack/react-query' import { MagnifyingGlassIcon, XMarkIcon, ChevronDownIcon, ChevronRightIcon, MapPinIcon, } from '@heroicons/react/24/outline' import { fetchMeetings, type MeetingSort, type MeetingCard } from '../api/meetings' import DecisionCardList from './DecisionCardList' import MeetingThumbnail from './MeetingThumbnail' /** * MeetingCardList — a reusable, meeting-grain browser. * * Backed by `GET /api/meetings`, where meetings are linked to a topic / policy * question through their transcript. Each card shows the meeting's real decision * and question counts; a meeting with decisions expands inline into its own * decision cards (DecisionCardList scoped by meetingId). * * 100% live data — honest loading / error / empty states, never fabricated * cards or counts (CLAUDE.md: No Fabricated Data). */ const PAGE_SIZE = 24 const SORTS: { id: MeetingSort; label: string }[] = [ { id: 'recent', label: 'Most recent' }, { id: 'decisions', label: 'Most decisions' }, { id: 'interesting', label: 'Most interesting' }, ] interface MeetingCardListProps { topicId?: number theme?: string questionId?: string /** 2-letter state code or full state name. */ state?: string city?: string /** Heading shown above the list, e.g. "Meetings on Affordable housing". */ title?: string } // A clean place label from the card's jurisdiction + state. function placeLabel(m: MeetingCard): string { const place = m.city || m.jurisdiction || '' return [place, m.state_code].filter(Boolean).join(', ') } // Friendly date from an ISO yyyy-mm-dd string (the API already coerces). function fmtDate(iso: string | null): string { if (!iso) return '' const d = new Date(`${iso}T00:00:00`) if (Number.isNaN(d.getTime())) return '' return d.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }) } function MeetingRow({ meeting }: { meeting: MeetingCard }) { const [open, setOpen] = useState(false) const canDrill = meeting.has_decisions && meeting.decision_count > 0 const date = fmtDate(meeting.date) return (