UI badge cleanup + /chart-of-accounts route + loop skills (#850)

Bundles three separable concerns:

- style(ui): badge audit + cleanup across 45 files — real-status chips use Badge variants (raw Tailwind colors dropped), non-status count/type/label chips demoted to muted text, clustered badges consolidated; 20 unused imports removed.
- feat(bookkeeping): Kontoplan moved to a dedicated /chart-of-accounts route (nav + command palette wired); /bookkeeping shows the journal list only.
- chore(skills): loop-* automation skills + design-scan workflow under .claude/.

fix(reports): restored the destructive count badge on blocking errors in the periodisk sammanställning (EC Sales List) — a genuine status cue the audit had wrongly flattened; flagged by the PR reviewer and Swedish compliance bot, now clean.

All CI green; compliance bots report no findings.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jakob Wennberg
2026-07-01 14:29:23 +02:00
committed by GitHub
co-authored by Claude Opus 4.8
parent d63d2aecf0
commit 2da9c71eb3
57 changed files with 613 additions and 315 deletions
@@ -3,7 +3,6 @@
import { useState, useEffect, useMemo, useCallback } from 'react'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Badge } from '@/components/ui/badge'
import { useToast } from '@/components/ui/use-toast'
import {
Dialog,
@@ -193,17 +192,14 @@ export default function BookingTemplatePicker({ onApply, entityType, defaultAmou
>
<div className="flex items-start justify-between gap-2">
<div className="flex-1 min-w-0">
<div className="flex items-center gap-2">
<span className="font-medium text-sm">{t.name}</span>
<Badge variant="secondary" className="text-[10px] px-1.5 py-0 shrink-0">
<ScopeIcon className="h-3 w-3 mr-0.5" />
<div className="font-medium text-sm">{t.name}</div>
<div className="flex items-center gap-1 text-[11px] text-muted-foreground mt-0.5">
<ScopeIcon className="h-3 w-3 shrink-0" />
<span>
{SCOPE_LABELS[scope]}
</Badge>
{t.entity_type !== 'all' && (
<Badge variant="outline" className="text-[10px] px-1.5 py-0 shrink-0">
{t.entity_type === 'enskild_firma' ? 'EF' : 'AB'}
</Badge>
)}
{t.entity_type !== 'all' &&
` · ${t.entity_type === 'enskild_firma' ? 'EF' : 'AB'}`}
</span>
</div>
{t.description && (
<p className="text-xs text-muted-foreground mt-0.5 line-clamp-2">
+11 -14
View File
@@ -3,7 +3,6 @@
import { useState, useEffect } from 'react'
import { createClient } from '@/lib/supabase/client'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { ChevronDown, ChevronRight, Search } from 'lucide-react'
@@ -127,7 +126,7 @@ export default function ChartOfAccounts() {
<span className="font-semibold">
Klass {cls}: {CLASS_LABELS[classNum] || ''}
</span>
<Badge variant="secondary">{classAccounts.length} konton</Badge>
<span className="text-sm text-muted-foreground">{classAccounts.length} konton</span>
</div>
</button>
@@ -179,18 +178,16 @@ export default function ChartOfAccounts() {
</button>
)}
</td>
<td className="py-2 text-center">
<Badge variant="outline" className="text-xs">
{account.account_type === 'asset'
? 'Tillgång'
: account.account_type === 'liability'
? 'Skuld'
: account.account_type === 'equity'
? 'EK'
: account.account_type === 'revenue'
? 'Intäkt'
: 'Kostnad'}
</Badge>
<td className="py-2 text-center text-xs text-muted-foreground">
{account.account_type === 'asset'
? 'Tillgång'
: account.account_type === 'liability'
? 'Skuld'
: account.account_type === 'equity'
? 'EK'
: account.account_type === 'revenue'
? 'Intäkt'
: 'Kostnad'}
</td>
<td className="py-2 text-center text-xs text-muted-foreground">
{account.normal_balance === 'debit' ? 'Debet' : 'Kredit'}
@@ -3,7 +3,6 @@
import { useState, useEffect, useCallback, useMemo } from 'react'
import { useTranslations } from 'next-intl'
import { Card, CardContent } from '@/components/ui/card'
import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Switch } from '@/components/ui/switch'
@@ -283,9 +282,9 @@ export default function ChartOfAccountsManager() {
<TabsList>
<TabsTrigger value="my-accounts">
{t('tab_my_accounts')}
<Badge variant="secondary" className="ml-1.5 text-xs">
{accounts.length}
</Badge>
<span className="ml-1.5 text-xs text-muted-foreground tabular-nums">
({accounts.length})
</span>
</TabsTrigger>
<TabsTrigger value="bas-catalog">
<BookOpen className="mr-1.5 h-3.5 w-3.5" />
@@ -349,9 +348,9 @@ export default function ChartOfAccountsManager() {
<span className="font-semibold text-left">
{t('class_heading', { cls, label: classLabel(classNum) })}
</span>
<Badge variant="secondary" className="text-xs">
{activeCount}/{classAccounts.length}
</Badge>
<span className="text-xs text-muted-foreground tabular-nums">
{t('active_count_label', { active: activeCount, total: classAccounts.length })}
</span>
</div>
</button>
@@ -383,9 +382,9 @@ export default function ChartOfAccountsManager() {
<span className="flex items-center gap-1.5">
{account.account_name}
{account.is_system_account && (
<Badge variant="outline" className="text-[10px] px-1 py-0">
<span className="text-[10px] uppercase tracking-wider text-muted-foreground">
{t('system_badge')}
</Badge>
</span>
)}
</span>
</td>
@@ -395,9 +394,9 @@ export default function ChartOfAccountsManager() {
</span>
</td>
<td className="py-2 text-center">
<Badge variant="outline" className="text-xs">
<span className="text-xs text-muted-foreground">
{typeLabel(account.account_type)}
</Badge>
</span>
</td>
<td className="py-2 text-center">
<Switch
@@ -479,9 +478,9 @@ export default function ChartOfAccountsManager() {
<span className="font-semibold text-left">
{t('class_heading', { cls, label: classLabel(classNum) })}
</span>
<Badge variant="secondary" className="text-xs">
<span className="text-xs text-muted-foreground tabular-nums">
{t('active_count_label', { active: activatedCount, total: classAccounts.length })}
</Badge>
</span>
</div>
</button>
@@ -524,9 +523,9 @@ export default function ChartOfAccountsManager() {
</span>
</td>
<td className="py-2 text-center">
<Badge variant="outline" className="text-xs">
<span className="text-xs text-muted-foreground">
{typeLabel(account.account_type)}
</Badge>
</span>
</td>
<td className="py-2 text-right">
{account.is_activated ? (
@@ -11,7 +11,6 @@ import {
} from '@/components/ui/dialog'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Badge } from '@/components/ui/badge'
import AccountCombobox from '@/components/bookkeeping/AccountCombobox'
import CorrectionPreview from '@/components/bookkeeping/CorrectionPreview'
import { useToast } from '@/components/ui/use-toast'
@@ -166,9 +165,9 @@ export default function CorrectionEntryDialog({ entry, open, onOpenChange, onCor
{/* Original entry metadata — lines live inside CorrectionPreview below */}
<div className="space-y-1">
<div className="flex items-center gap-2 text-sm text-muted-foreground flex-wrap">
<span className="text-muted-foreground">Original</span>
<span className="font-mono">{formatVoucher(entry)}</span>
<span className="tabular-nums">{formatDate(entry.entry_date)}</span>
<Badge variant="outline" className="text-xs">Original</Badge>
</div>
<p className="text-sm">{entry.description}</p>
</div>
@@ -10,7 +10,6 @@ import {
DialogDescription,
DialogFooter,
} from '@/components/ui/dialog'
import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'
import { Skeleton } from '@/components/ui/skeleton'
import { Input } from '@/components/ui/input'
@@ -228,15 +227,13 @@ export default function InboxDocumentPicker({ open, onClose, journalEntryId, onL
<span className="text-sm font-medium truncate">
{it.supplier_name ?? it.file_name}
</span>
<Badge variant="outline" className="shrink-0 text-[10px]">
{sourceLabel}
</Badge>
</div>
<div className="flex items-center gap-2 text-xs text-muted-foreground tabular-nums">
{it.invoice_date && <span>{formatDate(it.invoice_date)}</span>}
{it.supplier_name && (
<span className="truncate font-normal">{it.file_name}</span>
)}
<span className="shrink-0">{sourceLabel}</span>
</div>
</div>
{it.amount != null && (
@@ -4,7 +4,6 @@ import { useCallback, useEffect, useMemo, useState } from 'react'
import Link from 'next/link'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { Button } from '@/components/ui/button'
import { Badge } from '@/components/ui/badge'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { Skeleton } from '@/components/ui/skeleton'
@@ -327,9 +326,7 @@ export function EfDeclarationSection({
<div className="flex-1">
<CardTitle className="text-base">{item.label}</CardTitle>
<p className="text-sm text-muted-foreground mt-1">{item.description}</p>
<Badge variant="outline" className="mt-2">
{item.ne_ruta}
</Badge>
<p className="text-xs text-muted-foreground mt-1">NE-ruta {item.ne_ruta}</p>
</div>
<p className="font-display text-2xl tabular-nums shrink-0">
{formatCurrency(item.amount)}