* feat(reports,settings): report library + focused report routes, settings modal Reports - Replace the monolithic /reports tab-switcher with a calm, grouped report library landing (ReportLibrary + RecentReportsShelf) driven by a new lib/reports/catalog.ts. - Each report opens a focused /reports/[slug] route (FocusedReport) with a shared fiscal-year selector, optional date-range, and URL-based account drill-down into the general ledger. - Extract every report view into components/reports/views, add a reusable ReportExportMenu, and remove the old ReportsNav. Settings - Add an intercepting @settingsModal parallel route so in-app navigation to /settings opens as a modal over the current page; hard loads still resolve to the full page. - Share one SettingsShell (rail + content) between page and modal, extract each section into components/settings/sections/*Content, add a settings hotkey and command-palette entry, and remove the old SettingsSidebar. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(reports,settings): remove dead salary-journal entry, fix border token Addresses PR review feedback (#629): - Remove the unreachable `salary-journal` report from the catalog. It had needsEmployees + no route + no FocusedView handler and `hasEmployees` was never plumbed through, so it never appeared in the library and a direct /reports/salary-journal URL rendered a blank frame. The report was never on the old page and has no view component; the API + generator stay in place for a proper follow-up. Drops its two now-unused i18n keys. - Replace opacity-suffixed `border-border/8` section dividers with full-opacity `border-border` across the extracted settings section components, per the design system (no opacity-suffixed border tokens on surfaces). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci: re-trigger checks (pg-real hit a Docker Hub registry timeout) --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
95 lines
3.2 KiB
TypeScript
95 lines
3.2 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 { Card, CardContent } from '@/components/ui/card'
|
|
import { PageHeader } from '@/components/ui/page-header'
|
|
import { EmptyState } from '@/components/ui/empty-state'
|
|
import { useCompany } from '@/contexts/CompanyContext'
|
|
import { FiscalYearSelector } from '@/components/common/FiscalYearSelector'
|
|
import { ReportLibrary } from '@/components/reports/ReportLibrary'
|
|
import { RecentReportsShelf } from '@/components/reports/RecentReportsShelf'
|
|
import { useRecentReports } from '@/components/reports/useRecentReports'
|
|
import { getReport } from '@/lib/reports/catalog'
|
|
|
|
/**
|
|
* Reports library landing. A calm, grouped index of every report — selecting
|
|
* one opens the focused /reports/[slug] route. The fiscal year picked here
|
|
* persists (FiscalYearSelector 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 t = useTranslations('reports')
|
|
const { recents, 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')}
|
|
action={
|
|
<FiscalYearSelector
|
|
value={selectedPeriod || null}
|
|
onChange={(id) => setSelectedPeriod(id || '')}
|
|
includeAllOption={false}
|
|
hideFuturePeriods
|
|
onReady={() => setIsLoadingInit(false)}
|
|
/>
|
|
}
|
|
/>
|
|
|
|
{isLoadingInit ? (
|
|
<div className="space-y-6">
|
|
<Skeleton className="h-4 w-40" />
|
|
<Card>
|
|
<CardContent className="p-6 space-y-4">
|
|
<Skeleton className="h-5 w-32" />
|
|
<Skeleton className="h-64" />
|
|
</CardContent>
|
|
</Card>
|
|
</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"
|
|
/>
|
|
) : (
|
|
<div className="space-y-8">
|
|
<RecentReportsShelf
|
|
slugs={recents}
|
|
entityType={company?.entity_type}
|
|
onOpen={openReport}
|
|
/>
|
|
<ReportLibrary
|
|
entityType={company?.entity_type}
|
|
onOpen={openReport}
|
|
/>
|
|
</div>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|