import { store } from '@/actions/App/Http/Controllers/Settings/AccountController'; import { store as storeBank } from '@/actions/App/Http/Controllers/Settings/BankController'; import { AccountForm, AccountFormData, } from '@/components/accounts/account-form'; import { StepHeader } from '@/components/onboarding/step-header'; import { Button } from '@/components/ui/button'; import { CreatedAccount } from '@/hooks/use-onboarding-state'; import { encrypt, importKey } from '@/lib/crypto'; import { getStoredKey } from '@/lib/key-storage'; import { type AccountType, formatAccountType } from '@/types/account'; import { __ } from '@/utils/i18n'; import { AlertCircle, CheckCircle2, CreditCard } from 'lucide-react'; import { useCallback, useMemo, useRef, useState } from 'react'; import { StepButton } from './step-button'; interface ExistingAccount { id: string; name: string; name_iv: string; type: string; currency_code: string; bank_id: string; bank?: { id: string; name: string; logo: string | null; }; } interface StepCreateAccountProps { banks: { id: string; name: string; logo: string | null }[]; isFirstAccount: boolean; existingAccounts?: ExistingAccount[]; onAccountCreated: (account: CreatedAccount) => void; onSkip?: () => void; } export function StepCreateAccount({ isFirstAccount, existingAccounts = [], onAccountCreated, onSkip, }: StepCreateAccountProps) { const [isSubmitting, setIsSubmitting] = useState(false); const [error, setError] = useState(null); const formDataRef = useRef({ displayName: '', bankId: null, type: isFirstAccount ? 'checking' : null, currencyCode: null, customBank: null, }); const handleFormChange = useCallback((data: AccountFormData) => { formDataRef.current = data; }, []); async function createBankAndGetId(): Promise { const customBank = formDataRef.current.customBank; if (!customBank) return null; const formData = new FormData(); formData.append('name', customBank.name); if (customBank.logo) { formData.append('logo', customBank.logo); } const response = await fetch(storeBank.url(), { method: 'POST', body: formData, headers: { 'X-XSRF-TOKEN': decodeURIComponent( document.cookie .split('; ') .find((row) => row.startsWith('XSRF-TOKEN=')) ?.split('=')[1] || '', ), Accept: 'application/json', }, }); if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.message || 'Failed to create bank'); } const data = await response.json(); return data.id; } async function handleSubmit(event: React.FormEvent) { event.preventDefault(); setError(null); const { displayName, bankId, type, currencyCode, customBank } = formDataRef.current; const keyString = getStoredKey(); if (!keyString) { setError( __( 'Encryption key not available. Please go back and set up encryption.', ), ); return; } if (!displayName.trim()) { setError(__('Please enter an account name.')); return; } if (!type || !currencyCode) { setError(__('Please fill in all required fields.')); return; } if (isFirstAccount && type !== 'checking') { setError(__('Your first account must be a checking account.')); return; } setIsSubmitting(true); try { let finalBankId: string; if (customBank) { if (!customBank.name.trim()) { setError(__('Please enter a bank name.')); setIsSubmitting(false); return; } const createdBankId = await createBankAndGetId(); if (!createdBankId) { throw new Error('Failed to create bank'); } finalBankId = createdBankId; } else { if (!bankId) { setError(__('Please select a bank.')); setIsSubmitting(false); return; } finalBankId = String(bankId); } const key = await importKey(keyString); const { encrypted, iv } = await encrypt(displayName, key); const response = await fetch(store.url(), { method: 'POST', body: JSON.stringify({ name: encrypted, name_iv: iv, bank_id: finalBankId, type: type, currency_code: currencyCode, }), headers: { 'Content-Type': 'application/json', 'X-XSRF-TOKEN': decodeURIComponent( document.cookie .split('; ') .find((row) => row.startsWith('XSRF-TOKEN=')) ?.split('=')[1] || '', ), Accept: 'application/json', }, }); if (!response.ok) { const errorData = await response.json(); throw new Error( errorData.message || Object.values(errorData.errors || {})[0] || 'Failed to create account', ); } const accountData = await response.json(); onAccountCreated({ id: accountData.id || finalBankId, name: displayName, type: type, currencyCode: currencyCode, }); setIsSubmitting(false); } catch (err) { console.error('Account creation failed:', err); setError( err instanceof Error ? err.message : __('Failed to create account. Please try again.'), ); setIsSubmitting(false); } } const hasExistingAccounts = existingAccounts.length > 0; const { title, description } = useMemo(() => { if (hasExistingAccounts) { return { title: __('Your Accounts'), description: __( "You already have accounts set up. Let's continue with the onboarding.", ), }; } if (isFirstAccount) { return { title: __('Create an Account'), description: __( "Let's start with your main checking account. You can add more accounts later.", ), }; } return { title: __('Add Another Account'), description: __( 'Add another account to track more of your finances.', ), }; }, [hasExistingAccounts, isFirstAccount]); return (
{hasExistingAccounts ? (
{existingAccounts.map((account) => (

{account.bank?.name || 'Account'}

{formatAccountType( account.type as AccountType, )}{' '} • {account.currency_code}

))}
onAccountCreated({ id: existingAccounts[0].id, name: existingAccounts[0].bank?.name || 'Account', type: existingAccounts[0].type, currencyCode: existingAccounts[0].currency_code, }) } />
) : (
{isFirstAccount && (

{__('Your first account must be a')}{' '} {__('Checking')}{' '} {__('account.')}

)} {error && (
{error}
)} {!isFirstAccount && onSkip && ( )} )}
); }