Files
familysync/apps/pwa/src/components/ListsEmptyState.tsx
T
Lucas Berger 95dbc663c1 feat(04-03): wire ListsIndex + ListCard + CreateListSheet + ListDeleteDialog (LIST-01)
- listsClient.ts: add createList/patchList/deleteList + List/ListItem types with activeCount/doneCount
- ListsEmptyState.tsx: extracted standalone component (ClipboardList icon, UI-SPEC copy)
- ListCard.tsx: name/count badge/Shared pill/ChevronRight; hover-reveal delete button; navigates /lists/:id
- CreateListSheet.tsx: bottom-sheet/modal; Shared default (D-01); optimistic useMutation; auto-focus; Escape to close
- ListDeleteDialog.tsx: mirrors DeleteConfirmationDialog pattern; props-driven (no calendarStore); XSS guard on name
- ListsIndex.tsx: replaced placeholder with real data via useQuery+useMutation; mounts CreateListSheet+ListDeleteDialog
- DeleteConfirmationDialog.tsx NOT modified (stable, D-06 pattern preserved)
- PWA typecheck passes; DeleteConfirmationDialog.test.tsx 10 passed
- Playwright E2E: create Groceries+Gift Ideas (Shared pills); delete dialog → confirm → card disappears
2026-06-09 12:45:19 -04:00

63 lines
1.7 KiB
TypeScript

/**
* ListsEmptyState — shown in ListsIndex when the user has no lists.
*
* UI-SPEC §ListsEmptyState:
* - Center-aligned in the list-index scrollable area
* - Icon: ClipboardList (lucide-react, 32px, --color-text-muted)
* - Heading: "No lists yet" (--text-heading-*)
* - Body: "Tap + to create your first shared list — Groceries, Gift Ideas, or anything else."
*
* T-04-06 XSS guard: all text is static plain-text JSX children (no user input).
*/
import { ClipboardList } from 'lucide-react'
export function ListsEmptyState() {
return (
<div
role="status"
aria-live="polite"
style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
flex: 1,
padding: '48px var(--space-4)',
gap: 'var(--space-4)',
textAlign: 'center',
fontFamily: 'var(--font-family-base)',
}}
>
<ClipboardList
size={32}
color="var(--color-text-muted)"
aria-hidden="true"
/>
<div
style={{
fontSize: 'var(--text-heading-size, 18px)',
fontWeight: 600,
color: 'var(--color-text-primary)',
lineHeight: 'var(--text-heading-line-height, 1.25)',
}}
>
{/* Plain text — XSS guard */}
No lists yet
</div>
<div
style={{
fontSize: 'var(--text-body-size, 15px)',
fontWeight: 400,
color: 'var(--color-text-muted)',
lineHeight: 'var(--text-body-line-height, 1.5)',
maxWidth: '280px',
}}
>
{/* Plain text — XSS guard */}
Tap + to create your first shared list &mdash; Groceries, Gift Ideas, or anything else.
</div>
</div>
)
}