'use client' import { useEffect, useMemo, useState } from 'react' import { useTranslations } from 'next-intl' import { AlertCircle, History } from 'lucide-react' import { Card, CardContent } from '@/components/ui/card' import { Skeleton } from '@/components/ui/skeleton' import { EmptyState } from '@/components/ui/empty-state' import { SegmentedControl } from '@/components/ui/segmented-control' import { ReportExportMenu } from '@/components/reports/ReportExportMenu' import { formatDateTime } from '@/lib/utils' import { getErrorMessage } from '@/lib/errors/get-error-message' import type { DateRangeValue } from '@/components/common/ReportDateRange' import { BEHANDLINGSHISTORIK_CATEGORIES, type BehandlingshistorikCategory, type BehandlingshistorikReport, } from '@/lib/reports/behandlingshistorik-types' type CategoryFilter = 'all' | BehandlingshistorikCategory function buildQuery(periodId: string, dateRange: DateRangeValue): string { const params = new URLSearchParams({ period_id: periodId }) if (dateRange.fromDate) params.set('from_date', dateRange.fromDate) if (dateRange.toDate) params.set('to_date', dateRange.toDate) return params.toString() } /** * Behandlingshistorik report body (BFL 5 kap. 11 §). Event labels arrive in * Swedish from the read model (räkenskapsinformation); the chrome here * (headers, filter, counts) is translated. */ export function BehandlingshistorikView({ periodId, dateRange, }: { periodId: string dateRange: DateRangeValue }) { const t = useTranslations('reports') const [data, setData] = useState(null) const [loading, setLoading] = useState(false) const [error, setError] = useState(null) const [filter, setFilter] = useState('all') const query = buildQuery(periodId, dateRange) useEffect(() => { if (!periodId) return let cancelled = false const run = async () => { setLoading(true) setError(null) try { const res = await fetch(`/api/reports/behandlingshistorik?${query}`) const result = await res.json() if (cancelled) return if (!res.ok || result.error) { setError(getErrorMessage(result)) } else { setData(result.data) } } catch { if (!cancelled) setError(t('bh_error')) } finally { if (!cancelled) setLoading(false) } } run() return () => { cancelled = true } // t is stable for the mounted locale; the query string is the real input. // eslint-disable-next-line react-hooks/exhaustive-deps }, [periodId, query]) const visible = useMemo(() => { if (!data) return [] if (filter === 'all') return data.events return data.events.filter((e) => e.category === filter) }, [data, filter]) if (loading) { return ( {[1, 2, 3, 4, 5, 6].map((i) => ( ))} ) } if (error) { return ( {error} ) } if (!data || data.events.length === 0) { return } const filterOptions = [ { value: 'all' as CategoryFilter, label: t('bh_filter_all'), count: data.total_events }, ...BEHANDLINGSHISTORIK_CATEGORIES.filter((c) => data.by_category[c] > 0).map((c) => ({ value: c as CategoryFilter, label: t(`bh_cat_${c}`), count: data.by_category[c], })), ] return (

{t('bh_summary', { count: data.total_events })} · {data.range.from} {t('bh_range_to')} {data.range.to} {data.app_version ? ` · ${t('bh_version')}: ${data.app_version}` : ''}

{visible.map((e) => ( ))}
{t('bh_col_time')} {t('bh_col_event')} {t('bh_col_actor')} {t('bh_col_details')}
{formatDateTime(e.occurred_at)}
{e.event}
{e.object && (
{e.object}
)}
{e.actor.label} {e.details.length > 0 && (
    {e.details.map((d, i) => (
  • {d}
  • ))}
)}
) }