Errors now show as toasts in the bottom right corner

This commit is contained in:
bwilliams committed 2026-10-16 16:22:19 +01:00
1 parent 115ee29a85
commit 49dd11be56
6 files changed
+125 -29

No files matched your search

+20 -18
View File
@@ -1,24 +1,27 @@
import type { JSX } from 'solid-js';
import { createMemo, createSignal, Match, Show, Switch } from 'solid-js';
import { createEffect, createMemo, createSignal, Match, Switch } from 'solid-js';
import { FilterPanel } from '@/components/FilterPanel';
import { OpportunityTable } from '@/components/OpportunityTable';
import { buttonPrimary, linkButton } from '@/components/styles';
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 { 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, any save error, and the table of opportunities. */
/** 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);
const annotations = createOpportunityAnnotations(opportunities.items, toasts.show);
createLiveUpdates({ opportunities, classifications, annotations });
const theme = createTheme();
const [filters, setFilters] = createSignal<OpportunityFilters>(emptyFilters());
@@ -32,6 +35,17 @@ export function App(): JSX.Element {
setFilters(emptyFilters());
};
// 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 (
<div class="mx-auto max-w-[1440px] px-3 pt-4 pb-12 sm:px-6 sm:pt-7 sm:pb-16">
<header class="mb-5 flex items-center justify-between gap-4">
@@ -52,20 +66,6 @@ export function App(): JSX.Element {
total={opportunities.items().length}
/>
<Show when={annotations.error()}>
{(message) => (
<div
class="mb-4 flex items-center justify-between gap-4 rounded-md bg-danger-soft px-3.5 py-2.5 text-danger"
role="alert"
>
<span>{message()}</span>
<button type="button" class={linkButton} onClick={annotations.dismissError}>
Dismiss
</button>
</div>
)}
</Show>
<main>
<Switch fallback={<OpportunityTable opportunities={rows()} annotations={annotations} />}>
<Match when={opportunities.isLoading() && opportunities.items().length === 0}>
@@ -94,6 +94,8 @@ export function App(): JSX.Element {
</Match>
</Switch>
</main>
<ToastList toasts={toasts.items()} onDismiss={toasts.dismiss} />
</div>
);
}