ff01640f60
* 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>
56 lines
2.0 KiB
TypeScript
56 lines
2.0 KiB
TypeScript
'use client'
|
|
|
|
import { Suspense } from 'react'
|
|
import { SettingsRail } from './SettingsRail'
|
|
import { SettingsLoadingSkeleton } from './SettingsLoadingSkeleton'
|
|
import { SETTINGS_SECTIONS } from './sections'
|
|
|
|
interface SettingsShellProps {
|
|
variant: 'page' | 'modal'
|
|
/** Resolved section id. Required for the modal (drives which content renders);
|
|
* optional for the page where `{children}` is the route's own content. */
|
|
activeSection?: string
|
|
children?: React.ReactNode
|
|
}
|
|
|
|
/**
|
|
* Shared two-pane settings layout (category rail + content). The full-page
|
|
* route renders it via `settings/layout.tsx` (content = the route's children);
|
|
* the routed modal renders it inside a Dialog (content resolved from the
|
|
* section map). Keeping one shell means page and modal stay visually identical.
|
|
*/
|
|
export function SettingsShell({ variant, activeSection, children }: SettingsShellProps) {
|
|
if (variant === 'modal') {
|
|
const Section = activeSection ? SETTINGS_SECTIONS[activeSection] : undefined
|
|
return (
|
|
<div className="flex min-h-0 flex-1">
|
|
<aside className="hidden w-56 shrink-0 overflow-y-auto border-r border-border p-3 md:block">
|
|
<SettingsRail variant="modal" display="rail" activeId={activeSection} />
|
|
</aside>
|
|
<div className="min-h-0 flex-1 overflow-y-auto p-6">
|
|
<div className="mb-6 md:hidden">
|
|
<SettingsRail variant="modal" display="select" activeId={activeSection} />
|
|
</div>
|
|
<Suspense fallback={<SettingsLoadingSkeleton />}>
|
|
{Section ? <Section /> : null}
|
|
</Suspense>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<div className="grid gap-8 md:grid-cols-[220px_1fr]">
|
|
<aside className="md:sticky md:top-8 md:self-start">
|
|
<div className="mb-4 md:hidden">
|
|
<SettingsRail variant="page" display="select" />
|
|
</div>
|
|
<div className="hidden md:block">
|
|
<SettingsRail variant="page" display="rail" />
|
|
</div>
|
|
</aside>
|
|
<div className="min-w-0">{children}</div>
|
|
</div>
|
|
)
|
|
}
|