feat(bokslut): the bokslut trio in the flat concept language (scenes 34-36) (#1161)
* feat(bokslut): Arsbokslut hub as Stegen with de-boxed linear steps (trio 1/3) Scene 34: the six-step wizard gets the house horizontal stepper (done checks behind the current step, click-back navigation, forward stays with each step's continue action), the period select moves into the header as the context control, and the progress-bar and picker cards die. Preflight, Preview, Execute and Result are de-boxed: sans eyebrow sections with hairlines, quiet action links, attn lines instead of boxed alerts, muted text for normal states. Step content is centered at reading width. Accruals/Dispositions keep their internals for now (interactive panels; follow-up pass). All wizard logic untouched. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(bokslut): INK2 and NE-bilaga views in the flat house language (trio 2/3) Scene 36 direction: the declaration views lose every card. Statutory sections (Tillgangar, Eget kapital och skulder, Resultatrakning, INK2S, Intakter, Kostnader) become sans eyebrow sections with hairlines, the header card becomes a flat block with the SRU download beside it, the filing instructions lose their info box, warnings render as attn lines, and the NE R11 result becomes the emphasized document-foot row. All ruta tables, SRU downloads and warnings logic untouched. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(bokslut): Arsredovisning page, studio and digital filing in the flat house language (trio 3/3) De-boxes the whole scene-35 surface: the page shell (period picker, K3 note, narrativ, flerarsoversikt, underskrifter, PDF section), the AnnualReportStudio (workflow strip, scope form, completeness checks, versions) and DigitalInlamning (iXBRL review, submission form, status history) all move from Card shells to sans-eyebrow sections with hairlines. Inner info boxes flatten to bordered text blocks; the digital-checks warning becomes an attn line. The iframe border and the Kommer snart overlay sign stay: one frames an external document, the other floats above blurred content. Statutory Swedish-only surface: no new i18n keys. * polish(bokslut): align trio controls with the house language Founder feedback: buttons and selects still read old-style next to the bookkeeping page. Sweeps all trio surfaces: - Drop every min-h-11 override on Buttons and Inputs (44px chunky controls) so the compact house pill and h-10 input apply. - Replace all native <select> elements (BooleanQuestion, currency, group size, signing method, signer role, versions, AGM outcome, roll) with the shadcn Select primitive used system-wide. - Step navigation matches the merged Moms Stegen: white outline size-sm pills labelled 'Nästa: <steg> →' and '← Tillbaka'. Booking commits (Verkställ, Bokför valda dispositioner) stay primary. - Year-end period picker becomes the ContextPicker chip (the house context-picker idiom), and the stepper row widens to max-w-4xl so step 6 'Klart' no longer clips. - font-sans on two uppercase h3 eyebrows that rendered serif via the global h1-h3 display rule. * fix(vat): one toolbar row and the FyPicker chip on every fiscal-year surface The prod momsdeklaration (helårsmoms) broke into two sparse rows: Exportera alone, then a labelled 280px FiscalYearSelector below it, plus a serif uppercase worklist heading and internal check codes in the UI. - The VAT toolbar is one flat row: periodicity picker, fiscal-year chip, black Exportera, all h-9 aligned. - FyPicker (the rounded ContextPicker chip from UI-migration PR 3) replaces FiscalYearSelector on every page-level surface it had left: the VAT toolbar, FocusedReport's header (all report detail pages) and Kassaflödesanalys. Dialog/settings forms keep the labelled select, which is a form field, not a context picker. - 'Verifikationer som saknar basbelopp' becomes a sans eyebrow with hairline; internal codes (RC_BASIS_MISSING et al) no longer render in check rows, the Swedish message already cites the SKV felkod. - font-sans on the SkatteverketPanel validation eyebrow. * polish(vat): periodicity, year and quarter/month pickers as ContextPicker chips Founder feedback: the Arsvis dropdown should also be the rounded style. The cadence choice now lives behind a settings-style 'Period' chip (Manadsvis/Kvartalsvis/Arsvis with a check on the active one), and the year and quarter/month selects become chips too, so the whole momsdeklaration toolbar is chip-shaped: Period, 2026, Kvartal 3 (jul-sep) or Rakenskapsar 2026, then the black Exportera pill. The concrete period chip already implies the cadence, so the Period chip can stay label-only. --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
49e86e2e67
commit
98e1a48c2b
@@ -11,7 +11,7 @@ import { EmptyState } from '@/components/ui/empty-state'
|
||||
import { Card, CardContent } from '@/components/ui/card'
|
||||
import { Skeleton } from '@/components/ui/skeleton'
|
||||
import { useCompany } from '@/contexts/CompanyContext'
|
||||
import { FiscalYearSelector } from '@/components/common/FiscalYearSelector'
|
||||
import { FyPicker } from '@/components/common/FyPicker'
|
||||
import { ReportDateRange, type DateRangeValue } from '@/components/common/ReportDateRange'
|
||||
import { DimensionFilter, type DimensionFilterValue } from '@/components/reports/DimensionFilter'
|
||||
import { DATE_RANGE_SLUGS, DIMENSION_FILTER_SLUGS, getReport } from '@/lib/reports/catalog'
|
||||
@@ -117,7 +117,7 @@ function FocusedReportInner({
|
||||
title={reportName}
|
||||
action={
|
||||
isStandalone ? undefined : (
|
||||
<FiscalYearSelector
|
||||
<FyPicker
|
||||
value={selectedPeriod || null}
|
||||
onChange={(id, period) => {
|
||||
setSelectedPeriod(id || '')
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect, useState } from 'react'
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Skeleton } from '@/components/ui/skeleton'
|
||||
import { Table, TableBody, TableHead, TableHeader, TableRow } from '@/components/ui/table'
|
||||
@@ -97,26 +96,22 @@ export function INK2DeclarationView({ periodId }: { periodId: string }) {
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<Card>
|
||||
<CardContent className="p-6 flex flex-wrap items-center gap-3 text-destructive">
|
||||
<AlertCircle className="h-4 w-4 shrink-0" />
|
||||
<span className="text-sm">{error}</span>
|
||||
<Button variant="outline" onClick={() => setRetryKey((k) => k + 1)}>
|
||||
Försök igen
|
||||
</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<div className="flex flex-wrap items-center gap-3 py-4 text-destructive">
|
||||
<AlertCircle className="h-4 w-4 shrink-0" />
|
||||
<span className="text-sm">{error}</span>
|
||||
<Button variant="outline" onClick={() => setRetryKey((k) => k + 1)}>
|
||||
Försök igen
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (loading && !data) {
|
||||
return (
|
||||
<Card>
|
||||
<CardContent className="p-6 space-y-4">
|
||||
<Skeleton className="h-5 w-48" />
|
||||
<Skeleton className="h-64" />
|
||||
</CardContent>
|
||||
</Card>
|
||||
<div className="space-y-4">
|
||||
<Skeleton className="h-5 w-48" />
|
||||
<Skeleton className="h-64" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -127,11 +122,10 @@ export function INK2DeclarationView({ periodId }: { periodId: string }) {
|
||||
className={`space-y-8 transition-opacity duration-150 ${loading ? 'opacity-60' : ''}`}
|
||||
>
|
||||
{/* Header: company, year, filing artifact, and how to file it */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<div className="px-1">
|
||||
<div className="flex flex-wrap items-start justify-between gap-3">
|
||||
<div>
|
||||
<CardTitle className="text-base">INK2 (Aktiebolag)</CardTitle>
|
||||
<h3 className="font-sans text-sm font-medium">INK2 (Aktiebolag)</h3>
|
||||
<p className="text-sm text-muted-foreground mt-1">
|
||||
{data.companyInfo.companyName} · {data.fiscalYear.name}
|
||||
{data.companyInfo.orgNumber && ` · Org.nr: ${data.companyInfo.orgNumber}`}
|
||||
@@ -148,11 +142,9 @@ export function INK2DeclarationView({ periodId }: { periodId: string }) {
|
||||
{downloadError}
|
||||
</div>
|
||||
)}
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="flex items-start gap-2 rounded-lg border border-border bg-muted/30 p-3">
|
||||
<Info className="h-4 w-4 mt-1 shrink-0 text-muted-foreground" />
|
||||
<div className="text-sm text-muted-foreground space-y-2">
|
||||
<div className="mt-4 flex items-start gap-2">
|
||||
<Info className="mt-1 h-4 w-4 shrink-0 text-muted-foreground" aria-hidden="true" />
|
||||
<div className="text-[13px] leading-6 text-muted-foreground space-y-2">
|
||||
<p>
|
||||
INK2 visar det bokföringsmässiga resultatet baserat på din bokföring.
|
||||
</p>
|
||||
@@ -172,35 +164,31 @@ export function INK2DeclarationView({ periodId }: { periodId: string }) {
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* Warnings */}
|
||||
{data.warnings.length > 0 && (
|
||||
<Card>
|
||||
<CardContent className="py-4">
|
||||
<div className="flex items-start gap-2">
|
||||
<AlertTriangle className="h-4 w-4 mt-1 shrink-0 text-warning" />
|
||||
<div className="space-y-1">
|
||||
<p className="text-sm font-medium">
|
||||
{data.warnings.length}{' '}
|
||||
{data.warnings.length === 1 ? 'varning' : 'varningar'}
|
||||
</p>
|
||||
{data.warnings.map((warning, i) => (
|
||||
<p key={i} className="text-sm text-muted-foreground">{warning}</p>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<div className="flex items-start gap-2 px-1">
|
||||
<AlertTriangle className="mt-0.5 h-4 w-4 shrink-0 text-attn" aria-hidden="true" />
|
||||
<div className="space-y-1">
|
||||
<p className="text-[12.5px] font-medium leading-5 text-attn">
|
||||
{data.warnings.length}{' '}
|
||||
{data.warnings.length === 1 ? 'varning' : 'varningar'}
|
||||
</p>
|
||||
{data.warnings.map((warning, i) => (
|
||||
<p key={i} className="text-[12.5px] leading-5 text-muted-foreground">{warning}</p>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Assets section */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-base">Tillgångar</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<section>
|
||||
<div className="mb-1 flex items-center gap-2 px-1">
|
||||
<h3 className="font-sans text-xs font-medium uppercase tracking-wider text-muted-foreground">Tillgångar</h3>
|
||||
<div className="h-px flex-1 bg-border/60" />
|
||||
</div>
|
||||
<div className="px-1 pt-2">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
@@ -228,15 +216,16 @@ export function INK2DeclarationView({ periodId }: { periodId: string }) {
|
||||
</tr>
|
||||
</tfoot>
|
||||
</Table>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Equity & Liabilities section */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-base">Eget kapital och skulder</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<section>
|
||||
<div className="mb-1 flex items-center gap-2 px-1">
|
||||
<h3 className="font-sans text-xs font-medium uppercase tracking-wider text-muted-foreground">Eget kapital och skulder</h3>
|
||||
<div className="h-px flex-1 bg-border/60" />
|
||||
</div>
|
||||
<div className="px-1 pt-2">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
@@ -264,15 +253,16 @@ export function INK2DeclarationView({ periodId }: { periodId: string }) {
|
||||
</tr>
|
||||
</tfoot>
|
||||
</Table>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Income Statement section */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-base">Resultaträkning</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<section>
|
||||
<div className="mb-1 flex items-center gap-2 px-1">
|
||||
<h3 className="font-sans text-xs font-medium uppercase tracking-wider text-muted-foreground">Resultaträkning</h3>
|
||||
<div className="h-px flex-1 bg-border/60" />
|
||||
</div>
|
||||
<div className="px-1 pt-2">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
@@ -314,15 +304,16 @@ export function INK2DeclarationView({ periodId }: { periodId: string }) {
|
||||
</tr>
|
||||
</tfoot>
|
||||
</Table>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* INK2S summary */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-base">INK2S: Skattemässiga justeringar</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<section>
|
||||
<div className="mb-1 flex items-center gap-2 px-1">
|
||||
<h3 className="font-sans text-xs font-medium uppercase tracking-wider text-muted-foreground">INK2S: Skattemässiga justeringar</h3>
|
||||
<div className="h-px flex-1 bg-border/60" />
|
||||
</div>
|
||||
<div className="px-1 pt-2">
|
||||
<p className="text-sm text-muted-foreground mb-4">
|
||||
Grundläggande justeringar och de återläggningar som sparats i bokslutet
|
||||
beräknas automatiskt. Kontrollera övriga deklarationsjusteringar med din
|
||||
@@ -384,8 +375,8 @@ export function INK2DeclarationView({ periodId }: { periodId: string }) {
|
||||
</tr>
|
||||
</tfoot>
|
||||
</Table>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect, useState } from 'react'
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Skeleton } from '@/components/ui/skeleton'
|
||||
import { Table, TableBody, TableHead, TableHeader, TableRow } from '@/components/ui/table'
|
||||
@@ -108,26 +107,22 @@ export function NEDeclarationView({ periodId }: { periodId: string }) {
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<Card>
|
||||
<CardContent className="p-6 flex flex-wrap items-center gap-3 text-destructive">
|
||||
<AlertCircle className="h-4 w-4 shrink-0" />
|
||||
<span className="text-sm">{error}</span>
|
||||
<Button variant="outline" onClick={() => setRetryKey((k) => k + 1)}>
|
||||
Försök igen
|
||||
</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<div className="flex flex-wrap items-center gap-3 py-4 text-destructive">
|
||||
<AlertCircle className="h-4 w-4 shrink-0" />
|
||||
<span className="text-sm">{error}</span>
|
||||
<Button variant="outline" onClick={() => setRetryKey((k) => k + 1)}>
|
||||
Försök igen
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (loading && !data) {
|
||||
return (
|
||||
<Card>
|
||||
<CardContent className="p-6 space-y-4">
|
||||
<Skeleton className="h-5 w-48" />
|
||||
<Skeleton className="h-64" />
|
||||
</CardContent>
|
||||
</Card>
|
||||
<div className="space-y-4">
|
||||
<Skeleton className="h-5 w-48" />
|
||||
<Skeleton className="h-64" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -138,11 +133,10 @@ export function NEDeclarationView({ periodId }: { periodId: string }) {
|
||||
className={`space-y-8 transition-opacity duration-150 ${loading ? 'opacity-60' : ''}`}
|
||||
>
|
||||
{/* Header: company, year, and the filing artifact */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<div className="px-1">
|
||||
<div className="flex flex-wrap items-start justify-between gap-3">
|
||||
<div>
|
||||
<CardTitle className="text-base">NE-bilaga (Enskild firma)</CardTitle>
|
||||
<h3 className="font-sans text-sm font-medium">NE-bilaga (Enskild firma)</h3>
|
||||
<p className="text-sm text-muted-foreground mt-1">
|
||||
{data.companyInfo.companyName} · {data.fiscalYear.name}
|
||||
{data.companyInfo.orgNumber && ` · Org.nr: ${data.companyInfo.orgNumber}`}
|
||||
@@ -159,35 +153,31 @@ export function NEDeclarationView({ periodId }: { periodId: string }) {
|
||||
{downloadError}
|
||||
</div>
|
||||
)}
|
||||
</CardHeader>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* Warnings */}
|
||||
{data.warnings.length > 0 && (
|
||||
<Card>
|
||||
<CardContent className="py-4">
|
||||
<div className="flex items-start gap-2">
|
||||
<AlertTriangle className="h-4 w-4 mt-1 shrink-0 text-warning" />
|
||||
<div className="space-y-1">
|
||||
<p className="text-sm font-medium">
|
||||
{data.warnings.length}{' '}
|
||||
{data.warnings.length === 1 ? 'varning' : 'varningar'}
|
||||
</p>
|
||||
{data.warnings.map((warning, i) => (
|
||||
<p key={i} className="text-sm text-muted-foreground">{warning}</p>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<div className="flex items-start gap-2 px-1">
|
||||
<AlertTriangle className="mt-0.5 h-4 w-4 shrink-0 text-attn" aria-hidden="true" />
|
||||
<div className="space-y-1">
|
||||
<p className="text-[12.5px] font-medium leading-5 text-attn">
|
||||
{data.warnings.length}{' '}
|
||||
{data.warnings.length === 1 ? 'varning' : 'varningar'}
|
||||
</p>
|
||||
{data.warnings.map((warning, i) => (
|
||||
<p key={i} className="text-[12.5px] leading-5 text-muted-foreground">{warning}</p>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Revenue section */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-base">Intäkter</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<section>
|
||||
<div className="mb-1 flex items-center gap-2 px-1">
|
||||
<h3 className="font-sans text-xs font-medium uppercase tracking-wider text-muted-foreground">Intäkter</h3>
|
||||
<div className="h-px flex-1 bg-border/60" />
|
||||
</div>
|
||||
<div className="px-1 pt-2">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
@@ -217,15 +207,16 @@ export function NEDeclarationView({ periodId }: { periodId: string }) {
|
||||
</tr>
|
||||
</tfoot>
|
||||
</Table>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Expenses section */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-base">Kostnader</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<section>
|
||||
<div className="mb-1 flex items-center gap-2 px-1">
|
||||
<h3 className="font-sans text-xs font-medium uppercase tracking-wider text-muted-foreground">Kostnader</h3>
|
||||
<div className="h-px flex-1 bg-border/60" />
|
||||
</div>
|
||||
<div className="px-1 pt-2">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
@@ -261,27 +252,21 @@ export function NEDeclarationView({ periodId }: { periodId: string }) {
|
||||
</tr>
|
||||
</tfoot>
|
||||
</Table>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Result */}
|
||||
<Card>
|
||||
<CardContent className="py-4">
|
||||
<div className="flex justify-between items-center">
|
||||
<div>
|
||||
<span className="font-mono text-xs bg-muted px-1 rounded mr-2">R11</span>
|
||||
<span className="font-display text-xl">Årets resultat</span>
|
||||
</div>
|
||||
<span
|
||||
className={`font-display text-2xl tabular-nums ${
|
||||
data.rutor.R11 >= 0 ? 'text-success' : 'text-destructive'
|
||||
}`}
|
||||
>
|
||||
{formatWholeKronor(data.rutor.R11)}
|
||||
</span>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
{/* Result: the emphasized document foot (house skv-foot idiom). */}
|
||||
<div className="flex items-baseline gap-3 border-t-2 border-foreground/80 px-1 pt-3">
|
||||
<span className="font-mono text-xs text-muted-foreground">R11</span>
|
||||
<span className="flex-1 text-sm font-medium">Årets resultat</span>
|
||||
<span
|
||||
className={`text-sm font-semibold tabular-nums ${
|
||||
data.rutor.R11 >= 0 ? '' : 'text-destructive'
|
||||
}`}
|
||||
>
|
||||
{formatWholeKronor(data.rutor.R11)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -946,7 +946,7 @@ function SkatteverketPanelInner({
|
||||
{/* Validation results from Skatteverket */}
|
||||
{kontroller.length > 0 && (
|
||||
<div className="space-y-2">
|
||||
<h3 className="text-sm font-medium uppercase tracking-wider text-muted-foreground">
|
||||
<h3 className="font-sans text-xs font-medium uppercase tracking-wider text-muted-foreground">
|
||||
Skatteverkets valideringsresultat
|
||||
</h3>
|
||||
{kontroller.map((k, i) => (
|
||||
|
||||
@@ -316,10 +316,7 @@ export function VatChecksCard({
|
||||
) : (
|
||||
<AlertTriangle className="mt-0.5 h-4 w-4 shrink-0 text-attn" aria-hidden="true" />
|
||||
)}
|
||||
<div>
|
||||
<span className="mr-2 font-mono text-xs text-muted-foreground">{c.code}</span>
|
||||
{c.message}
|
||||
</div>
|
||||
<div>{c.message}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
@@ -327,12 +324,15 @@ export function VatChecksCard({
|
||||
|
||||
{showGapWorklist && (
|
||||
<div className="space-y-3">
|
||||
<h3
|
||||
className="text-sm font-medium uppercase tracking-wider text-muted-foreground"
|
||||
aria-live="polite"
|
||||
>
|
||||
Verifikationer som saknar basbelopp ({gaps.length})
|
||||
</h3>
|
||||
<div className="mb-1 flex items-center gap-2">
|
||||
<h3
|
||||
className="font-sans text-xs font-medium uppercase tracking-wider text-muted-foreground"
|
||||
aria-live="polite"
|
||||
>
|
||||
Verifikationer som saknar basbelopp ({gaps.length})
|
||||
</h3>
|
||||
<div className="h-px flex-1 bg-border/60" />
|
||||
</div>
|
||||
|
||||
{gapsLoading ? (
|
||||
<div className="flex items-center gap-2 text-sm text-muted-foreground">
|
||||
|
||||
@@ -21,7 +21,8 @@ import {
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from '@/components/ui/select'
|
||||
import { FiscalYearSelector } from '@/components/common/FiscalYearSelector'
|
||||
import { FyPicker } from '@/components/common/FyPicker'
|
||||
import { ContextPicker } from '@/components/common/ContextPicker'
|
||||
import { cn, formatDate } from '@/lib/utils'
|
||||
import { roundOre } from '@/lib/money'
|
||||
import { formatVoucher } from '@/lib/bookkeeping/voucher-series-resolver'
|
||||
@@ -1671,35 +1672,29 @@ export function VatDeclarationView() {
|
||||
return (
|
||||
<VatDrillContext.Provider value={{ fiscalPeriodId: isYearly ? fiscalPeriodId : undefined }}>
|
||||
<div className="space-y-8">
|
||||
{/* XML and PDF live in "Lämna in" below: they are filing artifacts, not
|
||||
report exports, and one home avoids two competing download surfaces. */}
|
||||
<ReportExportMenu
|
||||
variant="default"
|
||||
items={[
|
||||
{ format: 'xlsx', href: `/api/reports/vat-declaration/xlsx?${vatQueryString()}` },
|
||||
]}
|
||||
/>
|
||||
|
||||
{/* Period selection — the declaration below follows it automatically.
|
||||
Flat toolbar row (concept language), far right like a context picker. */}
|
||||
{/* One flat toolbar row (concept language): period pickers + Exportera
|
||||
far right. XML and PDF live in "Lämna in" below: they are filing
|
||||
artifacts, not report exports, and one home avoids two competing
|
||||
download surfaces. */}
|
||||
<div className="flex flex-wrap items-center justify-end gap-2">
|
||||
<Select
|
||||
{/* Cadence lives behind a settings-style "Period" chip: the
|
||||
concrete period chip next to it already shows the cadence
|
||||
("Kvartal 2" implies quarterly, "Räkenskapsår ..." yearly). */}
|
||||
<ContextPicker
|
||||
items={[
|
||||
{ id: 'monthly', label: 'Månadsvis' },
|
||||
{ id: 'quarterly', label: 'Kvartalsvis' },
|
||||
{ id: 'yearly', label: 'Årsvis' },
|
||||
]}
|
||||
value={periodType}
|
||||
onValueChange={(value) => handlePeriodTypeChange(value as VatPeriodType)}
|
||||
>
|
||||
<SelectTrigger aria-label="Periodicitet" className="h-9 w-36">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="monthly">Månadsvis</SelectItem>
|
||||
<SelectItem value="quarterly">Kvartalsvis</SelectItem>
|
||||
<SelectItem value="yearly">Årsvis</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
onChange={(value) => handlePeriodTypeChange(value as VatPeriodType)}
|
||||
triggerLabel="Period"
|
||||
ariaLabel="Periodicitet"
|
||||
/>
|
||||
{isYearly ? (
|
||||
// Annual VAT covers a räkenskapsår: picked here, not a
|
||||
// calendar year.
|
||||
<FiscalYearSelector
|
||||
<FyPicker
|
||||
value={fiscalPeriodId || null}
|
||||
onChange={(id, fp) => {
|
||||
setFiscalPeriodId(id || '')
|
||||
@@ -1710,35 +1705,35 @@ export function VatDeclarationView() {
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<Select value={String(year)} onValueChange={(value) => setYear(parseInt(value))}>
|
||||
<SelectTrigger aria-label="År" className="h-9 w-24 tabular-nums">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{yearOptions.map((y) => (
|
||||
<SelectItem key={y} value={String(y)} className="tabular-nums">
|
||||
{y}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<Select
|
||||
<ContextPicker
|
||||
items={yearOptions.map((y) => ({ id: String(y), label: String(y) }))}
|
||||
value={String(year)}
|
||||
onChange={(value) => setYear(parseInt(value))}
|
||||
triggerLabel={String(year)}
|
||||
ariaLabel="År"
|
||||
className="tabular-nums"
|
||||
/>
|
||||
<ContextPicker
|
||||
items={getPeriodOptions().map((opt) => ({
|
||||
id: String(opt.value),
|
||||
label: opt.label,
|
||||
}))}
|
||||
value={String(period)}
|
||||
onValueChange={(value) => setPeriod(parseInt(value))}
|
||||
>
|
||||
<SelectTrigger aria-label="Period" className="h-9 w-44">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{getPeriodOptions().map((opt) => (
|
||||
<SelectItem key={opt.value} value={String(opt.value)}>
|
||||
{opt.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
onChange={(value) => setPeriod(parseInt(value))}
|
||||
triggerLabel={
|
||||
getPeriodOptions().find((opt) => opt.value === period)?.label ??
|
||||
String(period)
|
||||
}
|
||||
ariaLabel="Redovisningsperiod"
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<ReportExportMenu
|
||||
variant="default"
|
||||
items={[
|
||||
{ format: 'xlsx', href: `/api/reports/vat-declaration/xlsx?${vatQueryString()}` },
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{error && (
|
||||
|
||||
Reference in New Issue
Block a user