fix(audit): frontend HIGHs — surface fetch errors, kill href=#, invalidate queries, toast over alert
R2-H10: webhook-delivery-log and audit-log-list both swallowed fetch errors silently — failed loads showed spinner forever or stale data. Both now set a loadError state, show an inline retry banner, and fire a toast.error. Same applies to audit-log loadMore. R2-H11: audit-log-card rendered as `<a href="#">` — tapping on mobile inserted `#` in the URL and scrolled to top (back-button trap). ListCard now treats `href` as optional and renders a non-link `<div>` when omitted; audit-log-card no longer passes href. R2-H12: smart-archive-dialog only invalidated ['clients'] / ['berths'] / ['interests']. Detail header kept showing Archived=false until hard reload. Now also invalidates ['clients', clientId] and removes the ['client-archive-dossier', clientId] cache so re-open re-fetches. R2-H13: client-list bulk mutation used native alert() on partial failure (blocking the page) and had no onError handler. Replaced with toast.warning / toast.success / toast.error. 1175/1175 vitest passing. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -42,6 +42,7 @@ export function WebhookDeliveryLog({ webhookId }: Props) {
|
||||
const [total, setTotal] = useState(0);
|
||||
const [page, setPage] = useState(1);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [loadError, setLoadError] = useState<string | null>(null);
|
||||
const [retrying, setRetrying] = useState<string | null>(null);
|
||||
const { can } = usePermissions();
|
||||
const canReplay = can('admin', 'manage_webhooks');
|
||||
@@ -63,14 +64,17 @@ export function WebhookDeliveryLog({ webhookId }: Props) {
|
||||
|
||||
async function load(p: number) {
|
||||
setLoading(true);
|
||||
setLoadError(null);
|
||||
try {
|
||||
const result = await apiFetch<{ data: Delivery[]; total: number }>(
|
||||
`/api/v1/admin/webhooks/${webhookId}/deliveries?page=${p}&limit=25`,
|
||||
);
|
||||
setDeliveries(result.data);
|
||||
setTotal(result.total);
|
||||
} catch {
|
||||
// ignore
|
||||
} catch (err) {
|
||||
const msg = err instanceof Error ? err.message : 'Failed to load deliveries';
|
||||
setLoadError(msg);
|
||||
toast.error(msg);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -85,6 +89,17 @@ export function WebhookDeliveryLog({ webhookId }: Props) {
|
||||
return <p className="text-sm text-muted-foreground">Loading deliveries...</p>;
|
||||
}
|
||||
|
||||
if (loadError && deliveries.length === 0) {
|
||||
return (
|
||||
<div className="rounded-md border border-destructive/30 bg-destructive/5 p-3 text-sm space-y-2">
|
||||
<p className="text-destructive">Couldn’t load deliveries: {loadError}</p>
|
||||
<Button size="sm" variant="outline" onClick={() => void load(page)}>
|
||||
Retry
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!loading && deliveries.length === 0) {
|
||||
return <p className="text-sm text-muted-foreground">No deliveries yet.</p>;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user