'use client' import { useEffect, useState } from 'react' import { useRouter } from 'next/navigation' import Link from 'next/link' import { useTranslations } from 'next-intl' import { ArrowRight, ArrowRightLeft, FileCheck, Landmark, MessageCircle, Sparkles, } from 'lucide-react' import { Button } from '@/components/ui/button' import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' import { cn } from '@/lib/utils' import { useErrorToast } from '@/lib/hooks/use-error-toast' import { ENABLED_EXTENSION_IDS } from '@/lib/extensions/_generated/enabled-extensions' import { useCapability } from '@/contexts/CompanyContext' import { CAPABILITY } from '@/lib/entitlements/keys' import type { InitialSetupPath, InitialSetupState } from '@/types' interface NewUserChecklistProps { initialState: InitialSetupState className?: string hasBookkeepingImported?: boolean hasBankConnected?: boolean hasSkatteverketConnected?: boolean hasAgentBuilt?: boolean } const pathIcons = { migration: ArrowRightLeft, bank: Landmark, fresh: Sparkles, } as const export default function NewUserChecklist({ initialState, className, hasBookkeepingImported = false, hasBankConnected = false, hasSkatteverketConnected = false, hasAgentBuilt = false, }: NewUserChecklistProps) { const t = useTranslations('initial_setup') const router = useRouter() const showError = useErrorToast() const hasAi = useCapability(CAPABILITY.ai) const [state, setState] = useState(initialState) const [saving, setSaving] = useState(null) const hasMigration = ENABLED_EXTENSION_IDS.has('arcim-migration') const hasBanking = ENABLED_EXTENSION_IDS.has('enable-banking') const hasSkatteverket = ENABLED_EXTENSION_IDS.has('skatteverket') const persist = async ( body: Record, pending: InitialSetupPath | 'dismiss' | 'complete', ): Promise => { setSaving(pending) try { const response = await fetch('/api/onboarding/state', { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body), }) if (!response.ok) { await showError(response, { context: 'settings' }) return null } const payload = await response.json() as { data: InitialSetupState } setState(payload.data) return payload.data } catch (error) { await showError(error, { context: 'settings' }) return null } finally { setSaving(null) } } useEffect(() => { const selectedPathComplete = (state.path === 'migration' && hasBookkeepingImported) || (state.path === 'bank' && hasBankConnected) if (!state.completedAt && selectedPathComplete && saving === null) { void persist({ completed: true }, 'complete') } // persist intentionally stays out: its identity follows the toast hook and // would retrigger this completion sync after every render. // eslint-disable-next-line react-hooks/exhaustive-deps }, [hasBankConnected, hasBookkeepingImported, saving, state.completedAt, state.path]) if (state.dismissedAt || state.completedAt) return null const choosePath = async (path: InitialSetupPath) => { const updated = await persist({ path }, path) if (!updated || path === 'fresh') return if (path === 'migration') { router.push(hasMigration ? '/import?mode=migration' : '/import?mode=sie') } else { router.push(hasBanking ? '/import?mode=psd2' : '/import?mode=bank') } } if (!state.path) { return ( {t('title')} {t('description')} {(['migration', 'bank', 'fresh'] as const).map((path) => { const Icon = pathIcons[path] return ( ) })} ) } const primaryHref = state.path === 'migration' ? (hasMigration ? '/import?mode=migration' : '/import?mode=sie') : (hasBanking ? '/import?mode=psd2' : '/import?mode=bank') const PrimaryIcon = pathIcons[state.path] return (
{t(`${state.path}_selected_title`)} {t(`${state.path}_selected_description`)}
{hasSkatteverket && !hasSkatteverketConnected && ( // The authorize endpoint redirects off-site to Skatteverket. // eslint-disable-next-line @next/next/no-html-link-for-pages {t('optional_skatteverket')} )} {!hasAgentBuilt && ( {t('optional_assistant')} )}
) }