Files
accounted/app/(dashboard)/reports/page.tsx
T
Jakob WennbergandClaude Fable 5 6911f657e9 feat(reports): catalog as one dry table with band groups and Senast öppnad (#1147)
The founder-picked Tabellen variant from the rest-of-nav 2 concept:
the report library becomes a single dry table where band rows carry the
accounting taxonomy, each report is one clickable line with its
description in muted ink, and a Senast oppnad column replaces the
recents shelf (RecentReportsShelf deleted). useRecentReports now stores
slug+timestamp pairs (legacy plain-slug entries parse as undated).
FiscalYearSelector swaps to the house FyPicker chip, help moves behind
the ? popover, catalog footnote as pgnote. Entity gating, dimension
gating, route-owning reports and the persisted FY choice all unchanged.

9341 tests pass, lint clean, guards pass. sv+en keys added.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 16:41:09 +02:00

99 lines
3.3 KiB
TypeScript

'use client'
import { useState } from 'react'
import { useRouter } from 'next/navigation'
import { useTranslations } from 'next-intl'
import { Skeleton } from '@/components/ui/skeleton'
import { PageHeader } from '@/components/ui/page-header'
import { HelpPopover } from '@/components/ui/help-popover'
import { EmptyState } from '@/components/ui/empty-state'
import { useCompany } from '@/contexts/CompanyContext'
import { useCompanySettings } from '@/components/settings/useSettings'
import { FyPicker } from '@/components/common/FyPicker'
import { ReportLibrary } from '@/components/reports/ReportLibrary'
import { useRecentReports } from '@/components/reports/useRecentReports'
import { getReport } from '@/lib/reports/catalog'
/**
* Reports catalog landing (concept "Tabellen"): one dry table grouped by
* accounting taxonomy, with a "Senast öppnad" column instead of a separate
* recents shelf. Selecting a report opens the focused /reports/[slug] route.
* The fiscal year picked here persists (FyPicker localStorage) and is
* restored on the focused page, so the choice carries across without URL
* plumbing.
*/
export default function ReportsPage() {
const router = useRouter()
const [selectedPeriod, setSelectedPeriod] = useState('')
const [isLoadingInit, setIsLoadingInit] = useState(true)
const { company } = useCompany()
const { settings } = useCompanySettings()
const t = useTranslations('reports')
const { openedAt, pushRecent } = useRecentReports(company?.id)
// Open a report. Route-owning reports (cash flow, annual report, KPI, SIE)
// navigate to their own page; the rest open the focused /reports/[slug] route.
const openReport = (slug: string) => {
const report = getReport(slug)
if (report?.route) {
const href =
slug === 'arsredovisning' && selectedPeriod
? `${report.route}?period=${selectedPeriod}`
: report.route
router.push(href)
return
}
pushRecent(slug)
router.push(`/reports/${slug}`)
}
return (
<div className="space-y-8">
<PageHeader
title={t('title')}
help={
<HelpPopover>
<p>{t('help_text')}</p>
</HelpPopover>
}
action={
<FyPicker
value={selectedPeriod || null}
onChange={(id) => setSelectedPeriod(id || '')}
includeAllOption={false}
hideFuturePeriods
onReady={() => setIsLoadingInit(false)}
/>
}
/>
{isLoadingInit ? (
<div className="space-y-3">
{[1, 2, 3, 4, 5, 6].map((i) => (
<Skeleton key={i} className="h-9 w-full" />
))}
</div>
) : !selectedPeriod ? (
<EmptyState
title="Inget räkenskapsår valt"
description="Skapa ett räkenskapsår för att kunna se rapporter."
actionLabel="Gå till inställningar"
actionHref="/settings"
/>
) : (
<>
<ReportLibrary
entityType={company?.entity_type}
dimensionsEnabled={settings?.dimensions_enabled === true}
openedAt={openedAt}
onOpen={openReport}
/>
<p className="px-1 text-xs leading-5 text-muted-foreground">
{t('catalog_footnote')}
</p>
</>
)}
</div>
)
}