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:
@@ -4,6 +4,7 @@ import { useState } from 'react';
|
||||
import { useParams } from 'next/navigation';
|
||||
import { Plus, Archive, Tag as TagIcon, TagsIcon, Trash2 } from 'lucide-react';
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { DataTable } from '@/components/shared/data-table';
|
||||
@@ -102,9 +103,14 @@ export function ClientList() {
|
||||
queryClient.invalidateQueries({ queryKey: ['clients'] });
|
||||
const s = res.data.summary;
|
||||
if (s.failed > 0) {
|
||||
alert(`${s.succeeded} of ${s.total} succeeded. ${s.failed} failed.`);
|
||||
toast.warning(`${s.succeeded} of ${s.total} succeeded. ${s.failed} failed.`);
|
||||
} else if (s.succeeded > 0) {
|
||||
toast.success(`${s.succeeded} client${s.succeeded === 1 ? '' : 's'} updated.`);
|
||||
}
|
||||
},
|
||||
onError: (err: unknown) => {
|
||||
toast.error(err instanceof Error ? err.message : 'Bulk action failed');
|
||||
},
|
||||
});
|
||||
|
||||
const columns = getClientColumns({
|
||||
|
||||
@@ -209,6 +209,11 @@ export function SmartArchiveDialog({ open, onOpenChange, clientId, clientName, o
|
||||
: `${clientName} archived.`,
|
||||
);
|
||||
qc.invalidateQueries({ queryKey: ['clients'] });
|
||||
// Invalidate the single-client query AND the dossier so detail
|
||||
// pages re-fetch (header now shows Archived badge) and a re-open
|
||||
// of the dialog re-fetches a fresh dossier.
|
||||
qc.invalidateQueries({ queryKey: ['clients', clientId] });
|
||||
qc.removeQueries({ queryKey: ['client-archive-dossier', clientId] });
|
||||
qc.invalidateQueries({ queryKey: ['berths'] });
|
||||
qc.invalidateQueries({ queryKey: ['interests'] });
|
||||
onOpenChange(false);
|
||||
|
||||
Reference in New Issue
Block a user