/** * SetupBanner — self-service credential onboarding banner (D-07). * * Shown ONLY when meQuery.data?.user.needsProviderSetup === true. * NO dismiss/X button — the ONLY way this clears is a successful credential save * which invalidates ['me'] → /api/me refetches → needsProviderSetup becomes false * → this component unmounts on the next render. * * Success-only dismissal contract: * CredentialSheet.onSuccess → invalidateQueries(['me']) → meQuery.data.user.needsProviderSetup=false * → SetupBanner returns null → banner disappears. * There is NO other code path that hides this banner. * * UI-SPEC §Surface 4: * - role="status" + aria-live="polite" (screen readers announce on load) * - KeyRound icon (size 20, var(--color-member-0)) * - Heading "Set up your calendar" (15px/600/primary) * - Body copy (13px/400/secondary) * - "Set up now" CTA (accent-filled, minHeight 44px) → opens CredentialSheet in self-service mode * - Card style: var(--color-surface-dim), 1px border, var(--space-2) radius, var(--space-4) padding * * Security: T-05-24 — all copy is plain-text JSX children, no dangerouslySetInnerHTML. */ import { useState, useRef } from 'react'; import { KeyRound } from 'lucide-react'; import { useQuery } from '@tanstack/react-query'; import { fetchMe } from '../api/client.js'; import { CredentialSheet } from './CredentialSheet.js'; export function SetupBanner() { const [sheetOpen, setSheetOpen] = useState(false); // Use HTMLButtonElement for the ref (assignable to the CredentialSheet's HTMLElement trigger) const ctaRef = useRef(null); const meQuery = useQuery({ queryKey: ['me'], queryFn: fetchMe, retry: false, staleTime: 5 * 60 * 1000, }); // Only show when needsProviderSetup is explicitly true // (undefined / false = no banner) if (meQuery.data?.user.needsProviderSetup !== true) return null; const memberName = meQuery.data.user.displayName ?? 'Member'; return ( <>
{/* CredentialSheet in self-service mode. On success: invalidates ['me'] → needsProviderSetup=false → this component unmounts. */} setSheetOpen(false)} mode="self-service" memberName={memberName} triggerRef={ctaRef} /> ); }