'use client' import { useEffect, useState } from 'react' import Link from 'next/link' import { useLocale, useTranslations } from 'next-intl' import { AlertCircle, FolderArchive } from 'lucide-react' import { Card, CardContent } from '@/components/ui/card' import { EmptyState } from '@/components/ui/empty-state' import { Skeleton } from '@/components/ui/skeleton' import { TH_CLASS, TD_CLASS, QUIET_LINK_CLASS } from '@/components/ui/dry-table' import { ReportExportMenu } from '@/components/reports/ReportExportMenu' import { cn, formatCurrency, formatDate } from '@/lib/utils' import { getErrorMessage } from '@/lib/errors/get-error-message' import type { BokslutsbilagorReport } from '@/lib/reports/bokslutsbilagor-types' /** * The bokslutsbilagor pärm on screen: one row per balance account as of the * balansdag (booked, per underlag, difference, sign-off, files) with the PDF * export; each row links into the Avstämning page where the work is done. */ interface BokslutsbilagorViewProps { periodId: string } export function BokslutsbilagorView({ periodId }: BokslutsbilagorViewProps) { const t = useTranslations('bokslutsbilagor') const locale = useLocale() const [report, setReport] = useState(null) const [loading, setLoading] = useState(true) const [error, setError] = useState(null) // Keyed on periodId by the caller: a new period is a fresh mount, so the // initial loading state is the reset. useEffect(() => { let cancelled = false fetch(`/api/reports/bokslutsbilagor?period_id=${encodeURIComponent(periodId)}`) .then(async (res) => { const json = await res.json().catch(() => ({})) if (cancelled) return if (!res.ok) { setError(getErrorMessage(json, { statusCode: res.status })) return } setReport(json.data as BokslutsbilagorReport) }) .catch(() => { if (!cancelled) setError(t('load_failed')) }) .finally(() => { if (!cancelled) setLoading(false) }) return () => { cancelled = true } }, [periodId, t]) if (loading) { return (
) } if (error) { return ( {error} ) } if (!report || report.accounts.length === 0) { return } const money = (n: number | null) => (n == null ? '-' : formatCurrency(n, 'SEK')) const externalLabel = (a: BokslutsbilagorReport['accounts'][number]) => (locale === 'en' ? a.external_label_en : a.external_label_sv) return (

{t('summary', { accounts: report.summary.accounts, signed: report.summary.signed_on_balansdag, files: report.summary.attachments, date: formatDate(report.period.end), })} {' · '} {t('checklist_summary', { done: report.checklist.summary.done + report.checklist.summary.not_applicable, total: report.checklist.summary.total })}

{report.accounts.map((a) => { const files = a.attachments.filter((f) => !f.removed_at).length return ( ) })}
{t('col_account')} {t('col_booked')} {t('col_external')} {t('col_difference')} {t('col_signoff')} {t('col_files')}
{a.account_number} {a.name}
{externalLabel(a)}
{money(a.closing_balance)} {money(a.external_balance)} = 0.005 && 'text-warning', )} data-ph-mask > {money(a.difference)} {a.signoff ? ( {a.signoff.on_balansdag ? t('signed', { who: a.signoff.signed_by_label, when: formatDate(a.signoff.signed_at) }) : t('signed_other_date', { date: formatDate(a.signoff.through_date), who: a.signoff.signed_by_label })} ) : ( {t('unsigned')} )} {files}
) }