'use client' import { useEffect } from 'react' import { useTranslations } from 'next-intl' import { useForm, Controller } from 'react-hook-form' import { zodResolver } from '@hookform/resolvers/zod' import { z } from 'zod' import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { Checkbox } from '@/components/ui/checkbox' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select' import { InfoTooltip } from '@/components/ui/info-tooltip' import { Loader2, ArrowRight, ArrowLeft, Info } from 'lucide-react' import { useToast } from '@/components/ui/use-toast' import { deriveSwedishVatNumber } from '@/lib/vat/vat-number' import type { MomsPeriod, EntityType } from '@/types' const schema = z.object({ vat_registered: z.boolean(), vat_number: z.string().optional(), moms_period: z.enum(['monthly', 'quarterly', 'yearly']).optional(), accounting_method: z.enum(['accrual', 'cash']), }).superRefine((data, ctx) => { if (data.vat_registered && !data.moms_period) { ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'Välj momsredovisningsperiod.', path: ['moms_period'], }) } }) type FormData = z.infer interface Step4Output { vat_registered: boolean vat_number?: string moms_period?: MomsPeriod accounting_method: 'accrual' | 'cash' } interface Step4Props { initialData: Partial entityType?: EntityType orgNumber?: string onNext: (data: Step4Output) => void onBack: () => void isSaving: boolean } export default function Step4VatAccounting({ initialData, entityType, orgNumber, onNext, onBack, isSaving, }: Step4Props) { const t = useTranslations('onboarding') const { toast } = useToast() const { register, handleSubmit, watch, control, setValue, formState: {}, } = useForm({ resolver: zodResolver(schema), mode: 'onTouched', defaultValues: { vat_registered: initialData.vat_registered ?? false, vat_number: initialData.vat_number || '', moms_period: initialData.moms_period, accounting_method: initialData.accounting_method ?? 'accrual', }, }) const vatRegistered = watch('vat_registered') const vatNumber = watch('vat_number') const accountingMethod = watch('accounting_method') // Auto-fill VAT number when vat_registered toggles on. Derive via the shared // helper so a 12-digit personnummer (enskild firma) gets its century dropped — // building SE${orgNumber}01 verbatim produced SE + 14 digits and failed // validation on save. useEffect(() => { if (vatRegistered && !vatNumber && orgNumber) { const derived = deriveSwedishVatNumber(orgNumber) if (derived) { setValue('vat_number', derived) } } }, [vatRegistered, vatNumber, orgNumber, setValue]) const onSubmit = (data: FormData) => { const output: Step4Output = { vat_registered: data.vat_registered, vat_number: data.vat_registered ? data.vat_number : undefined, moms_period: data.vat_registered ? data.moms_period : undefined, accounting_method: data.accounting_method, } onNext(output) } return (
{t('step4_card_title')} {t('step4_card_description')}
{ const fields = Object.keys(errs).join(', ') console.error('[onboarding] step 4 validation failed:', fields, errs) fetch('/api/log', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ message: 'step 4 validation failed', extra: { fields } }) }).catch(() => {}) const firstError = Object.values(errs)[0] const message = firstError?.message || t('check_all_fields') toast({ title: t('missing_fields'), description: String(message), variant: 'destructive' }) })} className="space-y-6"> {/* VAT section */}

{t('step4_vat_tip_title')}

{t('step4_vat_tip_body')}

{t('step4_vat_tip_note')}

} side="right" > {t('step4_vat_heading')}
( )} />

{t('step4_vat_registered_help')}

{vatRegistered && (

{t('step4_vat_number_format')}

{t('step4_vat_period_tip_title')}

{t('step4_vat_period_tip_body')}

  • {t('step4_vat_period_bracket_low')}
  • {t('step4_vat_period_bracket_mid')}
  • {t('step4_vat_period_bracket_high')}
} side="right" > ( )} />

{t('step4_period_help')}

)} {/* Accounting method */}
(
{ if (checked) field.onChange('accrual') }} />
{ if (checked) field.onChange('cash') }} />
)} />
{accountingMethod === 'accrual' ? t('step4_method_accrual') : t('step4_method_cash')}

{accountingMethod === 'accrual' ? t('step4_method_accrual_desc') : t('step4_method_cash_desc')}

{t('step4_cash_limit_note')}

) }