Public Access
- 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.
29 lines
985 B
TypeScript
29 lines
985 B
TypeScript
import { cn } from '../../lib/utils';
|
|
import type { ReactNode } from 'react';
|
|
|
|
interface BadgeProps {
|
|
variant?: 'primary' | 'success' | 'warning' | 'danger' | 'info' | 'neutral';
|
|
children: ReactNode;
|
|
className?: string;
|
|
icon?: ReactNode;
|
|
'aria-label'?: string;
|
|
}
|
|
|
|
export function Badge({ variant = 'neutral', children, className, icon, 'aria-label': ariaLabel }: BadgeProps) {
|
|
const variants = {
|
|
primary: 'bg-primary-50 text-primary-700 border border-primary-200',
|
|
success: 'bg-success-50 text-success-700 border border-success-200',
|
|
warning: 'bg-warning-50 text-warning-700 border border-warning-200',
|
|
danger: 'bg-danger-50 text-danger-700 border border-danger-200',
|
|
info: 'bg-blue-50 text-blue-700 border border-blue-200',
|
|
neutral: 'bg-surface-100 text-surface-600 border border-surface-200',
|
|
};
|
|
|
|
return (
|
|
<span className={cn('badge', variants[variant], className)} aria-label={ariaLabel}>
|
|
{icon}
|
|
{children}
|
|
</span>
|
|
);
|
|
}
|