import type { JSX } from 'solid-js'; import { createEffect, createMemo, createSignal, Match, Switch } from 'solid-js'; import { FilterPanel } from '@/components/FilterPanel'; import { OpportunityTable } from '@/components/OpportunityTable'; import { buttonPrimary } from '@/components/styles'; import { ThemeToggle } from '@/components/ThemeToggle'; import { ToastList } from '@/components/ToastList'; import type { OpportunityFilters } from '@/lib/filterOpportunities'; import { emptyFilters, filterOpportunities, statusOptions } from '@/lib/filterOpportunities'; import { formatCount } from '@/lib/format'; import { createLiveUpdates } from '@/primitives/createLiveUpdates'; import { createClassifications, createOpportunities } from '@/primitives/createOpportunities'; import { createOpportunityAnnotations } from '@/primitives/createOpportunityAnnotations'; import { createTheme } from '@/primitives/createTheme'; import { createToasts } from '@/primitives/createToasts'; const statePanel = 'rounded-[10px] border border-line bg-surface px-6 py-12 text-center'; /** The page: the filters, the table of opportunities, and toasts for errors. */ export function App(): JSX.Element { const toasts = createToasts(); const opportunities = createOpportunities(); const classifications = createClassifications(); const annotations = createOpportunityAnnotations(opportunities.items, toasts.show); createLiveUpdates({ opportunities, classifications, annotations }); const theme = createTheme(); const [filters, setFilters] = createSignal(emptyFilters()); const statuses = createMemo(() => statusOptions(opportunities.items())); // annotations.get reads from a store, so hiding an opportunity re-runs this. const rows = createMemo(() => filterOpportunities(opportunities.items(), filters(), annotations.get), ); const clear = (): void => { setFilters(emptyFilters()); }; // Only the rows shown are hidden: those a filter leaves out keep their // marks, and come back as they were when it's cleared. const hideable = createMemo(() => rows() .map((opportunity) => opportunity.uuid) .filter((uuid) => !annotations.get(uuid).hidden), ); const hideAllVisible = (): void => { const uuids = hideable(); const count = uuids.length === 1 ? 'this opportunity' : `these ${formatCount(uuids.length)} opportunities`; if (window.confirm(`Hide ${count}?`)) annotations.hideAll(uuids); }; // A failed load is shown where the list would be, with a retry, and as a // toast, so it's seen wherever the user is looking. createEffect(() => { const err = opportunities.error(); if (err) toasts.show(`Opportunities didn't load: ${err.message}`); }); createEffect(() => { const err = classifications.error(); if (err) toasts.show(`Classifications didn't load: ${err.message}`); }); return (

Opportunities

} >

Loading opportunities…

{(err) => ( )}

There are no opportunities yet.

No opportunities match these filters.

); }