refactor(design): no amber boxes, attention is one ochre sentence (#1562)

The founder wants the yellow boxes gone everywhere. The design system
already agreed: status colors are data, not chrome (convention 12) and
attention is a single ochre sentence, never a banner (convention 6).
This enforces it:

- ConfirmationDialog: the amber warning panel is now an AttnLine, and
  the hardcoded Swedish default warningText is gone: it injected an
  immutability warning into dialogs whose authors never asked for one
  (every current caller passes the prop explicitly, so no behavior
  change at any call site).
- Badge warning variant: amber fill replaced with a hairline chip and
  ochre text.
- DestructiveConfirmDialog warning variant: neutral icon disc, default
  primary confirm button (only --destructive survives as chrome).
- BankSyncStatusChip stale state: same neutral shape as the healthy
  chip, ochre text carries the signal.
- SandboxBanner: solid amber bar becomes secondary-on-border chrome.
- BankIdAuth, BankIdCompanyPicker, SessionTimeoutModal: the last three
  raw-amber (bg-amber-*) holdouts moved onto tokens, the company-picker
  banner becoming a plain AttnLine.
- Mechanical sweep of the ~58 hand-rolled bg-warning/border-warning
  boxes across 45 files: fills to bg-muted/30 (icon discs bg-muted),
  borders to border-border, text-warning-foreground to text-attn. The
  account-class dots in account-number.tsx keep bg-warning: they are
  data indicators, not chrome.

Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Jakob Wennberg
2026-08-13 11:20:16 +02:00
committed by GitHub
co-authored by Jakob Wennberg Claude Fable 5
parent abff8d90f6
commit e51a2c8102
50 changed files with 157 additions and 156 deletions
+1 -1
View File
@@ -681,7 +681,7 @@ export default function JournalEntryDetailPage({ params }: { params: Promise<{ i
<CardContent className="text-sm">
{attachmentCount === 0 && references.length === 0 ? (
<div className="flex items-center gap-2">
<AlertTriangle className="h-4 w-4 text-warning-foreground" />
<AlertTriangle className="h-4 w-4 text-attn" />
<span className="text-muted-foreground">{t('no_attachments')}</span>
</div>
) : (
+3 -3
View File
@@ -1390,7 +1390,7 @@ export default function InvoiceDetailPage({ params }: { params: Promise<{ id: st
className={cn(
'flex items-center gap-2',
invoice.status === 'paid' && 'text-success',
invoice.status === 'partially_paid' && 'text-warning-foreground',
invoice.status === 'partially_paid' && 'text-attn',
)}
>
{invoice.status === 'paid' ? (
@@ -1423,7 +1423,7 @@ export default function InvoiceDetailPage({ params }: { params: Promise<{ id: st
<p
className={cn(
'font-display text-xl tabular-nums mt-1',
invoice.status === 'partially_paid' && 'text-warning-foreground',
invoice.status === 'partially_paid' && 'text-attn',
)}
>
{formatCurrency(
@@ -1567,7 +1567,7 @@ export default function InvoiceDetailPage({ params }: { params: Promise<{ id: st
{/* Credit note reference (if this invoice was credited) */}
{creditNote && (
<Card className={creditNote.status === 'draft' ? undefined : 'border-warning/50'}>
<Card className={creditNote.status === 'draft' ? undefined : 'border-border'}>
<CardHeader>
<CardTitle className={cn(
'flex items-center gap-2',
+1 -1
View File
@@ -247,7 +247,7 @@ export default function RecurringInvoicesPage() {
)}
{s.last_run_warning && (
<AlertTriangle
className="h-4 w-4 text-warning-foreground"
className="h-4 w-4 text-attn"
aria-label={s.last_run_warning}
/>
)}
@@ -481,7 +481,7 @@ export default function EmployeeDetailPage({ params }: { params: Promise<{ id: s
</div>
</div>
{showChecksumWarning && (
<p className="mt-2 text-xs text-warning-foreground">{t('bank_warn_checksum')}</p>
<p className="mt-2 text-xs text-attn">{t('bank_warn_checksum')}</p>
)}
<p className="text-xs text-muted-foreground mt-2">{t('form_bank_hint')}</p>
</CardContent>
+5 -5
View File
@@ -465,14 +465,14 @@ export function BankIdAuth({ mode, onComplete, hero = false }: BankIdAuthProps)
if (status === 'service_unavailable') {
return (
<div className="rounded-lg border border-amber-200 bg-amber-50 p-4 dark:border-amber-900 dark:bg-amber-950/30">
<div className="rounded-lg border border-border bg-muted/30 p-4">
<div className="flex items-start gap-3">
<AlertTriangle className="mt-0.5 h-4 w-4 shrink-0 text-amber-600 dark:text-amber-400" />
<AlertTriangle className="mt-0.5 h-4 w-4 shrink-0 text-attn" />
<div className="space-y-1.5">
<p className="text-sm font-medium text-amber-800 dark:text-amber-200">
<p className="text-sm font-medium">
BankID är inte tillgängligt just nu
</p>
<p className="text-sm text-amber-700 dark:text-amber-300">
<p className="text-sm text-muted-foreground">
{mode === 'login'
? 'Logga in med e-post och lösenord nedan, eller använd "Glömt lösenord?" för en inloggningslänk via e-post.'
: mode === 'signup'
@@ -483,7 +483,7 @@ export function BankIdAuth({ mode, onComplete, hero = false }: BankIdAuthProps)
onClick={startSession}
variant="ghost"
size="sm"
className="mt-1 h-auto px-0 py-0 text-xs text-amber-600 underline underline-offset-2 hover:text-amber-800 dark:text-amber-400"
className="mt-1 h-auto px-0 py-0 text-xs text-muted-foreground underline underline-offset-2 hover:text-foreground"
>
Försök med BankID igen
</Button>
+1 -1
View File
@@ -35,7 +35,7 @@ export function SessionTimeoutModal({
onInteractOutside={(event) => event.preventDefault()}
>
<DialogHeader>
<div className="mb-2 flex h-10 w-10 items-center justify-center rounded-full bg-amber-100 text-amber-700 dark:bg-amber-950 dark:text-amber-300">
<div className="mb-2 flex h-10 w-10 items-center justify-center rounded-full bg-muted text-attn">
<ShieldAlert className="h-5 w-5" aria-hidden="true" />
</div>
<DialogTitle>{t('warning_title')}</DialogTitle>
@@ -130,10 +130,10 @@ export function ActivateAccountsDialog({
)}
{!loading && unknownRows.length > 0 && (
<div className="rounded-md border border-warning/40 bg-warning/5 px-3 py-2 text-xs text-warning-foreground">
<div className="rounded-md border border-border bg-muted/30 px-3 py-2 text-xs text-attn">
<p className="font-medium">Finns inte i BAS-katalogen:</p>
<p className="mt-1 font-mono">{unknownRows.map((r) => r.account_number).join(', ')}</p>
<p className="mt-1 text-warning-foreground/80">
<p className="mt-1 text-attn/80">
Skapa dem som egna konton, eller kontrollera inmatningen.
</p>
{onCreateUnknown && (
+3 -3
View File
@@ -184,9 +184,9 @@ export function AddAccountDialog({
<div className="space-y-4 py-2">
{isBASMatch && (
<div className="flex items-start gap-2 rounded-lg bg-warning/10 border border-warning/30 p-3">
<AlertTriangle className="h-4 w-4 text-warning-foreground mt-0.5 shrink-0" />
<p className="text-sm text-warning-foreground">
<div className="flex items-start gap-2 rounded-lg bg-muted/30 border border-border p-3">
<AlertTriangle className="h-4 w-4 text-attn mt-0.5 shrink-0" />
<p className="text-sm text-attn">
Kontonummer {accountNumber} finns i BAS-standarden. Använd &quot;BAS-katalog&quot;-fliken för att aktivera standardkonton istället.
</p>
</div>
@@ -321,8 +321,8 @@ export default function AttachmentPreviewSheet({
{isPdfType(doc.mime_type, doc.file_name) && integrity[doc.id] === 'invalid' && (
<div className="flex h-[70vh] w-full flex-col items-center justify-center gap-4 rounded-lg border border-border bg-muted/30 p-6 text-center">
<div className="flex h-10 w-10 items-center justify-center rounded-full bg-warning/15">
<AlertTriangle className="h-5 w-5 text-warning-foreground" />
<div className="flex h-10 w-10 items-center justify-center rounded-full bg-muted">
<AlertTriangle className="h-5 w-5 text-attn" />
</div>
<div className="max-w-md space-y-2">
<p className="text-sm font-medium">{t('corrupt_title')}</p>
@@ -420,8 +420,8 @@ export default function AttachmentPreviewSheet({
<DialogContent>
<DialogHeader>
<div className="flex items-center gap-3">
<div className="flex h-10 w-10 items-center justify-center rounded-full bg-warning/15 shrink-0">
<Lock className="h-5 w-5 text-warning-foreground" />
<div className="flex h-10 w-10 items-center justify-center rounded-full bg-muted shrink-0">
<Lock className="h-5 w-5 text-attn" />
</div>
<DialogTitle>{tj('remove_blocked_title')}</DialogTitle>
</div>
@@ -135,7 +135,7 @@ export default function CorrectOpeningBalanceDialog({
</DialogHeader>
{/* Storno explanation: a booked verifikat can't be edited in place */}
<div className="flex items-start gap-3 rounded-lg border border-warning/30 bg-warning/5 px-4 py-3">
<div className="flex items-start gap-3 rounded-lg border border-border bg-muted/30 px-4 py-3">
<AlertTriangle className="h-4 w-4 text-warning mt-0.5 shrink-0" />
<p className="text-sm text-warning">
En bokförd verifikation kan inte ändras direkt (Bokföringslagen). När du sparar stornas
@@ -432,8 +432,8 @@ export default function JournalEntryAttachments({
<DialogContent>
<DialogHeader>
<div className="flex items-center gap-3">
<div className="flex h-10 w-10 items-center justify-center rounded-full bg-warning/15 shrink-0">
<Lock className="h-5 w-5 text-warning-foreground" />
<div className="flex h-10 w-10 items-center justify-center rounded-full bg-muted shrink-0">
<Lock className="h-5 w-5 text-attn" />
</div>
<DialogTitle>{t('remove_blocked_title')}</DialogTitle>
</div>
+10 -10
View File
@@ -1277,9 +1277,9 @@ export default function JournalEntryForm({
</div>
{(monthChanged || selectedPeriodLocked) && (
<div className="flex items-start gap-3 rounded-lg border border-warning/30 bg-warning/10 p-3">
<AlertTriangle className="h-5 w-5 text-warning-foreground mt-0.5 shrink-0" />
<div className="flex-1 text-sm text-warning-foreground space-y-0.5">
<div className="flex items-start gap-3 rounded-lg border border-border bg-muted/30 p-3">
<AlertTriangle className="h-5 w-5 text-attn mt-0.5 shrink-0" />
<div className="flex-1 text-sm text-attn space-y-0.5">
{monthChanged && (
<p className="font-medium">
{t('review_month_changed', { prev: monthLabel(lastPostedMonth as string), current: monthLabel(entryMonth) })}
@@ -1291,7 +1291,7 @@ export default function JournalEntryForm({
)}
{uploadedFiles.filter((f) => f.status === 'uploaded').length === 0 && (
<div className="flex items-start gap-3 rounded-lg border border-warning/30 bg-warning/10 p-3 text-sm text-warning-foreground">
<div className="flex items-start gap-3 rounded-lg border border-border bg-muted/30 p-3 text-sm text-attn">
<AlertTriangle className="h-5 w-5 mt-0.5 shrink-0" />
<p>{t('no_doc_body')}</p>
</div>
@@ -1510,9 +1510,9 @@ export default function JournalEntryForm({
)}
{periodMismatch === 'no_period' && (
<div className="flex items-start gap-3 rounded-lg border border-warning/30 bg-warning/10 p-3">
<AlertTriangle className="h-5 w-5 text-warning-foreground mt-0.5 shrink-0" />
<div className="flex-1 text-sm text-warning-foreground">
<div className="flex items-start gap-3 rounded-lg border border-border bg-muted/30 p-3">
<AlertTriangle className="h-5 w-5 text-attn mt-0.5 shrink-0" />
<div className="flex-1 text-sm text-attn">
<p className="font-medium">{t('no_period_warning', { date: entryDate })}</p>
<p className="mt-0.5">{t('no_period_help')}</p>
</div>
@@ -2043,9 +2043,9 @@ export default function JournalEntryForm({
warningText={embedded ? '' : t('review_warning')}
>
{(monthChanged || selectedPeriodLocked) && (
<div className="mb-4 flex items-start gap-3 rounded-lg border border-warning/30 bg-warning/10 p-3">
<AlertTriangle className="h-5 w-5 text-warning-foreground mt-0.5 shrink-0" />
<div className="flex-1 text-sm text-warning-foreground space-y-0.5">
<div className="mb-4 flex items-start gap-3 rounded-lg border border-border bg-muted/30 p-3">
<AlertTriangle className="h-5 w-5 text-attn mt-0.5 shrink-0" />
<div className="flex-1 text-sm text-attn space-y-0.5">
{monthChanged && (
<p className="font-medium">
{t('review_month_changed', {
@@ -237,7 +237,7 @@ export function DispositionsStep({
fiscalYear={fiscalYear}
/>
{taxDepreciationDirty && (
<p className="text-sm text-warning-foreground" role="status">
<p className="text-sm text-attn" role="status">
Spara eller återställ ändringarna i skattemässig avskrivning innan du lämnar steget.
</p>
)}
@@ -302,7 +302,7 @@ export function DispositionsStep({
{proposal.warnings?.map((warning) => (
<Card key={warning}>
<CardContent className="p-4 text-sm text-warning-foreground flex items-start gap-2">
<CardContent className="p-4 text-sm text-attn flex items-start gap-2">
<AlertTriangle className="h-4 w-4 shrink-0 mt-0.5" />
<span>{warning}</span>
</CardContent>
@@ -335,7 +335,7 @@ export function DispositionsStep({
{completed.warnings.length > 0 && (
<CardContent className="space-y-2">
{completed.warnings.map((warning) => (
<p key={warning} className="text-sm text-warning-foreground">{warning}</p>
<p key={warning} className="text-sm text-attn">{warning}</p>
))}
</CardContent>
)}
@@ -378,7 +378,7 @@ export function DispositionsStep({
)}
{taxDepreciationDirty && (
<p className="text-sm text-warning-foreground" role="status">
<p className="text-sm text-attn" role="status">
Spara eller återställ ändringarna i skattemässig avskrivning innan du lämnar steget.
</p>
)}
@@ -582,7 +582,7 @@ function ProposalCard({
</CardHeader>
<CardContent className="space-y-3">
{proposal.warnings.map((w, i) => (
<p key={i} className="text-sm text-warning-foreground">
<p key={i} className="text-sm text-attn">
{w}
</p>
))}
@@ -186,7 +186,7 @@ export function EfDeclarationSection({
{noPostedEntries && (
<Card className="border-border">
<CardContent className="p-4 flex items-start gap-3">
<AlertTriangle className="h-4 w-4 mt-0.5 text-warning-foreground shrink-0" />
<AlertTriangle className="h-4 w-4 mt-0.5 text-attn shrink-0" />
<p className="text-sm">
<span className="font-medium">Inga verifikat bokförda i perioden.</span>{' '}
Värdena nedan baseras enbart på NE-bilagans räkenskapsschema (intäkter och
@@ -298,7 +298,7 @@ export function EfDeclarationSection({
{inputWarnings.map((w, i) => (
<Card key={i} className="border-border">
<CardContent className="p-4 flex items-start gap-3">
<AlertTriangle className="h-4 w-4 mt-0.5 text-warning-foreground shrink-0" />
<AlertTriangle className="h-4 w-4 mt-0.5 text-attn shrink-0" />
<p className="text-sm">{w}</p>
</CardContent>
</Card>
@@ -335,7 +335,7 @@ export function EfDeclarationSection({
</div>
</CardHeader>
{item.warnings.length > 0 && (
<CardContent className="text-sm text-warning-foreground space-y-1">
<CardContent className="text-sm text-attn space-y-1">
{item.warnings.map((w, i) => (
<p key={i}>{w}</p>
))}
+5 -3
View File
@@ -17,20 +17,22 @@ export function SandboxBanner() {
router.push('/register')
}
// Primary-on-secondary, not a solid amber bar: the sandbox notice is
// environment chrome, and chrome never wears the warning fill.
return (
<div className="relative z-50 flex items-center justify-center gap-x-3 gap-y-1 bg-warning px-10 py-2 text-sm text-warning-foreground sm:px-4 flex-wrap">
<div className="relative z-50 flex items-center justify-center gap-x-3 gap-y-1 border-b border-border bg-secondary px-10 py-2 text-sm text-secondary-foreground sm:px-4 flex-wrap">
<span className="font-medium text-center text-xs sm:text-sm">
Sandlådemiljö: AI och externa tjänster är avstängda. Data raderas efter 24h.
</span>
<button
onClick={handleCreateAccount}
className="shrink-0 rounded-md bg-warning-foreground/15 px-3 py-0.5 text-xs font-semibold hover:bg-warning-foreground/25 transition-colors"
className="shrink-0 rounded-md bg-foreground/10 px-3 py-0.5 text-xs font-semibold hover:bg-foreground/15 transition-colors"
>
Skapa konto
</button>
<button
onClick={() => setDismissed(true)}
className="absolute right-2 top-1/2 -translate-y-1/2 rounded p-1 hover:bg-warning-foreground/15 transition-colors"
className="absolute right-2 top-1/2 -translate-y-1/2 rounded p-1 hover:bg-foreground/10 transition-colors"
aria-label="Stäng"
>
<X className="h-3.5 w-3.5" />
+2 -2
View File
@@ -126,7 +126,7 @@ export function TaxTodoWidget({ deadlines, onStatusChange }: TaxTodoWidgetProps)
<CardHeader className="pb-2">
<div className="flex items-center justify-between">
<CardTitle className="text-lg flex items-center gap-2">
<FileText className="h-5 w-5 text-warning-foreground" />
<FileText className="h-5 w-5 text-attn" />
{t('title')}
</CardTitle>
<div className="flex gap-2">
@@ -155,7 +155,7 @@ export function TaxTodoWidget({ deadlines, onStatusChange }: TaxTodoWidgetProps)
{isOverdue ? (
<AlertTriangle className="h-4 w-4 text-destructive flex-shrink-0 mt-0.5" />
) : (
<Clock className="h-4 w-4 text-warning-foreground flex-shrink-0 mt-0.5" />
<Clock className="h-4 w-4 text-attn flex-shrink-0 mt-0.5" />
)}
<div className="min-w-0 flex-1">
<p className="text-sm font-medium">{deadline.title}</p>
@@ -146,7 +146,7 @@ export function UpcomingDeadlinesWidget({ deadlines, maxItems = 5, onStatusChang
{deadline.status === 'overdue' ? (
<AlertTriangle className="h-4 w-4 text-destructive flex-shrink-0" />
) : deadline.status === 'action_needed' ? (
<Clock className="h-4 w-4 text-warning-foreground flex-shrink-0" />
<Clock className="h-4 w-4 text-attn flex-shrink-0" />
) : (
<Clock className="h-4 w-4 text-muted-foreground flex-shrink-0" />
)}
@@ -1052,7 +1052,7 @@ export default function BookDirectlyDialog({ open, onOpenChange, item, docUrl =
<p
className={cn(
'text-xs tabular-nums',
disabledReason ? 'text-warning-foreground' : 'text-muted-foreground'
disabledReason ? 'text-attn' : 'text-muted-foreground'
)}
aria-live="polite"
>
@@ -19,15 +19,15 @@ export default function MockDataBanner({ importedAt, onClear, onReplace }: MockD
: null
return (
<Card className="border-l-4 border-l-warning bg-warning/[0.03]">
<Card className="border-l-4 border-l-warning bg-muted/30">
<CardContent className="pt-4 pb-4">
<div className="flex items-center gap-3">
<FlaskConical className="h-5 w-5 text-warning-foreground shrink-0" />
<FlaskConical className="h-5 w-5 text-attn shrink-0" />
<div className="flex-1 min-w-0">
<p className="text-sm font-medium text-warning-foreground">
<p className="text-sm font-medium text-attn">
Testdata aktivt
</p>
<p className="text-xs text-warning-foreground mt-0.5">
<p className="text-xs text-attn mt-0.5">
Rapporten visar importerad testdata istället för bokföringsdata.
{formatted && <> Importerat {formatted}.</>}
</p>
+5 -5
View File
@@ -99,7 +99,7 @@ export default function ArticlesEditStep({
<CardContent className="space-y-4">
{/* Duplicate handling banner */}
{liveDuplicateCount > 0 && (
<div className="flex items-start gap-3 rounded-lg border border-warning/30 bg-warning/5 px-4 py-3">
<div className="flex items-start gap-3 rounded-lg border border-border bg-muted/30 px-4 py-3">
<RefreshCw className="h-4 w-4 text-warning mt-0.5 shrink-0" />
<div className="flex-1 space-y-2">
<p className="text-sm">
@@ -129,7 +129,7 @@ export default function ArticlesEditStep({
{/* VAT-adjustment notice: rows whose rate was snapped/defaulted */}
{adjustedVatCount > 0 && (
<div className="flex items-start gap-3 rounded-lg border border-warning/30 bg-warning/5 px-4 py-3">
<div className="flex items-start gap-3 rounded-lg border border-border bg-muted/30 px-4 py-3">
<AlertTriangle className="h-4 w-4 text-warning mt-0.5 shrink-0" />
<p className="text-sm">
<span className="font-medium">{adjustedVatCount} rad{adjustedVatCount === 1 ? '' : 'er'}</span> fick
@@ -209,7 +209,7 @@ export default function ArticlesEditStep({
}
>
<SelectTrigger
className={cn('h-8', row.vat_rate_adjusted && 'border-warning text-warning')}
className={cn('h-8', row.vat_rate_adjusted && 'border-border text-warning')}
>
<SelectValue />
</SelectTrigger>
@@ -246,7 +246,7 @@ export default function ArticlesEditStep({
className={cn(
'text-[11px] font-medium px-1.5 py-0.5 rounded',
updateDuplicates
? 'bg-warning/15 text-warning'
? 'bg-muted text-warning'
: 'bg-muted text-muted-foreground',
)}
title={`Matchar ${row.duplicate_match.existing_name} (${row.duplicate_match.matched_by})`}
@@ -278,7 +278,7 @@ export default function ArticlesEditStep({
</div>
{hasErrors && (
<div className="flex items-start gap-3 rounded-lg border border-warning/30 bg-warning/5 px-4 py-3">
<div className="flex items-start gap-3 rounded-lg border border-border bg-muted/30 px-4 py-3">
<AlertTriangle className="h-4 w-4 text-warning mt-0.5 shrink-0" />
<p className="text-sm text-warning">
Vissa rader har valideringsfel (markerade i rött). Åtgärda eller ta bort dem
+3 -3
View File
@@ -89,7 +89,7 @@ export default function CustomersEditStep({
<CardContent className="space-y-4">
{/* Duplicate handling banner */}
{liveDuplicateCount > 0 && (
<div className="flex items-start gap-3 rounded-lg border border-warning/30 bg-warning/5 px-4 py-3">
<div className="flex items-start gap-3 rounded-lg border border-border bg-muted/30 px-4 py-3">
<RefreshCw className="h-4 w-4 text-warning mt-0.5 shrink-0" />
<div className="flex-1 space-y-2">
<p className="text-sm">
@@ -185,7 +185,7 @@ export default function CustomersEditStep({
className={cn(
'text-[11px] font-medium px-1.5 py-0.5 rounded',
updateDuplicates
? 'bg-warning/15 text-warning'
? 'bg-muted text-warning'
: 'bg-muted text-muted-foreground',
)}
title={`Matchar ${row.duplicate_match.existing_name} (${row.duplicate_match.matched_by})`}
@@ -216,7 +216,7 @@ export default function CustomersEditStep({
</div>
{hasErrors && (
<div className="flex items-start gap-3 rounded-lg border border-warning/30 bg-warning/5 px-4 py-3">
<div className="flex items-start gap-3 rounded-lg border border-border bg-muted/30 px-4 py-3">
<AlertTriangle className="h-4 w-4 text-warning mt-0.5 shrink-0" />
<p className="text-sm text-warning">
Vissa rader har valideringsfel (markerade i rött). Åtgärda eller ta bort dem
+3 -3
View File
@@ -179,7 +179,7 @@ export default function ImportResultStep({
)}
{result.success && result.nextPeriodIBResyncSkipped && (
<Card className="border-warning/50">
<Card className="border-border">
<CardHeader>
<CardTitle className="flex items-center gap-2 text-base text-warning">
<AlertCircle className="h-5 w-5" />
@@ -347,7 +347,7 @@ export default function ImportResultStep({
{/* Untransferred prior-year results — the year-end omföring is missing */}
{untransferred && untransferred.length > 0 && (
<Card className="border-warning/50">
<Card className="border-border">
<CardHeader>
<CardTitle className="flex items-center gap-2 text-warning">
<AlertCircle className="h-5 w-5" />
@@ -376,7 +376,7 @@ export default function ImportResultStep({
{/* Other warnings (filtered) */}
{otherWarnings.length > 0 && (
<Card className="border-warning/50">
<Card className="border-border">
<CardHeader>
<CardTitle className="flex items-center gap-2 text-warning">
<AlertCircle className="h-5 w-5" />
+1 -1
View File
@@ -413,7 +413,7 @@ export default function ImportReviewStep({
{/* Warnings */}
{!preview.trialBalance.isBalanced && (
<Card className="border-warning/50 bg-warning/5">
<Card className="border-border bg-muted/30">
<CardHeader>
<CardTitle className="flex items-center gap-2 text-warning">
<AlertCircle className="h-5 w-5" />
@@ -115,7 +115,7 @@ export default function OpeningBalancePeriodStep({
Hämtar perioder...
</div>
) : periods.length === 0 ? (
<div className="flex items-start gap-3 rounded-lg border border-warning/30 bg-warning/5 px-4 py-3">
<div className="flex items-start gap-3 rounded-lg border border-border bg-muted/30 px-4 py-3">
<AlertCircle className="h-4 w-4 text-warning mt-0.5 shrink-0" />
<p className="text-sm text-warning">
Inga räkenskapsperioder hittades. Skapa en räkenskapsperiod under Bokföring först.
@@ -142,7 +142,7 @@ export default function OpeningBalancePeriodStep({
{/* Replace notice: selecting a period that already has IB corrects it */}
{periodHasOB && !periodIsClosed && !periodIsLocked && (
<div className="flex items-start gap-3 rounded-lg border border-warning/30 bg-warning/5 px-4 py-3">
<div className="flex items-start gap-3 rounded-lg border border-border bg-muted/30 px-4 py-3">
<AlertCircle className="h-4 w-4 text-warning mt-0.5 shrink-0" />
<p className="text-sm text-warning">
Denna period har redan ingående balanser. Om du fortsätter makuleras (stornas) den
@@ -415,7 +415,7 @@ export default function OpeningBalanceRowEditor({
{/* Warnings */}
{!totals.isBalanced && Math.abs(totals.diff) > 1 && (
<div className="flex items-start gap-3 rounded-lg border border-warning/30 bg-warning/5 px-4 py-3">
<div className="flex items-start gap-3 rounded-lg border border-border bg-muted/30 px-4 py-3">
<AlertTriangle className="h-4 w-4 text-warning mt-0.5 shrink-0" />
<p className="text-sm text-warning">
Debet och kredit balanserar inte. Differens:{' '}
+1 -1
View File
@@ -106,7 +106,7 @@ export default function RegisterResultStep({
{result.warnings && result.warnings.length > 0 && (
<div className="space-y-2">
<h4 className="text-sm font-medium">Att notera</h4>
<div className="rounded-lg border border-warning/30 bg-warning/5 px-4 py-3">
<div className="rounded-lg border border-border bg-muted/30 px-4 py-3">
<ul className="space-y-1 text-sm text-muted-foreground">
{result.warnings.map((w, i) => (
<li key={i} className="flex items-start gap-2">
+4 -4
View File
@@ -150,7 +150,7 @@ export default function SIEPreviewStep({
</div>
{/* Trial balance check */}
<Card className={preview.trialBalance.isBalanced ? 'border-success/50' : 'border-warning/50'}>
<Card className={preview.trialBalance.isBalanced ? 'border-success/50' : 'border-border'}>
<CardHeader>
<CardTitle className="flex items-center gap-2">
{preview.trialBalance.isBalanced ? (
@@ -185,7 +185,7 @@ export default function SIEPreviewStep({
{/* Significant imbalance: explain + require acknowledgement before continuing */}
{significantImbalance && (
<div className="mt-4 space-y-3 rounded-lg border border-warning/40 bg-warning/5 px-4 py-3">
<div className="mt-4 space-y-3 rounded-lg border border-border bg-muted/30 px-4 py-3">
<div className="flex items-start gap-2 text-sm">
<AlertCircle className="h-4 w-4 text-warning mt-0.5 flex-shrink-0" />
<div className="space-y-1">
@@ -222,7 +222,7 @@ export default function SIEPreviewStep({
preview.mappingStatus.unmapped > 0
? 'border-destructive/50'
: preview.mappingStatus.lowConfidence > 0
? 'border-warning/50'
? 'border-border'
: 'border-success/50'
}
>
@@ -356,7 +356,7 @@ export default function SIEPreviewStep({
{/* Warnings */}
{warnings.length > 0 && (
<Card className="border-warning/50">
<Card className="border-border">
<CardHeader>
<CardTitle className="flex items-center gap-2">
<AlertCircle className="h-5 w-5 text-warning" />
+3 -3
View File
@@ -168,7 +168,7 @@ export default function SIEUploadStep({ onFileSelect, isLoading, error, errorTyp
<div className="mt-4 space-y-3">
<div className={`p-4 rounded-lg flex gap-3 ${
errorType === 'duplicate' || errorType === 'duplicate_period'
? 'bg-warning/10 border border-warning/20'
? 'bg-muted/30 border border-border'
: 'bg-destructive/10 border border-destructive/20'
}`}>
<AlertCircle className={`h-5 w-5 flex-shrink-0 mt-0.5 ${
@@ -200,7 +200,7 @@ export default function SIEUploadStep({ onFileSelect, isLoading, error, errorTyp
<Button
variant="outline"
size="sm"
className="border-warning/50 text-warning hover:bg-warning/10"
className="border-border text-warning hover:bg-muted/30"
disabled={isReplacing}
onClick={(e) => {
e.stopPropagation()
@@ -246,7 +246,7 @@ export default function SIEUploadStep({ onFileSelect, isLoading, error, errorTyp
{/* Validation warnings list */}
{validationWarnings && validationWarnings.length > 0 && (
<div className="p-4 bg-warning/5 border border-warning/15 rounded-lg space-y-2">
<div className="p-4 bg-muted/30 border border-border rounded-lg space-y-2">
<p className="text-sm font-medium text-warning">Varningar ({validationWarnings.length})</p>
<div className="space-y-1.5 max-h-32 overflow-y-auto">
{validationWarnings.map((warn, i) => (
+3 -3
View File
@@ -80,7 +80,7 @@ export default function SuppliersEditStep({
</CardHeader>
<CardContent className="space-y-4">
{liveDuplicateCount > 0 && (
<div className="flex items-start gap-3 rounded-lg border border-warning/30 bg-warning/5 px-4 py-3">
<div className="flex items-start gap-3 rounded-lg border border-border bg-muted/30 px-4 py-3">
<RefreshCw className="h-4 w-4 text-warning mt-0.5 shrink-0" />
<div className="flex-1 space-y-2">
<p className="text-sm">
@@ -175,7 +175,7 @@ export default function SuppliersEditStep({
className={cn(
'text-[11px] font-medium px-1.5 py-0.5 rounded',
updateDuplicates
? 'bg-warning/15 text-warning'
? 'bg-muted text-warning'
: 'bg-muted text-muted-foreground',
)}
title={`Matchar ${row.duplicate_match.existing_name} (${row.duplicate_match.matched_by})`}
@@ -207,7 +207,7 @@ export default function SuppliersEditStep({
</div>
{hasErrors && (
<div className="flex items-start gap-3 rounded-lg border border-warning/30 bg-warning/5 px-4 py-3">
<div className="flex items-start gap-3 rounded-lg border border-border bg-muted/30 px-4 py-3">
<AlertTriangle className="h-4 w-4 text-warning mt-0.5 shrink-0" />
<p className="text-sm text-warning">
Vissa rader har valideringsfel (markerade i rött). Åtgärda eller ta bort dem
+1 -1
View File
@@ -1952,7 +1952,7 @@ export default function InvoiceEditor(props: InvoiceEditorProps = { mode: 'creat
30%/50% applies to the full line total: the seller
must ensure the line is 100% labor; material has
to be invoiced separately. */}
<div className="mt-2 flex items-start gap-2 text-xs text-warning-foreground">
<div className="mt-2 flex items-start gap-2 text-xs text-attn">
<AlertTriangle className="h-3.5 w-3.5 mt-0.5 text-warning shrink-0" />
<p>{t('deduction_labor_only_warning')}</p>
</div>
@@ -429,7 +429,7 @@ function NewRecurringScheduleForm({
{t('auto_send_description')}
</p>
{customerMissingEmail && (
<p className="text-sm text-warning-foreground mt-1">
<p className="text-sm text-attn mt-1">
{t('auto_send_missing_email')}
</p>
)}
+2 -2
View File
@@ -441,8 +441,8 @@ export default function RotRutPayoutDialog({
)}
{visibleCandidates.length > MAX_CASES_PER_FILE && (
<div className="flex items-start gap-2 rounded-md border border-warning/30 bg-warning/5 p-3 text-xs text-muted-foreground">
<AlertTriangle className="mt-0.5 h-3.5 w-3.5 shrink-0 text-warning-foreground" />
<div className="flex items-start gap-2 rounded-md border border-border bg-muted/30 p-3 text-xs text-muted-foreground">
<AlertTriangle className="mt-0.5 h-3.5 w-3.5 shrink-0 text-attn" />
<span>{t('rot_rut_max_cases_help', { count: MAX_CASES_PER_FILE })}</span>
</div>
)}
@@ -4,7 +4,8 @@ import { useMemo, useState } from 'react'
import { useRouter } from 'next/navigation'
import { useTranslations } from 'next-intl'
import Link from 'next/link'
import { Loader2, AlertTriangle } from 'lucide-react'
import { Loader2 } from 'lucide-react'
import { AttnLine } from '@/components/ui/attn-line'
import { useToast } from '@/components/ui/use-toast'
import { switchCompany } from '@/lib/company/actions'
import { mapEntityType } from '@/lib/company-lookup/entity-type-map'
@@ -162,12 +163,7 @@ export default function BankIdCompanyPicker({
)}
{enrichmentStale && (
<div className="mb-6 rounded-lg border border-amber-200 bg-amber-50 p-3 dark:border-amber-900/60 dark:bg-amber-950/30">
<div className="flex items-start gap-2.5">
<AlertTriangle className="h-4 w-4 text-amber-600 dark:text-amber-400 mt-0.5 flex-shrink-0" />
<p className="text-sm text-amber-800 dark:text-amber-200">{t('enrichment_stale')}</p>
</div>
</div>
<AttnLine className="mb-6">{t('enrichment_stale')}</AttnLine>
)}
<div className="jny-biginput jny-filter" style={{ margin: '0 auto' }}>
+1 -1
View File
@@ -241,7 +241,7 @@ export default function EmployeeTaxCard({
{t('tax_column_suggested_under_66')}
</p>
) : isSenior && !columnTouched ? (
<p className="text-xs text-warning-foreground">
<p className="text-xs text-attn">
{t('tax_column_senior_warning')}
</p>
) : null}
+1 -1
View File
@@ -415,7 +415,7 @@ function NewEmployeeForm({ onCreated, onCancel }: { onCreated: () => void; onCan
</Field>
</div>
{showChecksumWarning && (
<p className="text-xs text-warning-foreground">{BANK_CHECKSUM_WARNING_SV}</p>
<p className="text-xs text-attn">{BANK_CHECKSUM_WARNING_SV}</p>
)}
</section>
</div>
+8 -8
View File
@@ -97,8 +97,8 @@ interface AbsenceTypeMeta {
// neutral = the rest (study, unpaid, other)
const TYPE_META: Record<AbsenceType, AbsenceTypeMeta> = {
sick: { labelKey: 'type_sick', shortLabelKey: 'type_sick_short', icon: HeartPulse, pillClass: 'bg-destructive/10 text-destructive' },
vab: { labelKey: 'type_vab', shortLabelKey: 'type_vab_short', icon: Baby, pillClass: 'bg-warning/15 text-warning-foreground' },
care_relative: { labelKey: 'type_care_relative', shortLabelKey: 'type_care_relative_short', icon: Heart, pillClass: 'border border-warning/40 text-warning-foreground' },
vab: { labelKey: 'type_vab', shortLabelKey: 'type_vab_short', icon: Baby, pillClass: 'bg-muted text-attn' },
care_relative: { labelKey: 'type_care_relative', shortLabelKey: 'type_care_relative_short', icon: Heart, pillClass: 'border border-border text-attn' },
parental: { labelKey: 'type_parental', shortLabelKey: 'type_parental_short', icon: Heart, pillClass: 'bg-success/10 text-success' },
pregnancy: { labelKey: 'type_pregnancy', shortLabelKey: 'type_pregnancy_short', icon: Heart, pillClass: 'border border-success/40 text-success' },
study: { labelKey: 'type_study', shortLabelKey: 'type_study_short', icon: Activity, pillClass: 'bg-secondary text-secondary-foreground' },
@@ -727,11 +727,11 @@ function BulkWorkedDialog({
</div>
{conflicts.length > 0 && (
<div className="space-y-1 rounded-md border border-warning/40 bg-warning/10 p-2 text-xs">
<div className="font-medium text-warning-foreground">
<div className="space-y-1 rounded-md border border-border bg-muted/30 p-2 text-xs">
<div className="font-medium text-attn">
{t('conflicts_worked', { count: conflicts.length })}
</div>
<ul className="list-disc space-y-0.5 pl-4 text-warning-foreground tabular-nums">
<ul className="list-disc space-y-0.5 pl-4 text-attn tabular-nums">
{conflicts.map(c => <li key={c.date}>{c.date}</li>)}
</ul>
</div>
@@ -880,11 +880,11 @@ function BulkAbsenceDialog({
</div>
{conflicts.length > 0 && (
<div className="space-y-1 rounded-md border border-warning/40 bg-warning/10 p-2 text-xs">
<div className="font-medium text-warning-foreground">
<div className="space-y-1 rounded-md border border-border bg-muted/30 p-2 text-xs">
<div className="font-medium text-attn">
{t('conflicts_absence', { count: conflicts.length })}
</div>
<ul className="list-disc space-y-0.5 pl-4 text-warning-foreground tabular-nums">
<ul className="list-disc space-y-0.5 pl-4 text-attn tabular-nums">
{conflicts.map(c => <li key={c.date}>{c.date}</li>)}
</ul>
</div>
+1 -1
View File
@@ -683,7 +683,7 @@ export function ApiKeysPanel() {
{hasSodConflict && (
<div
role="alert"
className="flex items-start gap-2 rounded-md border border-warning/40 bg-warning/10 p-3 text-xs text-foreground"
className="flex items-start gap-2 rounded-md border border-border bg-muted/30 p-3 text-xs text-foreground"
>
<AlertTriangle className="mt-0.5 h-4 w-4 shrink-0 text-warning" />
<p className="leading-snug">{t('sod_warning')}</p>
+6 -6
View File
@@ -323,8 +323,8 @@ export function TemplateForm({
</div>
{ratioSumOff && (
<div className="rounded-lg border border-warning/30 bg-warning/[0.03] px-3 py-2">
<p className="text-xs text-warning-foreground leading-snug">
<div className="rounded-lg border border-border bg-muted/30 px-3 py-2">
<p className="text-xs text-attn leading-snug">
{t('ratio_sum_warning')}
</p>
</div>
@@ -361,16 +361,16 @@ export function TemplateForm({
)}
{nameCollision && (
<div className="rounded-lg border border-warning/30 bg-warning/[0.03] px-3 py-2">
<p className="text-xs text-warning-foreground leading-snug">
<div className="rounded-lg border border-border bg-muted/30 px-3 py-2">
<p className="text-xs text-attn leading-snug">
{t('duplicate_name_warning')}
</p>
</div>
)}
{!isConvertible && (
<div className="rounded-lg border border-warning/30 bg-warning/[0.03] px-3 py-2">
<p className="text-xs text-warning-foreground leading-snug">
<div className="rounded-lg border border-border bg-muted/30 px-3 py-2">
<p className="text-xs text-attn leading-snug">
{t('unconvertible_hint')}
</p>
</div>
@@ -946,10 +946,10 @@ export default function NewSupplierInvoiceForm({
return (
<div
role="status"
className="flex items-start gap-2 rounded-lg border border-warning/30 bg-warning/10 p-3"
className="flex items-start gap-2 rounded-lg border border-border bg-muted/30 p-3"
>
<AlertTriangle className="h-4 w-4 text-warning-foreground mt-0.5 shrink-0" />
<p className="flex-1 text-sm text-warning-foreground">
<AlertTriangle className="h-4 w-4 text-attn mt-0.5 shrink-0" />
<p className="flex-1 text-sm text-attn">
{t('slp_hint', { amount: formatAmount(slpAmount) })}
</p>
<Button
@@ -1980,10 +1980,10 @@ export default function NewSupplierInvoiceForm({
{rcAccountWarningRows.length > 0 && (
<div
role="status"
className="mb-4 flex items-start gap-2 rounded-lg border border-warning/30 bg-warning/10 p-3"
className="mb-4 flex items-start gap-2 rounded-lg border border-border bg-muted/30 p-3"
>
<AlertTriangle className="h-4 w-4 text-warning-foreground mt-0.5 shrink-0" />
<p className="text-sm text-warning-foreground">
<AlertTriangle className="h-4 w-4 text-attn mt-0.5 shrink-0" />
<p className="text-sm text-attn">
{t('rc_account_warning', {
count: rcAccountWarningRows.length,
rows: rcAccountWarningRows.map((i) => i + 1).join(', '),
@@ -1998,10 +1998,10 @@ export default function NewSupplierInvoiceForm({
{foreignZeroVatRows.length > 0 && (
<div
role="status"
className="mb-4 flex items-start gap-2 rounded-lg border border-warning/30 bg-warning/10 p-3"
className="mb-4 flex items-start gap-2 rounded-lg border border-border bg-muted/30 p-3"
>
<AlertTriangle className="h-4 w-4 text-warning-foreground mt-0.5 shrink-0" />
<p className="text-sm text-warning-foreground">
<AlertTriangle className="h-4 w-4 text-attn mt-0.5 shrink-0" />
<p className="text-sm text-attn">
{t('foreign_zero_vat_warning', {
count: foreignZeroVatRows.length,
rows: foreignZeroVatRows.map((i) => i + 1).join(', '),
@@ -123,10 +123,12 @@ export default function BankSyncStatusChip() {
}
if (state.kind === 'stale') {
// Same neutral shape as the healthy chip: the ochre text is the signal,
// never an amber box (convention 12: status colors are data, not chrome).
return (
<Link
href="/settings/banking"
className="inline-flex items-center gap-1.5 rounded-md border border-warning/40 bg-warning/5 px-2.5 py-1 text-xs text-warning transition-colors hover:bg-warning/10"
className="inline-flex items-center gap-1.5 rounded-md border border-border bg-muted/30 px-2.5 py-1 text-xs text-attn transition-colors hover:bg-muted/50"
>
<AlertTriangle className="h-3.5 w-3.5" />
<span>
@@ -73,9 +73,9 @@ export default function BatchCategorySelector({
) : (
<div className="space-y-4 py-2">
{/* Underlag reminder */}
<div className="flex items-start gap-2 rounded-lg bg-warning/10 border border-warning/30 p-3">
<Paperclip className="h-4 w-4 text-warning-foreground mt-0.5 shrink-0" />
<p className="text-xs text-warning-foreground">
<div className="flex items-start gap-2 rounded-lg bg-muted/30 border border-border p-3">
<Paperclip className="h-4 w-4 text-attn mt-0.5 shrink-0" />
<p className="text-xs text-attn">
{t('underlag_reminder')}
</p>
</div>
@@ -199,8 +199,8 @@ export default function DuplicateBookingDialog({
null amount already shows dialog_duplicate_amount_unknown. */}
{candidate.amount == null && candidate.currency && (
<div className="flex items-start gap-2 pt-1">
<AlertTriangle className="h-3.5 w-3.5 text-warning-foreground flex-shrink-0 mt-0.5" />
<p className="text-xs text-warning-foreground leading-snug">
<AlertTriangle className="h-3.5 w-3.5 text-attn flex-shrink-0 mt-0.5" />
<p className="text-xs text-attn leading-snug">
{t('dialog_duplicate_sek_unavailable', { currency: candidate.currency })}
</p>
</div>
@@ -211,8 +211,8 @@ export default function DuplicateBookingDialog({
match rests on date + account + direction alone. */}
{candidate.amount != null && !candidate.amount_verified && (
<div className="flex items-start gap-2 pt-1">
<AlertTriangle className="h-3.5 w-3.5 text-warning-foreground flex-shrink-0 mt-0.5" />
<p className="text-xs text-warning-foreground leading-snug">
<AlertTriangle className="h-3.5 w-3.5 text-attn flex-shrink-0 mt-0.5" />
<p className="text-xs text-attn leading-snug">
{t('dialog_duplicate_amount_unverified')}
</p>
</div>
@@ -404,11 +404,11 @@ export default function InvoiceMatchDialog({
<div className="space-y-4">
{/* Duplicate-payment warning: customer-side only, only when a candidate exists */}
{!targetBlocked && candidate && isCustomerInvoice && (
<div className="rounded-lg border border-warning/40 bg-warning/10 p-4 space-y-3">
<div className="rounded-lg border border-border bg-muted/30 p-4 space-y-3">
<div className="flex items-start gap-2">
<AlertTriangle className="h-4 w-4 flex-shrink-0 mt-0.5 text-warning-foreground" />
<AlertTriangle className="h-4 w-4 flex-shrink-0 mt-0.5 text-attn" />
<div className="text-sm space-y-1">
<p className="font-medium text-warning-foreground">{t('duplicate_title')}</p>
<p className="font-medium text-attn">{t('duplicate_title')}</p>
{/* candidate.amount is the voucher leg's SEK debit
(duplicate-payment-detection.ts), so it is formatted as
SEK regardless of the transaction's currency: an
@@ -553,7 +553,7 @@ export default function InvoiceMatchDialog({
if (targetBlocked) {
const isSettled = targetMatchState === 'settled'
return (
<div className="flex items-start gap-2 p-3 rounded-lg bg-warning/10 text-warning-foreground">
<div className="flex items-start gap-2 p-3 rounded-lg bg-muted/30 text-attn">
<AlertTriangle className="h-4 w-4 flex-shrink-0 mt-0.5" />
<div className="text-sm">
<p className="font-medium">
@@ -617,7 +617,7 @@ export default function InvoiceMatchDialog({
}
return (
<div className="flex items-start gap-2 p-3 rounded-lg bg-warning/10 text-warning-foreground">
<div className="flex items-start gap-2 p-3 rounded-lg bg-muted/30 text-attn">
<AlertTriangle className="h-4 w-4 flex-shrink-0 mt-0.5" />
<div className="text-sm">
<p className="font-medium">{t('amounts_differ')}</p>
@@ -652,9 +652,9 @@ export default function InvoiceMatchDialog({
rather than inside the Valutaomräkning card below, because in
this state the preview 400s and that card never renders. */}
{!targetBlocked && invoiceRateMissing && (
<div className="rounded-lg border border-warning/40 bg-warning/5 p-4">
<div className="rounded-lg border border-border bg-muted/30 p-4">
<div className="flex items-start gap-2">
<AlertTriangle className="h-4 w-4 mt-0.5 text-warning-foreground flex-shrink-0" />
<AlertTriangle className="h-4 w-4 mt-0.5 text-attn flex-shrink-0" />
<div className="flex-1 text-sm">
{/* Untinted title, matching the sibling
fx_rate_unavailable panel below: the ochre lives in the
@@ -691,9 +691,9 @@ export default function InvoiceMatchDialog({
if ('error' in fx) {
// Riksbanken unavailable: show manual rate input.
return (
<div className="rounded-lg border border-warning/40 bg-warning/5 p-4 space-y-3">
<div className="rounded-lg border border-border bg-muted/30 p-4 space-y-3">
<div className="flex items-start gap-2">
<AlertTriangle className="h-4 w-4 mt-0.5 text-warning-foreground flex-shrink-0" />
<AlertTriangle className="h-4 w-4 mt-0.5 text-attn flex-shrink-0" />
<div className="flex-1 text-sm">
<p className="font-medium">{t('fx_rate_unavailable_title')}</p>
<p className="text-muted-foreground mt-1">
+1 -1
View File
@@ -182,7 +182,7 @@ export default function InvoicePicker({ transaction, onSelect, isProcessing }: I
</span>
)}
{invoice.status === 'partially_paid' && (
<span className="text-[10px] uppercase tracking-wide text-warning-foreground">
<span className="text-[10px] uppercase tracking-wide text-attn">
{t('status_partially_paid')}
</span>
)}
@@ -458,7 +458,7 @@ export default function MatchAllocationDialog({
≈ {formatCurrency(parseAmount(draft.amount) / c.exchangeRate, c.currency)}
</p>
) : (
<p className="text-[11px] tabular-nums text-warning-foreground">
<p className="text-[11px] tabular-nums text-attn">
{t('fx_rate_missing_warning', { currency: c.currency })}
</p>
)
@@ -516,7 +516,7 @@ export default function MatchAllocationDialog({
// must equal the bank's actual receipt or reconciliation
// breaks. The user must allocate the full amount or remove
// selections. PR #607 round-1 review fix.
<div className="flex items-start gap-2 rounded-lg bg-warning/10 p-3 text-sm text-warning-foreground">
<div className="flex items-start gap-2 rounded-lg bg-muted/30 p-3 text-sm text-attn">
<AlertTriangle className="h-4 w-4 flex-shrink-0 mt-0.5" />
<p>
{t('undershoot_warning', {
@@ -443,8 +443,8 @@ export default function QuickReviewDialog({
{/* Template special rules */}
{template?.special_rules_sv && (
<div className="rounded-lg border border-warning/30 bg-warning/[0.03] px-3 py-2">
<p className="text-xs text-warning-foreground leading-snug">
<div className="rounded-lg border border-border bg-muted/30 px-3 py-2">
<p className="text-xs text-attn leading-snug">
{template.special_rules_sv}
</p>
</div>
@@ -461,10 +461,10 @@ export default function QuickReviewDialog({
{/* Reverse charge warning */}
{template?.requires_vat_registration_data && (
<div className="rounded-lg border border-warning/30 bg-warning/[0.03] px-3 py-2">
<div className="rounded-lg border border-border bg-muted/30 px-3 py-2">
<div className="flex items-start gap-2">
<AlertTriangle className="h-3.5 w-3.5 text-warning-foreground flex-shrink-0 mt-0.5" />
<p className="text-xs text-warning-foreground leading-snug">
<AlertTriangle className="h-3.5 w-3.5 text-attn flex-shrink-0 mt-0.5" />
<p className="text-xs text-attn leading-snug">
{t('reverse_charge_warning')}
</p>
</div>
@@ -177,7 +177,7 @@ export default function SupplierInvoicePicker({
</span>
)}
{invoice.status === 'partially_paid' && (
<span className="text-[10px] uppercase tracking-wide text-warning-foreground">
<span className="text-[10px] uppercase tracking-wide text-attn">
Delbetald
</span>
)}
+3 -1
View File
@@ -18,8 +18,10 @@ const badgeVariants = cva(
"border-border text-foreground bg-transparent",
success:
"border-transparent bg-success/10 text-success",
// No amber fill: status colors are data, not chrome (convention 12).
// The exception reads through the ochre text on a hairline chip.
warning:
"border-transparent bg-warning/15 text-warning-foreground",
"border-border bg-transparent text-attn",
},
},
defaultVariants: {
+8 -8
View File
@@ -10,7 +10,8 @@ import {
DialogFooter,
} from '@/components/ui/dialog'
import { Button } from '@/components/ui/button'
import { ClipboardCheck, Loader2, AlertTriangle } from 'lucide-react'
import { AttnLine } from '@/components/ui/attn-line'
import { ClipboardCheck, Loader2 } from 'lucide-react'
interface ConfirmationDialogProps {
open: boolean
@@ -33,7 +34,10 @@ export function ConfirmationDialog({
onConfirm,
isSubmitting,
title,
warningText = 'En verifikation skapas och kan inte ändras efteråt.',
// No default warning: an accounting-immutability sentence used to be baked
// in here, which put it into dialogs whose authors never asked for one.
// Callers that commit a voucher directly pass their own warningText.
warningText,
confirmLabel = 'Bekräfta & skapa',
extraActions,
children,
@@ -66,12 +70,8 @@ export function ConfirmationDialog({
</div>
<div className="border-t px-4 sm:px-6 py-3 sm:py-4 space-y-3 sm:space-y-4 shrink-0">
{warningText && (
<div className="flex items-start gap-2 rounded-lg bg-warning/10 border border-warning/30 p-3">
<AlertTriangle className="h-4 w-4 text-warning-foreground mt-0.5 shrink-0" />
<p className="text-sm text-warning-foreground">{warningText}</p>
</div>
)}
{/* Attention is one ochre sentence, not a banner (convention 6). */}
{warningText && <AttnLine>{warningText}</AttnLine>}
<DialogFooter>
<Button
+4 -5
View File
@@ -62,7 +62,7 @@ export function DestructiveConfirmDialog({
'flex-shrink-0 flex items-center justify-center h-10 w-10 rounded-full',
variant === 'destructive'
? 'bg-destructive/10 text-destructive'
: 'bg-warning/10 text-warning'
: 'bg-muted text-attn'
)}
>
<AlertTriangle className="h-5 w-5" />
@@ -87,14 +87,13 @@ export function DestructiveConfirmDialog({
>
{cancelLabel}
</Button>
{/* Warning-variant confirms use the default primary button: in
chrome only --destructive survives as a colored action. */}
<Button
variant={variant === 'destructive' ? 'destructive' : 'default'}
onClick={handleConfirm}
disabled={isLoading}
className={cn(
'min-h-11 w-full sm:w-auto',
variant === 'warning' && 'bg-warning hover:bg-warning/90 text-warning-foreground'
)}
className="min-h-11 w-full sm:w-auto"
>
{isLoading ? (
<Loader2 className="mr-2 h-4 w-4 animate-spin" />