- 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
63 lines
1.7 KiB
TypeScript
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 — Groceries, Gift Ideas, or anything else.
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|