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:
co-authored by
Claude Opus 4.8
parent
d63d2aecf0
commit
2da9c71eb3
@@ -298,8 +298,9 @@ export function AgentMemoryPanel() {
|
||||
|
||||
<div className="flex-1 min-w-0 space-y-2">
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<Badge variant={kindBadge(row.kind)}>{KIND_LABEL[row.kind]}</Badge>
|
||||
<Badge variant="outline">{SOURCE_LABEL[row.source]}</Badge>
|
||||
<span className="text-[11px] text-muted-foreground">
|
||||
{KIND_LABEL[row.kind]} · {SOURCE_LABEL[row.source]}
|
||||
</span>
|
||||
{dimmed && <Badge variant="secondary">Dold</Badge>}
|
||||
</div>
|
||||
|
||||
@@ -388,16 +389,3 @@ export function AgentMemoryPanel() {
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
function kindBadge(kind: Kind): 'default' | 'secondary' | 'outline' | 'success' | 'warning' {
|
||||
switch (kind) {
|
||||
case 'fact':
|
||||
return 'default'
|
||||
case 'preference':
|
||||
return 'secondary'
|
||||
case 'pattern':
|
||||
return 'outline'
|
||||
case 'correction':
|
||||
return 'warning'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -479,7 +479,7 @@ export function ApiKeysPanel() {
|
||||
<div>
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<p className="text-sm font-medium">Claude.ai</p>
|
||||
<Badge variant="secondary" className="text-[10px] font-normal px-1.5 py-0">{t('recommended_badge')}</Badge>
|
||||
<span className="text-xs text-muted-foreground">{t('recommended_badge')}</span>
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground mb-2">
|
||||
{t.rich('claude_ai_instructions', {
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
import { useState, type ReactNode } from 'react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { useToast } from '@/components/ui/use-toast'
|
||||
import type { BillingPlan } from '@/lib/stripe/client'
|
||||
|
||||
@@ -96,7 +95,7 @@ export function BillingActions({ isPaying, configured }: { isPaying: boolean; co
|
||||
'yearly',
|
||||
<>
|
||||
Årsvis
|
||||
<Badge variant="success" className="ml-2">Spara 2 mån</Badge>
|
||||
<span className="ml-2 text-xs text-muted-foreground">Spara 2 mån</span>
|
||||
</>,
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -314,7 +314,7 @@ function TemplateSection({
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<Icon className="h-4 w-4 text-muted-foreground" />
|
||||
<h3 className="text-sm font-medium">{title}</h3>
|
||||
<Badge variant="secondary" className="text-xs">{templates.length}</Badge>
|
||||
<span className="text-xs text-muted-foreground tabular-nums">{templates.length}</span>
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
{templates.map((tt) => {
|
||||
@@ -335,14 +335,10 @@ function TemplateSection({
|
||||
<div className="flex-1 min-w-0">
|
||||
<span className="text-sm font-medium">{tt.name}</span>
|
||||
<div className="flex items-center gap-1.5 mt-0.5 flex-wrap">
|
||||
<Badge variant="outline" className="text-[10px] px-1.5 py-0">
|
||||
<span className="text-xs text-muted-foreground">
|
||||
{TEMPLATE_CATEGORY_LABELS[tt.category]}
|
||||
</Badge>
|
||||
{tt.entity_type !== 'all' && (
|
||||
<Badge variant="outline" className="text-[10px] px-1.5 py-0">
|
||||
{entityLabels[tt.entity_type]}
|
||||
</Badge>
|
||||
)}
|
||||
{tt.entity_type !== 'all' && ` · ${entityLabels[tt.entity_type]}`}
|
||||
</span>
|
||||
{!isConvertible && (
|
||||
<Badge variant="warning" className="text-[10px] px-1.5 py-0">
|
||||
{t('unconvertible_badge')}
|
||||
|
||||
@@ -8,7 +8,6 @@ import { Switch } from '@/components/ui/switch'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { useToast } from '@/components/ui/use-toast'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Calendar, Copy, RefreshCw, Loader2, ExternalLink, Check } from 'lucide-react'
|
||||
import { DestructiveConfirmDialog, useDestructiveConfirm } from '@/components/ui/destructive-confirm-dialog'
|
||||
import type { CalendarFeed } from '@/types'
|
||||
@@ -261,9 +260,10 @@ export function CalendarFeedSettings() {
|
||||
minute: '2-digit',
|
||||
})}
|
||||
</span>
|
||||
<Badge variant="secondary" className="text-xs">
|
||||
<span className="text-muted-foreground/30">·</span>
|
||||
<span className="tabular-nums">
|
||||
{t('times_count', { count: feed.access_count })}
|
||||
</Badge>
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/com
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Label } from '@/components/ui/label'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
|
||||
import { useToast } from '@/components/ui/use-toast'
|
||||
import { useCompany } from '@/contexts/CompanyContext'
|
||||
@@ -242,9 +241,7 @@ export function CompanyMembersSection() {
|
||||
{roleLabels[member.role] || member.role}
|
||||
</span>
|
||||
{member.source === 'team' && (
|
||||
<Badge variant="outline" className="text-[10px] px-1.5 py-0">
|
||||
{t('members_team_badge')}
|
||||
</Badge>
|
||||
<span className="text-xs text-muted-foreground">· {t('members_team_badge')}</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
@@ -295,9 +292,9 @@ export function CompanyMembersSection() {
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Badge variant="secondary" className="text-xs">
|
||||
<span className="text-xs text-muted-foreground">
|
||||
{roleLabels[inv.role] || inv.role}
|
||||
</Badge>
|
||||
</span>
|
||||
{canInvite && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
|
||||
@@ -4,7 +4,6 @@ import { useTranslations } from 'next-intl'
|
||||
import { useState, useEffect, useCallback } from 'react'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { useToast } from '@/components/ui/use-toast'
|
||||
import { Loader2, Trash2, Users, ChevronDown } from 'lucide-react'
|
||||
import { formatAccountWithName } from '@/lib/bookkeeping/client-account-names'
|
||||
@@ -130,9 +129,6 @@ export function CounterpartyTemplatesPanel() {
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="flex items-center gap-2">
|
||||
<p className="text-sm font-medium truncate">{formatCounterpartyName(tt.counterparty_name)}</p>
|
||||
<Badge variant="outline" className="text-[10px] shrink-0">
|
||||
{SOURCE_LABELS[tt.source] || tt.source}
|
||||
</Badge>
|
||||
</div>
|
||||
<div className="flex items-center gap-1.5 mt-0.5 text-xs text-muted-foreground">
|
||||
{isMultiLine ? (
|
||||
@@ -157,6 +153,8 @@ export function CounterpartyTemplatesPanel() {
|
||||
<span className={`tabular-nums ${confidenceColor(Number(tt.confidence))}`}>
|
||||
{Math.round(Number(tt.confidence) * 100)}%
|
||||
</span>
|
||||
<span className="text-muted-foreground/30">·</span>
|
||||
<span>{SOURCE_LABELS[tt.source] || tt.source}</span>
|
||||
</div>
|
||||
</div>
|
||||
<ChevronDown className={`h-4 w-4 text-muted-foreground shrink-0 transition-transform ${isExpanded ? 'rotate-180' : ''}`} />
|
||||
@@ -172,9 +170,9 @@ export function CounterpartyTemplatesPanel() {
|
||||
<div className="space-y-1">
|
||||
{tt.line_pattern!.map((lp, i) => (
|
||||
<div key={i} className="flex items-center gap-2 text-xs">
|
||||
<Badge variant="secondary" className="text-[10px] px-1.5 py-0 w-14 justify-center">
|
||||
<span className="w-14 shrink-0 text-[10px] uppercase tracking-wider text-muted-foreground">
|
||||
{lp.side === 'debit' ? t('debit_label') : t('credit_label')}
|
||||
</Badge>
|
||||
</span>
|
||||
<span className="font-mono">{formatAccountWithName(lp.account)}</span>
|
||||
{lp.type === 'vat' && lp.vat_rate && (
|
||||
<span className="text-muted-foreground">{t('vat_paren', { rate: Math.round(lp.vat_rate * 100) })}</span>
|
||||
@@ -188,11 +186,11 @@ export function CounterpartyTemplatesPanel() {
|
||||
) : (
|
||||
<div className="space-y-1">
|
||||
<div className="flex items-center gap-2 text-xs">
|
||||
<Badge variant="secondary" className="text-[10px] px-1.5 py-0 w-14 justify-center">{t('debit_label')}</Badge>
|
||||
<span className="w-14 shrink-0 text-[10px] uppercase tracking-wider text-muted-foreground">{t('debit_label')}</span>
|
||||
<span className="font-mono">{formatAccountWithName(tt.debit_account)}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 text-xs">
|
||||
<Badge variant="secondary" className="text-[10px] px-1.5 py-0 w-14 justify-center">{t('credit_label')}</Badge>
|
||||
<span className="w-14 shrink-0 text-[10px] uppercase tracking-wider text-muted-foreground">{t('credit_label')}</span>
|
||||
<span className="font-mono">{formatAccountWithName(tt.credit_account)}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -254,7 +254,7 @@ function EnvironmentBadge({ environment, disabled }: { environment?: Environment
|
||||
}
|
||||
if (environment === 'test') {
|
||||
return (
|
||||
<Badge variant="outline" className="border-amber-400 text-amber-700 dark:border-amber-600 dark:text-amber-400">
|
||||
<Badge variant="warning">
|
||||
<FlaskConical className="mr-1 h-3 w-3" />
|
||||
{t('env_test')}
|
||||
</Badge>
|
||||
@@ -262,7 +262,7 @@ function EnvironmentBadge({ environment, disabled }: { environment?: Environment
|
||||
}
|
||||
if (environment === 'prod') {
|
||||
return (
|
||||
<Badge variant="outline" className="border-emerald-400 text-emerald-700 dark:border-emerald-600 dark:text-emerald-400">
|
||||
<Badge variant="success">
|
||||
{t('env_prod')}
|
||||
</Badge>
|
||||
)
|
||||
|
||||
@@ -6,7 +6,6 @@ import { Input } from '@/components/ui/input'
|
||||
import { Label } from '@/components/ui/label'
|
||||
import { Checkbox } from '@/components/ui/checkbox'
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import type { CompanySettings } from '@/types'
|
||||
|
||||
interface TaxSettingsFormProps {
|
||||
@@ -35,9 +34,9 @@ export function TaxSettingsForm({ settings }: TaxSettingsFormProps) {
|
||||
{t('entity_form_heading')}
|
||||
</h2>
|
||||
<div className="flex items-center gap-3">
|
||||
<Badge variant="secondary" className="text-sm">
|
||||
<span className="text-sm font-medium">
|
||||
{settings.entity_type === 'aktiebolag' ? t('entity_aktiebolag') : t('entity_enskild_firma')}
|
||||
</Badge>
|
||||
</span>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{t('entity_form_help')}
|
||||
</p>
|
||||
|
||||
Reference in New Issue
Block a user