Public Access
feat(ui): close 3 P2 audit findings + a11y sweep (Sprint 3)
- lib/toast.tsx (renamed from .ts for JSX): new showToast.undo(message, onUndo, ms=5000) helper. Inline 'Undo' button dismisses the toast and fires onUndo. Note: react-hot-toast 2.6 lacks onClose/onDismiss, so expiry is silent — same effective behavior as confirm() declined. - Dashboard.handleDelete: captures the full MealPlanItem before the DELETE so Undo can re-fire meals.generateItem(planId, dayOfWeek, mealType) and refill the slot (recipe may differ — see plan R4). - Pantry.handleRemove: fully reversible — Undo re-fires pantry.add with the original ingredient_id, quantity, and unit. New removeId state scopes the spinner to the clicked row. - Both confirm() call sites removed. - App.tsx Navigation: whitespace-nowrap + px-2 sm:px-3 so all 4 links fit on one line down to 360 px. aria-current='page' on the active link. <nav aria-label='Primary'>, <main id='main-content'>. - components/ui/Badge: optional icon and aria-label props. Dashboard approval-status Badge passes aria-label='Approval status: approved' (or the current value) so screen readers don't rely on color alone. - ErrorBoundary already mounted at App.tsx:42 — verified, no code change. - Review/sprint3-verification.md (new) + Review/ui-nielsen-audit.md and fix-ui-audit.md updated with Sprint 3 status and deploy steps. Build: npm run build (tsc + vite) green. tsc 0 errors.
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
import toast from 'react-hot-toast';
|
||||
|
||||
export const showToast = {
|
||||
success: (message: string) => toast.success(message),
|
||||
error: (message: string) => toast.error(message),
|
||||
loading: (message: string) => toast.loading(message),
|
||||
dismiss: (toastId: string) => toast.dismiss(toastId),
|
||||
promise: <T,>(
|
||||
promise: Promise<T>,
|
||||
messages: { loading: string; success: string; error: string }
|
||||
) => toast.promise(promise, messages),
|
||||
/**
|
||||
* Show a confirmation toast with an Undo button. The toast stays
|
||||
* visible for `ms` (default 5000) and the user can click Undo to
|
||||
* fire `onUndo`. If the user does nothing, the toast just dismisses.
|
||||
*/
|
||||
undo: (message: string, onUndo: () => void, ms = 5000) => {
|
||||
return toast(
|
||||
(t) => (
|
||||
<span className="flex items-center gap-3">
|
||||
<span>{message}</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
onUndo();
|
||||
toast.dismiss(t.id);
|
||||
}}
|
||||
className="font-semibold text-primary-600 hover:text-primary-700 focus:outline-none focus:ring-2 focus:ring-primary-400 rounded px-1"
|
||||
>
|
||||
Undo
|
||||
</button>
|
||||
</span>
|
||||
),
|
||||
{
|
||||
duration: ms,
|
||||
icon: '\u2715',
|
||||
style: { background: '#fff', color: '#404040' },
|
||||
}
|
||||
);
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user