import { useMemo, useState } from 'react'; import { useParams } from 'react-router-dom'; import { useQuery } from '@tanstack/react-query'; import Col from 'react-bootstrap/Col'; import Row from 'react-bootstrap/Row'; import { VerticalTimeline } from 'react-vertical-timeline-component'; import { fetchEvents, fetchSources, type Source } from '../api/client'; import { Filters } from '../components/Filters'; import { TimelineEntry } from '../components/TimelineEntry'; const RANGE_MIN = new Date('2010-01-01T00:00:00Z').getTime(); const RANGE_MAX = Date.now(); function parseDate(s: string): number { // Accept YYYY-MM-DD or full ISO datetime const t = new Date(s.includes('T') ? s : s + 'T00:00:00Z').getTime(); return isNaN(t) ? NaN : t; } function endOfDay(s: string): number { const t = new Date(s.includes('T') ? s : s + 'T23:59:59Z').getTime(); return isNaN(t) ? NaN : t; } function parseTimespan(timespan?: string): [number, number] | null { if (!timespan) return null; if (timespan.includes('..')) { const [a, b] = timespan.split('..'); const from = parseDate(a); const to = endOfDay(b); if (!isNaN(from) && !isNaN(to)) return [from, to]; } else { const from = parseDate(timespan); const to = endOfDay(timespan); if (!isNaN(from)) return [from, to]; } return null; } export function TimelineHome() { const { timespan } = useParams(); const [enabledSources, setEnabledSources] = useState>({ github: true, gitea: true, hg: true, bugzilla: true, }); const [rangeValue, setRangeValue] = useState<[number, number]>(() => { const parsed = parseTimespan(timespan); if (parsed) return parsed; const now = Date.now(); const thirtyDaysAgo = now - 30 * 24 * 60 * 60 * 1000; return [thirtyDaysAgo, now]; }); const [limit, setLimit] = useState(100); const sourcesQ = useQuery({ queryKey: ['sources'], queryFn: fetchSources, refetchInterval: 60_000, }); const activeSources = useMemo( () => (Object.keys(enabledSources) as Source[]).filter((s) => enabledSources[s]), [enabledSources], ); const eventsQ = useQuery({ queryKey: ['events', rangeValue, activeSources, limit], queryFn: () => fetchEvents({ from: new Date(rangeValue[0]), to: new Date(rangeValue[1]), sources: activeSources, limit, }), refetchInterval: 60_000, }); const events = eventsQ.data ?? []; return ( <> setEnabledSources((prev) => ({ ...prev, [s]: on })) } rangeMin={RANGE_MIN} rangeMax={RANGE_MAX} rangeValue={rangeValue} onRangeChange={setRangeValue} limit={limit} onLimitChange={setLimit} summaries={sourcesQ.data} />

{eventsQ.isLoading ? 'loading…' : eventsQ.isError ? `error: ${(eventsQ.error as Error).message}` : `showing ${events.length} ${events.length === 1 ? 'activity' : 'activities'}`}

{events.map((item) => ( ))}
); }