'use client' import { useState, useCallback, useEffect } from 'react' import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card' import { Badge } from '@/components/ui/badge' import { Button } from '@/components/ui/button' import { Skeleton } from '@/components/ui/skeleton' import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/components/ui/table' import { useExtensionData } from '@/lib/extensions/use-extension-data' import { useToast } from '@/components/ui/use-toast' import { Building2, CheckCircle, XCircle, MapPin, Mail, Phone, Settings, } from 'lucide-react' import Link from 'next/link' import type { WorkspaceComponentProps } from '@/lib/extensions/workspace-registry' import type { TICCompanyProfile } from '@/extensions/general/tic/lib/tic-types' function formatKSEK(value: number | null): string { if (value === null) return '—' return `${(value * 1000).toLocaleString('sv-SE')} kr` } function formatPercent(value: number | null): string { if (value === null) return '—' return `${value.toFixed(1)} %` } function toMs(epoch: number): number { // TIC returns epoch seconds; Date() expects milliseconds return epoch < 1e12 ? epoch * 1000 : epoch } function formatPeriod(start: number, end: number): string { const s = new Date(toMs(start)) const e = new Date(toMs(end)) const fmt = (d: Date) => d.toLocaleDateString('sv-SE', { year: 'numeric', month: '2-digit', day: '2-digit' }) return `${fmt(s)} – ${fmt(e)}` } function timeAgo(isoDate: string): string { const diff = Date.now() - new Date(isoDate).getTime() const minutes = Math.floor(diff / 60000) if (minutes < 1) return 'just nu' if (minutes < 60) return `${minutes} min sedan` const hours = Math.floor(minutes / 60) if (hours < 24) return `${hours} tim sedan` const days = Math.floor(hours / 24) return `${days} dag${days > 1 ? 'ar' : ''} sedan` } function ProfileSkeleton() { return (
{Array.from({ length: 6 }).map((_, i) => ( ))}
) } export default function TicWorkspace({ userId }: WorkspaceComponentProps) { const { getByKey, save, isLoading: isDataLoading } = useExtensionData('general', 'tic') const { toast } = useToast() const [profile, setProfile] = useState(null) const [isFetching, setIsFetching] = useState(false) const [noOrgNumber, setNoOrgNumber] = useState(false) const [initialLoad, setInitialLoad] = useState(true) // Load cached profile from extension data useEffect(() => { if (isDataLoading) return const cached = getByKey('company_profile') if (cached?.value) { setProfile(cached.value as unknown as TICCompanyProfile) } setInitialLoad(false) }, [isDataLoading, getByKey]) const fetchProfile = useCallback(async () => { setIsFetching(true) setNoOrgNumber(false) try { // Get org_number from company settings const settingsRes = await fetch('/api/settings') if (!settingsRes.ok) { toast({ title: 'Kunde inte hämta inställningar', variant: 'destructive' }) return } const { data: settings } = await settingsRes.json() const orgNumber = settings?.org_number if (!orgNumber) { setNoOrgNumber(true) return } const res = await fetch( `/api/extensions/ext/tic/profile?org_number=${encodeURIComponent(orgNumber)}` ) if (!res.ok) { const { error } = await res.json() toast({ title: error ?? 'Kunde inte hämta företagsprofil', variant: 'destructive' }) return } const { data } = await res.json() setProfile(data) await save('company_profile', data) } catch { toast({ title: 'Ett oväntat fel inträffade', variant: 'destructive' }) } finally { setIsFetching(false) } }, [save, toast]) // Auto-fetch on first visit when no cached data useEffect(() => { if (!initialLoad && !profile && !noOrgNumber && !isFetching) { fetchProfile() } }, [initialLoad, profile, noOrgNumber, isFetching, fetchProfile]) if (initialLoad || isDataLoading) { return } if (noOrgNumber) { return (

Inget organisationsnummer

Ange organisationsnummer under Inställningar för att visa företagsprofilen.

) } if (isFetching && !profile) { return } if (!profile) return null const isActive = profile.activityStatus !== 'ceased' return (
{/* Status bar */}
{isActive ? 'Aktiv' : 'Avregistrerat'} {profile.registration.fTax && F-skatt} {profile.registration.vat && Moms} {profile.registration.payroll && Arbetsgivare} Uppdaterad {timeAgo(profile.fetchedAt)}
{/* Company info card */} {profile.companyName} {profile.orgNumber} · {profile.legalEntityType} {profile.address && (
{[profile.address.street, `${profile.address.postalCode} ${profile.address.city}`] .filter(Boolean) .join(', ')}
)} {profile.email && (
{profile.email}
)} {profile.phone && (
{profile.phone}
)} {profile.sniCodes.length > 0 && (

SNI-koder

{profile.sniCodes .filter((sni, i, arr) => arr.findIndex(s => s.code === sni.code) === i) .map((sni) => (

{sni.code}{' '} {sni.name}

))}
)} {profile.bankAccounts.length > 0 && (

Bankuppgifter

{profile.bankAccounts.map((ba, i) => (

{ba.type}:{' '} {ba.accountNumber}

))}
)} {profile.purpose && (

Verksamhet

{profile.purpose}

)} {(profile.employeeRange || profile.turnoverRange) && (
{profile.employeeRange && (

Anställda: {profile.employeeRange}

)} {profile.turnoverRange && (

Omsättning: {profile.turnoverRange}

)}
)}
{/* Financials card */} Senaste bokslut {profile.financials && ( {formatPeriod(profile.financials.periodStart, profile.financials.periodEnd)} )} {profile.financials ? (
) : (

Inga finansiella uppgifter tillgängliga.

)}
{/* Financial reports table */} {profile.financialReports.length > 0 && ( Årsredovisningar Period Titel Inlämnad Reviderad Revisionsutlåtande {profile.financialReports .filter((r) => !r.isInterimReport) .sort((a, b) => { const aEnd = a.periodEnd ? new Date(a.periodEnd).getTime() : 0 const bEnd = b.periodEnd ? new Date(b.periodEnd).getTime() : 0 return bEnd - aEnd }) .slice(0, 10) .map((report, i) => ( {report.periodStart && report.periodEnd ? `${report.periodStart.slice(0, 10)} – ${report.periodEnd.slice(0, 10)}` : '—'} {report.title ?? '—'} {report.arrivalDate ? new Date(report.arrivalDate).toLocaleDateString('sv-SE') : '—'} {report.isAudited === true ? ( ) : report.isAudited === false ? ( ) : ( )} {report.auditOpinion ?? '—'} ))}
)}
) } function FinancialCell({ label, value, negative = false, }: { label: string value: string negative?: boolean }) { return (

{label}

{value}

) }