'use client' import { useState, useEffect, useRef } from 'react' import { useForm } 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 { Loader2, ArrowRight, ArrowLeft, CheckCircle2, AlertTriangle } from 'lucide-react' import type { EntityType } from '@/types' import type { CompanyLookupResult } from '@/lib/company-lookup/types' const schema = z.object({ company_name: z.string().min(1, 'Företagsnamn krävs'), org_number: z.string() .min(1, 'Organisationsnummer krävs') .regex(/^\d{6,8}[-\s]?\d{4}$/, 'Ogiltigt format. Ange XXXXXX-XXXX'), address_line1: z.string().optional(), postal_code: z.string().optional(), city: z.string().optional(), }) type FormData = z.infer const ORG_NUMBER_REGEX = /^\d{6,8}[-\s]?\d{4}$/ interface Step2Props { initialData: Partial entityType?: EntityType ticEnabled?: boolean onTicLookup?: (result: CompanyLookupResult | null) => void onNext: (data: FormData) => void onBack: () => void isSaving: boolean } export default function Step2CompanyDetails({ initialData, entityType, ticEnabled, onTicLookup, onNext, onBack, isSaving, }: Step2Props) { const { register, handleSubmit, watch, setValue, formState: { errors }, } = useForm({ resolver: zodResolver(schema), mode: 'onTouched', defaultValues: { company_name: initialData.company_name || '', org_number: initialData.org_number || '', address_line1: initialData.address_line1 || '', postal_code: initialData.postal_code || '', city: initialData.city || '', }, }) const [isLooking, setIsLooking] = useState(false) const [lookupError, setLookupError] = useState(null) const [lookupDone, setLookupDone] = useState(null) const abortRef = useRef(null) const orgNumber = watch('org_number') useEffect(() => { if (!ticEnabled || !orgNumber || !ORG_NUMBER_REGEX.test(orgNumber)) { return } setLookupError(null) setLookupDone(null) const timer = setTimeout(() => { // Abort any in-flight request abortRef.current?.abort() const controller = new AbortController() abortRef.current = controller setIsLooking(true) fetch(`/api/extensions/ext/tic/lookup?org_number=${encodeURIComponent(orgNumber)}`, { signal: controller.signal, }) .then(async (res) => { if (controller.signal.aborted) return if (res.status === 403) { // Extension disabled — silently ignore return } if (res.status === 404) { setLookupError('Inget företag hittades med det organisationsnumret.') onTicLookup?.(null) return } if (!res.ok) { setLookupError('Kunde inte hämta företagsuppgifter. Du kan fylla i manuellt.') onTicLookup?.(null) return } const { data } = (await res.json()) as { data: CompanyLookupResult } // Guard: only apply if org_number still matches (user may have changed it) if (controller.signal.aborted) return setLookupDone(data) onTicLookup?.(data) // Auto-fill from TIC — overwrite since user just entered a new org number if (data.companyName) setValue('company_name', data.companyName) if (data.address?.street) setValue('address_line1', data.address.street) if (data.address?.postalCode) setValue('postal_code', data.address.postalCode) if (data.address?.city) setValue('city', data.address.city) }) .catch((err) => { if ((err as Error).name === 'AbortError') return setLookupError('Kunde inte hämta företagsuppgifter. Du kan fylla i manuellt.') onTicLookup?.(null) }) .finally(() => { if (!controller.signal.aborted) setIsLooking(false) }) }, 500) return () => { clearTimeout(timer) abortRef.current?.abort() } // eslint-disable-next-line react-hooks/exhaustive-deps }, [ticEnabled, orgNumber]) const isAB = entityType === 'aktiebolag' return (
Grunduppgifter {ticEnabled ? 'Ange organisationsnummer så hämtas övriga uppgifter automatiskt.' : isAB ? 'Ange bolagets registrerade namn och organisationsnummer.' : 'Ange namn på din verksamhet.'}
{ const fields = Object.keys(errs).join(', ') console.error('[onboarding] step 2 validation failed:', fields, errs) fetch('/api/log', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ message: 'step 2 validation failed', extra: { fields } }) }).catch(() => {}) })} className="space-y-4">
{errors.org_number && (

{errors.org_number.message}

)}

{isAB ? 'Obligatoriskt för aktiebolag' : 'Vid enskild firma är orgnummer samma som ditt personnummer'}

{ticEnabled && isLooking && (
Hämtar företagsuppgifter...
)} {ticEnabled && lookupDone && !lookupDone.isCeased && (
{lookupDone.companyName}
)} {ticEnabled && lookupDone?.isCeased && (
{lookupDone.companyName} — företaget är avregistrerat
)} {ticEnabled && lookupError && (

{lookupError}

)}
{errors.company_name && (

{errors.company_name.message}

)}

Adress (för fakturor)

) }