style(ui): system-wide UX/UI polish pass — design-system conformance + copy cleanup (#835)

* style(ui): system-wide UX/UI polish pass — design-system conformance + copy cleanup

Multi-agent scan of all 404 UI files against the locked design system, then
141 verified surgical fixes across 109 files (net -32 lines):

- Remove forbidden elevation/motion: shadow-* and rounded-xl on cards, active:scale
  bounce, hover:shadow on list items, transition-all -> transition-colors.
- Drop font-medium from single-weight Hedvig display headings/numerals.
- Replace raw rainbow Tailwind status colors with Badge variants / brand tokens /
  neutral surfaces (achromatic chrome, semantic colors stay data-only).
- Route raw dates through formatDate(), hand-rolled currency through formatCurrency(),
  add tabular-nums to financial figures; text-gray-* -> text-foreground tokens.
- Swap hand-rolled skeletons for the Skeleton primitive; off-scale spacing -> token scale.
- Fix copy: mislabeled "Leverantörsfakturor" -> "Utgifter" on bank-import outflow total,
  collapse no-op identical-branch ternaries, broken Swedish diacritics (mojibake),
  correct mismatch-password toast, correct supplier currency-field label.
- Remove PII-leaking debug console.log on register, stray console.logs.

Verified: tsc clean on all changed files, eslint clean, production build passes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(auth): sanitize residual error logs in register flow

Follow-up to PR review (compliance swarm V16 / GDPR Art.5(1)(f)): the
remaining console.error calls in the register flow passed raw error
objects, which Supabase may populate with PII (email) in nested fields.
Log only sanitized message strings instead.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jakob Wennberg
2026-06-30 13:14:13 +02:00
committed by GitHub
parent a8072f6423
commit b800dcd403
109 changed files with 270 additions and 302 deletions
+3 -3
View File
@@ -276,7 +276,7 @@ function LoginPageContent() {
</p>
</div>
<div className="rounded-xl border bg-card p-4">
<div className="rounded-lg border bg-card p-4">
<p className="text-sm text-muted-foreground text-center leading-relaxed">
{showResetPassword ? tAuth('email_sent_hint_reset') : tAuth('email_sent_hint_login')}
</p>
@@ -327,7 +327,7 @@ function LoginPageContent() {
</p>
</div>
<div className="rounded-xl border bg-card p-6" style={{ boxShadow: 'var(--shadow-md)' }}>
<div className="rounded-lg border bg-card p-6">
<form onSubmit={handleResetPassword} className="space-y-5">
<div className="space-y-2">
<Label htmlFor="email">{tAuth('email_label')}</Label>
@@ -382,7 +382,7 @@ function LoginPageContent() {
</p>
</div>
<div className="rounded-xl border bg-card p-6" style={{ boxShadow: 'var(--shadow-md)' }}>
<div className="rounded-lg border bg-card p-6">
{callbackError === 'auth_error' && (
<div className="mb-5 rounded-lg border border-destructive/30 bg-destructive/5 p-4">
<p className="text-sm font-medium text-destructive">
+2 -2
View File
@@ -188,7 +188,7 @@ function MfaEnrollContent() {
</p>
</div>
<div className="rounded-xl border bg-card p-6" style={{ boxShadow: 'var(--shadow-md)' }}>
<div className="rounded-lg border bg-card p-6">
<div className="space-y-4">
<div className="rounded-lg border bg-muted/50 p-4">
<p className="text-sm text-muted-foreground leading-relaxed">
@@ -242,7 +242,7 @@ function MfaEnrollContent() {
</p>
</div>
<div className="rounded-xl border bg-card p-6 space-y-6" style={{ boxShadow: 'var(--shadow-md)' }}>
<div className="rounded-lg border bg-card p-6 space-y-6">
{/* QR Code */}
<div className="flex justify-center">
<div
+1 -1
View File
@@ -171,7 +171,7 @@ function MfaVerifyContent() {
</p>
</div>
<div className="rounded-xl border bg-card p-6" style={{ boxShadow: 'var(--shadow-md)' }}>
<div className="rounded-lg border bg-card p-6">
<form onSubmit={handleVerify} className="space-y-5">
<div className="space-y-2">
<Label htmlFor="code">{t('verify_code_label')}</Label>
+8 -48
View File
@@ -140,7 +140,7 @@ function RegisterPageContent() {
})
if (error) {
console.error('[register] BankID verifyOtp failed', error)
console.error('[register] BankID verifyOtp failed', error.message)
toast({
title: t('register_failed_complete'),
description: getErrorMessage(error, { context: 'auth', locale: errorLocale }),
@@ -152,7 +152,7 @@ function RegisterPageContent() {
router.push('/select-company')
router.refresh()
} catch (error) {
console.error('[register] BankID signup error', error)
console.error('[register] BankID signup error', error instanceof Error ? error.message : String(error))
toast({
title: t('register_failed_title'),
description: getErrorMessage(error, { context: 'auth', locale: errorLocale }),
@@ -201,14 +201,6 @@ function RegisterPageContent() {
}
try {
console.log('[register] attempting signUp', {
email: emailValue,
hasPassword: !!passwordValue,
passwordLength: passwordValue.length,
redirectTo: `${window.location.origin}/auth/callback`,
supabaseUrl: process.env.NEXT_PUBLIC_SUPABASE_URL,
})
const { data, error } = await supabase.auth.signUp({
email: emailValue,
password: passwordValue,
@@ -218,15 +210,7 @@ function RegisterPageContent() {
})
if (error) {
console.error('[register] signUp error', {
message: error.message,
code: error.code,
status: error.status,
name: error.name,
stack: error.stack,
cause: error.cause,
fullError: JSON.stringify(error, Object.getOwnPropertyNames(error)),
})
console.error('[register] signUp error', error.message)
toast({
title: t('register_failed_title'),
description: getErrorMessage(error, { context: 'auth', locale: errorLocale }),
@@ -235,16 +219,6 @@ function RegisterPageContent() {
return
}
console.log('[register] signUp response', {
userId: data.user?.id,
email: data.user?.email,
isAnonymous: data.user?.is_anonymous,
identities: data.user?.identities?.length,
hasSession: !!data.session,
confirmationSentAt: data.user?.confirmation_sent_at,
provider: data.user?.app_metadata?.provider,
})
// If auto-confirmed (local dev), process invite immediately and redirect
if (data.session) {
const cookieMatch = document.cookie.match(/gnubok-invite-token=([^;]+)/)
@@ -260,19 +234,11 @@ function RegisterPageContent() {
if (res.ok) {
document.cookie = 'gnubok-invite-token=; path=/; max-age=0'
console.log('[register] invite accepted after auto-confirm — redirecting')
window.location.href = '/'
return
}
// Log the error response so we can diagnose invite failures
const errBody = await res.json().catch(() => ({}))
console.error('[register] invite acceptance returned non-ok', {
status: res.status,
error: errBody.error,
})
} catch (err) {
console.error('[register] invite acceptance failed:', err)
console.error('[register] invite acceptance failed:', err instanceof Error ? err.message : String(err))
}
}
@@ -294,13 +260,7 @@ function RegisterPageContent() {
setEmail(emailValue)
setIsRegistered(true)
} catch (error) {
console.error('[register] unexpected exception', {
error,
message: error instanceof Error ? error.message : String(error),
stack: error instanceof Error ? error.stack : undefined,
type: typeof error,
constructor: error?.constructor?.name,
})
console.error('[register] unexpected exception', error instanceof Error ? error.message : String(error))
toast({
title: t('register_failed_title'),
description: getErrorMessage(error, { context: 'auth', locale: errorLocale }),
@@ -329,7 +289,7 @@ function RegisterPageContent() {
</p>
</div>
<div className="rounded-xl border bg-card p-4">
<div className="rounded-lg border bg-card p-4">
<p className="text-sm text-muted-foreground text-center leading-relaxed">
{t('duplicate_hint')}
</p>
@@ -377,7 +337,7 @@ function RegisterPageContent() {
</p>
</div>
<div className="rounded-xl border bg-card p-4">
<div className="rounded-lg border bg-card p-4">
<p className="text-sm text-muted-foreground text-center leading-relaxed">
{t('confirm_email_hint')}
</p>
@@ -416,7 +376,7 @@ function RegisterPageContent() {
</p>
</div>
<div className="rounded-xl border bg-card p-6" style={{ boxShadow: 'var(--shadow-md)' }}>
<div className="rounded-lg border bg-card p-6">
{bankIdEnabled && !bankIdUser && (
<>
<div className="mb-5">
+2 -2
View File
@@ -40,7 +40,7 @@ export default function ResetPasswordPage() {
if (password !== confirmPassword) {
toast({
title: t('mismatch_title'),
description: t('weak_description'),
description: t('mismatch_description'),
variant: 'destructive',
})
setIsLoading(false)
@@ -102,7 +102,7 @@ export default function ResetPasswordPage() {
</p>
</div>
<div className="rounded-xl border bg-card p-6" style={{ boxShadow: 'var(--shadow-md)' }}>
<div className="rounded-lg border bg-card p-6">
<form onSubmit={handleResetPassword} className="space-y-5">
<div className="space-y-2">
<Label htmlFor="password">{t('new_password_label')}</Label>
+1 -1
View File
@@ -189,7 +189,7 @@ export default function ArticleDetailPage({
<Icon className="h-6 w-6 text-primary" />
</div>
<div>
<h1 className="font-display text-2xl md:text-3xl font-medium tracking-tight">{article.name}</h1>
<h1 className="font-display text-2xl md:text-3xl tracking-tight">{article.name}</h1>
<div className="flex items-center gap-2 mt-1">
<Badge variant="secondary">{t(ARTICLE_TYPE_KEY[article.type])}</Badge>
{article.article_number && (
+2 -2
View File
@@ -255,7 +255,7 @@ export default function JournalEntryDetailPage({ params }: { params: Promise<{ i
<div className="flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between">
<div className="space-y-1">
<div className="flex items-center gap-2 flex-wrap">
<h1 className="font-display text-2xl md:text-3xl font-medium tracking-tight font-mono">
<h1 className="font-display text-2xl md:text-3xl tracking-tight font-mono">
{formatVoucher(entry)}
</h1>
<JournalEntryStatusBadge entry={entry} />
@@ -366,7 +366,7 @@ export default function JournalEntryDetailPage({ params }: { params: Promise<{ i
{entry.committed_at && (
<div className="flex justify-between">
<span className="text-muted-foreground">{t('field_posted_at')}</span>
<span>{new Date(entry.committed_at).toLocaleDateString('sv-SE')}</span>
<span>{formatDate(entry.committed_at)}</span>
</div>
)}
<div className="flex justify-between">
@@ -808,7 +808,7 @@ function ManualEntryEditor({
<div className="rounded-md border border-border p-3 space-y-3">
<div className="flex items-center justify-between">
<p className="text-sm font-medium">{template.name}</p>
<Button variant="ghost" size="sm" onClick={onRemove} className="h-7 px-2">
<Button variant="ghost" size="sm" onClick={onRemove} className="h-7 px-2" aria-label="Ta bort">
<Trash2 className="h-3.5 w-3.5" />
</Button>
</div>
+1 -1
View File
@@ -203,7 +203,7 @@ export default function CustomerDetailPage({
<Icon className="h-6 w-6 text-primary" />
</div>
<div>
<h1 className="font-display text-2xl md:text-3xl font-medium tracking-tight">{customer.name}</h1>
<h1 className="font-display text-2xl md:text-3xl tracking-tight">{customer.name}</h1>
<Badge variant="secondary">{t(CUSTOMER_TYPE_KEY[customer.customer_type])}</Badge>
</div>
</div>
+4 -3
View File
@@ -10,6 +10,7 @@ import { DeadlineList } from '@/components/deadlines/DeadlineList'
import { PageHeader } from '@/components/ui/page-header'
import { AlertTriangle, ArrowRight } from 'lucide-react'
import { useCompany } from '@/contexts/CompanyContext'
import { formatCurrency } from '@/lib/utils'
import type { Deadline } from '@/types'
const supabase = createClient()
@@ -224,7 +225,7 @@ export default function DeadlinesPage() {
}
return (
<div className="space-y-6">
<div className="space-y-8">
<PageHeader title={t('title')} />
{/* Overdue invoices alert */}
@@ -235,8 +236,8 @@ export default function DeadlinesPage() {
<AlertTriangle className="h-4 w-4 text-destructive flex-shrink-0" />
<p className="text-sm">
<span className="font-medium">{t('overdue_invoices', { count: overdueInvoices.count })}</span>
<span className="text-muted-foreground ml-1.5">
{overdueInvoices.total.toLocaleString('sv-SE')} kr
<span className="text-muted-foreground ml-1.5 tabular-nums">
{formatCurrency(overdueInvoices.total)}
</span>
</p>
</div>
+1 -1
View File
@@ -17,7 +17,7 @@ export default function DashboardError({
return (
<div className="flex flex-col items-center justify-center min-h-[50vh] gap-4">
<h2 className="text-xl font-semibold">Nagot gick fel</h2>
<h2 className="text-xl font-semibold">Något gick fel</h2>
<p className="text-muted-foreground text-sm max-w-md text-center">
Ett oväntat fel uppstod. Försök igen eller{' '}
<SupportLink variant="inline" subject="Oväntat fel">kontakta support</SupportLink>{' '}
+7 -7
View File
@@ -210,12 +210,12 @@ const glossaryTerms: GlossaryTerm[] = [
]
const categoryConfig = {
skatt: { labelKey: 'category_skatt', icon: Calculator, color: 'bg-orange-500/10 text-orange-600' },
moms: { labelKey: 'category_moms', icon: Receipt, color: 'bg-blue-500/10 text-blue-600' },
faktura: { labelKey: 'category_faktura', icon: FileText, color: 'bg-success/10 text-success' },
bokföring: { labelKey: 'category_bokforing', icon: BookOpen, color: 'bg-purple-500/10 text-purple-600' },
bank: { labelKey: 'category_bank', icon: Building2, color: 'bg-pink-500/10 text-pink-600' },
företag: { labelKey: 'category_foretag', icon: Building2, color: 'bg-cyan-500/10 text-cyan-600' },
skatt: { labelKey: 'category_skatt', icon: Calculator, color: 'bg-secondary text-muted-foreground' },
moms: { labelKey: 'category_moms', icon: Receipt, color: 'bg-secondary text-muted-foreground' },
faktura: { labelKey: 'category_faktura', icon: FileText, color: 'bg-secondary text-muted-foreground' },
bokföring: { labelKey: 'category_bokforing', icon: BookOpen, color: 'bg-secondary text-muted-foreground' },
bank: { labelKey: 'category_bank', icon: Building2, color: 'bg-secondary text-muted-foreground' },
företag: { labelKey: 'category_foretag', icon: Building2, color: 'bg-secondary text-muted-foreground' },
}
function TermCard({ term, isExpanded, onToggle }: { term: GlossaryTerm; isExpanded: boolean; onToggle: () => void }) {
@@ -224,7 +224,7 @@ function TermCard({ term, isExpanded, onToggle }: { term: GlossaryTerm; isExpand
const CategoryIcon = config.icon
return (
<Card className={cn('transition-all', isExpanded && 'ring-2 ring-primary/20')}>
<Card className={cn('transition-colors', isExpanded && 'ring-2 ring-primary/20')}>
<CardContent className="pt-4">
<button
onClick={onToggle}
+11 -11
View File
@@ -2164,7 +2164,7 @@ export default function ImportPage() {
<div className="space-y-6">
{/* Header */}
<div>
<h1 className="font-display text-2xl md:text-3xl font-medium tracking-tight">
<h1 className="font-display text-2xl md:text-3xl tracking-tight">
{view === 'export' ? t('export_title') : t('title')}
</h1>
<p className="text-muted-foreground">
@@ -2198,10 +2198,10 @@ export default function ImportPage() {
tabIndex={isSandbox ? -1 : 0}
aria-disabled={isSandbox}
className={cn(
'group flex items-start gap-4 rounded-lg border bg-card p-5 transition-all',
'group flex items-start gap-4 rounded-lg border bg-card p-5 transition-colors',
isSandbox
? 'opacity-50 cursor-not-allowed'
: 'cursor-pointer hover:border-foreground/15 hover:shadow-[var(--shadow-sm)] active:scale-[0.998]'
: 'cursor-pointer hover:border-foreground/15'
)}
onClick={() => { if (!isSandbox) setMode('psd2') }}
onKeyDown={(e) => { if (!isSandbox && (e.key === 'Enter' || e.key === ' ')) { e.preventDefault(); setMode('psd2') } }}
@@ -2231,10 +2231,10 @@ export default function ImportPage() {
tabIndex={isSandbox ? -1 : 0}
aria-disabled={isSandbox}
className={cn(
'group rounded-lg border bg-card p-5 transition-all',
'group rounded-lg border bg-card p-5 transition-colors',
isSandbox
? 'opacity-50 cursor-not-allowed'
: 'cursor-pointer hover:border-foreground/15 hover:shadow-[var(--shadow-sm)] active:scale-[0.998]'
: 'cursor-pointer hover:border-foreground/15'
)}
onClick={() => { if (!isSandbox) setMode('migration') }}
onKeyDown={(e) => { if (!isSandbox && (e.key === 'Enter' || e.key === ' ')) { e.preventDefault(); setMode('migration') } }}
@@ -2277,8 +2277,8 @@ export default function ImportPage() {
role="button"
tabIndex={0}
className={cn(
'group flex items-start gap-4 rounded-lg border bg-card p-5 transition-all',
'cursor-pointer hover:border-foreground/15 hover:shadow-[var(--shadow-sm)] active:scale-[0.998]'
'group flex items-start gap-4 rounded-lg border bg-card p-5 transition-colors',
'cursor-pointer hover:border-foreground/15'
)}
onClick={() => setMode('bank')}
onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); setMode('bank') } }}
@@ -2307,8 +2307,8 @@ export default function ImportPage() {
role="button"
tabIndex={0}
className={cn(
'group flex items-start gap-4 rounded-lg border bg-card p-5 transition-all',
'cursor-pointer hover:border-foreground/15 hover:shadow-[var(--shadow-sm)] active:scale-[0.998]'
'group flex items-start gap-4 rounded-lg border bg-card p-5 transition-colors',
'cursor-pointer hover:border-foreground/15'
)}
onClick={() => setMode('csv_data')}
onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); setMode('csv_data') } }}
@@ -2344,8 +2344,8 @@ export default function ImportPage() {
role="button"
tabIndex={0}
className={cn(
'group flex items-start gap-4 rounded-lg border bg-card p-5 transition-all',
'cursor-pointer hover:border-foreground/15 hover:shadow-[var(--shadow-sm)] active:scale-[0.998]'
'group flex items-start gap-4 rounded-lg border bg-card p-5 transition-colors',
'cursor-pointer hover:border-foreground/15'
)}
onClick={() => setMode('sie')}
onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); setMode('sie') } }}
@@ -155,7 +155,7 @@ export default function CreateCreditNotePage({ params }: { params: Promise<{ id:
<ArrowLeft className="h-5 w-5" />
</Button>
<div>
<h1 className="font-display text-2xl md:text-3xl font-medium tracking-tight">{t('title')}</h1>
<h1 className="font-display text-2xl md:text-3xl tracking-tight">{t('title')}</h1>
<p className="text-muted-foreground">
{t('subtitle', { number: invoice.invoice_number ?? '' })}
</p>
+1 -1
View File
@@ -82,7 +82,7 @@ export default function KpiPage() {
return (
<div className="space-y-8">
<div className="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
<h1 className="font-display text-2xl md:text-3xl font-medium tracking-tight">{t('title')}</h1>
<h1 className="font-display text-2xl md:text-3xl tracking-tight">{t('title')}</h1>
<div className="flex gap-2">
<KPISettingsDialog
preferences={preferences}
+4 -4
View File
@@ -6,7 +6,7 @@ export default function DashboardLoading() {
{/* Metrics row */}
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
{[1, 2, 3, 4].map((i) => (
<div key={i} className="rounded-xl border border-border/60 bg-card p-4 space-y-2">
<div key={i} className="rounded-lg border border-border bg-card p-4 space-y-2">
<Skeleton className="h-3.5 w-20" />
<Skeleton className="h-7 w-28" />
<Skeleton className="h-3 w-16" />
@@ -17,7 +17,7 @@ export default function DashboardLoading() {
{/* Income / Expenses */}
<div className="grid md:grid-cols-2 gap-4">
{[1, 2].map((i) => (
<div key={i} className="rounded-xl border border-border/60 bg-card p-6 space-y-3">
<div key={i} className="rounded-lg border border-border bg-card p-6 space-y-3">
<Skeleton className="h-4 w-20" />
<Skeleton className="h-8 w-32" />
<Skeleton className="h-3 w-24" />
@@ -27,12 +27,12 @@ export default function DashboardLoading() {
{/* Alerts + deadlines */}
<div className="grid gap-4 md:grid-cols-2">
<div className="rounded-xl border border-border/60 bg-card p-4 space-y-3">
<div className="rounded-lg border border-border bg-card p-4 space-y-3">
<Skeleton className="h-5 w-24" />
<Skeleton className="h-4 w-full" />
<Skeleton className="h-4 w-3/4" />
</div>
<div className="rounded-xl border border-border/60 bg-card p-4 space-y-3">
<div className="rounded-lg border border-border bg-card p-4 space-y-3">
<Skeleton className="h-5 w-32" />
{[1, 2, 3].map((i) => (
<div key={i} className="flex items-center justify-between">
@@ -144,7 +144,7 @@ export default function EmployeeDetailPage({ params }: { params: Promise<{ id: s
<Link href="/salary/employees" aria-label="Tillbaka till anställda"><ArrowLeft className="h-4 w-4" /></Link>
</Button>
<div>
<h1 className="font-display text-2xl md:text-3xl font-medium tracking-tight">
<h1 className="font-display text-2xl md:text-3xl tracking-tight">
{employee.first_name} {employee.last_name}
</h1>
<p className="text-sm text-muted-foreground mt-1">
@@ -92,7 +92,7 @@ export default function NewEmployeePage() {
<Button variant="ghost" size="icon" asChild>
<Link href="/salary/employees" aria-label="Tillbaka till anställda"><ArrowLeft className="h-4 w-4" /></Link>
</Button>
<h1 className="font-display text-2xl md:text-3xl font-medium tracking-tight">Ny anställd</h1>
<h1 className="font-display text-2xl md:text-3xl tracking-tight">Ny anställd</h1>
</div>
<form onSubmit={handleSubmit} className="space-y-6">
+1 -1
View File
@@ -45,7 +45,7 @@ export default function EmployeesPage() {
<Link href="/salary" aria-label={t('back_to_payroll')}><ArrowLeft className="h-4 w-4" /></Link>
</Button>
<div>
<h1 className="font-display text-2xl md:text-3xl font-medium tracking-tight">{t('title')}</h1>
<h1 className="font-display text-2xl md:text-3xl tracking-tight">{t('title')}</h1>
<p className="text-sm text-muted-foreground mt-1">{t('registered_count', { count: employees.length })}</p>
</div>
</div>
@@ -168,7 +168,7 @@ export default function SalaryRunEmployeeDetailPage({
</Link>
<div className="flex flex-wrap items-baseline justify-between gap-2">
<div>
<h1 className="font-serif text-2xl font-medium tracking-tight">
<h1 className="font-display text-2xl tracking-tight">
{employee.first_name} {employee.last_name}
</h1>
<p className="text-sm text-muted-foreground tabular-nums">
@@ -312,7 +312,7 @@ function SummaryCard({ label, value, accent, overridden }: { label: string; valu
function AbsenceCount({ label, days }: { label: string; days: number }) {
return (
<div className="rounded-md border bg-muted/30 px-2.5 py-1.5">
<div className="rounded-md border bg-muted/30 px-3 py-2">
<div className="text-[11px] text-muted-foreground">{label}</div>
<div className="text-sm font-medium tabular-nums">{days} dagar</div>
</div>
+2 -2
View File
@@ -387,7 +387,7 @@ export default function SalaryRunDetailPage({ params }: { params: Promise<{ id:
<Link href="/salary" aria-label="Tillbaka till löner"><ArrowLeft className="h-4 w-4" /></Link>
</Button>
<div>
<h1 className="font-display text-2xl md:text-3xl font-medium tracking-tight">
<h1 className="font-display text-2xl md:text-3xl tracking-tight">
Lönekörning {periodLabel}
</h1>
<p className="text-sm text-muted-foreground mt-1">
@@ -426,7 +426,7 @@ export default function SalaryRunDetailPage({ params }: { params: Promise<{ id:
<Card key={label}>
<CardContent className="p-4">
<p className="text-xs text-muted-foreground mb-2">{label}</p>
<p className={`font-display text-xl font-medium tabular-nums leading-tight ${accent ? 'text-success' : ''}`}>
<p className={`font-display text-xl tabular-nums leading-tight ${accent ? 'text-success' : ''}`}>
{formatCurrency(value)}
</p>
</CardContent>
+1 -1
View File
@@ -61,7 +61,7 @@ export default function NewSalaryRunPage() {
<Button variant="ghost" size="icon" asChild>
<Link href="/salary" aria-label="Tillbaka till löner"><ArrowLeft className="h-4 w-4" /></Link>
</Button>
<h1 className="font-display text-2xl md:text-3xl font-medium tracking-tight">Ny lönekörning</h1>
<h1 className="font-display text-2xl md:text-3xl tracking-tight">Ny lönekörning</h1>
</div>
<form onSubmit={handleSubmit} className="space-y-6">
@@ -581,7 +581,7 @@ export default function SupplierInvoiceDetailPage() {
{invoice.delivery_date && (
<div className="flex justify-between">
<span className="text-muted-foreground">{t('delivery_date_label')}</span>
<span>{invoice.delivery_date}</span>
<span className="tabular-nums">{formatDate(invoice.delivery_date)}</span>
</div>
)}
{invoice.payment_reference && (
@@ -1439,7 +1439,7 @@ export default function NewSupplierInvoicePage() {
{/* Valuta & moms — kept inline with the line items because they
drive how each row is interpreted. Hidden defaults (SEK +
normal moms) collapse to nothing so most users don't see this. */}
<div className="mb-5 pb-5 border-b grid grid-cols-1 sm:grid-cols-3 gap-4">
<div className="mb-4 pb-4 border-b grid grid-cols-1 sm:grid-cols-3 gap-4">
<div className="space-y-1.5">
<Label className="text-xs">{t('currency_label')}</Label>
<Controller
+4 -4
View File
@@ -152,7 +152,7 @@ export default function SupplierDetailPage() {
<ArrowLeft className="h-5 w-5" />
</Button>
<div>
<h1 className="font-display text-2xl md:text-3xl font-medium tracking-tight">{supplier.name}</h1>
<h1 className="font-display text-2xl md:text-3xl tracking-tight">{supplier.name}</h1>
<p className="text-muted-foreground">
{supplierTypeLabels[supplier.supplier_type]}
{supplier.org_number && t('org_number_inline', { number: supplier.org_number })}
@@ -189,7 +189,7 @@ export default function SupplierDetailPage() {
<CardTitle className="text-sm text-muted-foreground">{t('outstanding')}</CardTitle>
</CardHeader>
<CardContent>
<p className="font-display text-2xl font-medium tabular-nums">{formatAmount(supplier.stats?.total_outstanding || 0)} kr</p>
<p className="font-display text-2xl tabular-nums">{formatAmount(supplier.stats?.total_outstanding || 0)} kr</p>
</CardContent>
</Card>
<Card>
@@ -197,7 +197,7 @@ export default function SupplierDetailPage() {
<CardTitle className="text-sm text-muted-foreground">{t('total_paid')}</CardTitle>
</CardHeader>
<CardContent>
<p className="font-display text-2xl font-medium tabular-nums">{formatAmount(supplier.stats?.total_paid || 0)} kr</p>
<p className="font-display text-2xl tabular-nums">{formatAmount(supplier.stats?.total_paid || 0)} kr</p>
</CardContent>
</Card>
<Card>
@@ -205,7 +205,7 @@ export default function SupplierDetailPage() {
<CardTitle className="text-sm text-muted-foreground">{t('invoice_count')}</CardTitle>
</CardHeader>
<CardContent>
<p className="font-display text-2xl font-medium tabular-nums">{supplier.stats?.invoice_count || 0}</p>
<p className="font-display text-2xl tabular-nums">{supplier.stats?.invoice_count || 0}</p>
</CardContent>
</Card>
</div>
+9 -8
View File
@@ -8,6 +8,7 @@ import { Card, CardContent } from '@/components/ui/card'
import { Input } from '@/components/ui/input'
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog'
import { EmptyState } from '@/components/ui/empty-state'
import { Skeleton } from '@/components/ui/skeleton'
import { ReportExportMenu } from '@/components/reports/ReportExportMenu'
import { useToast } from '@/components/ui/use-toast'
import { Plus, Search, Building2, Lock } from 'lucide-react'
@@ -114,7 +115,7 @@ export default function SuppliersPage() {
<div className="space-y-8">
<div className="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
<div>
<h1 className="font-display text-2xl md:text-3xl font-medium tracking-tight">{t('title')}</h1>
<h1 className="font-display text-2xl md:text-3xl tracking-tight">{t('title')}</h1>
<p className="text-muted-foreground">
{t('subtitle')}
</p>
@@ -168,13 +169,13 @@ export default function SuppliersPage() {
{isLoading ? (
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
{[1, 2, 3].map((i) => (
<Card key={i} className="animate-pulse">
<Card key={i}>
<CardContent className="p-6 space-y-3">
<div className="h-5 bg-muted rounded w-1/2" />
<div className="h-3 bg-muted rounded w-2/3" />
<Skeleton className="h-5 w-1/2" />
<Skeleton className="h-3 w-2/3" />
<div className="h-px bg-muted" />
<div className="h-3 bg-muted rounded w-1/2" />
<div className="h-3 bg-muted rounded w-1/3" />
<Skeleton className="h-3 w-1/2" />
<Skeleton className="h-3 w-1/3" />
</CardContent>
</Card>
))}
@@ -206,7 +207,7 @@ export default function SuppliersPage() {
const location = formatLocation(supplier)
return (
<Link key={supplier.id} href={`/suppliers/${supplier.id}`} className="group">
<Card className="h-full cursor-pointer transition-all duration-150 hover:border-foreground/20 hover:shadow-sm motion-safe:active:scale-[0.99] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2">
<Card className="h-full cursor-pointer transition-colors duration-150 hover:border-foreground/20 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2">
<CardContent className="p-6 flex flex-col h-full">
<div className="space-y-1 mb-4">
<h3 className="text-[15px] font-semibold tracking-tight leading-tight truncate group-hover:text-primary transition-colors">
@@ -217,7 +218,7 @@ export default function SuppliersPage() {
</p>
</div>
<dl className="mt-auto space-y-1.5 text-sm border-t pt-3">
<dl className="mt-auto space-y-2 text-sm border-t pt-3">
{supplier.org_number && (
<div className="flex items-baseline justify-between gap-3">
<dt className="text-xs text-muted-foreground shrink-0">{t('label_org_number')}</dt>
+3 -3
View File
@@ -2061,7 +2061,7 @@ export default function TransactionsPage() {
}
return (
<div className="space-y-6">
<div className="space-y-8">
{/* Status bar */}
<TransactionStatusBar
uncategorizedCount={totalUncategorizedCount ?? uncategorizedTransactions.length}
@@ -2091,7 +2091,7 @@ export default function TransactionsPage() {
</div>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" size="sm" className="h-9 gap-1.5 px-3 text-sm">
<Button variant="outline" size="sm" className="h-9 gap-2 px-3 text-sm">
{mode === 'inbox'
? `Att bokföra${(totalUncategorizedCount ?? uncategorizedTransactions.length) > 0 ? ` (${totalUncategorizedCount ?? uncategorizedTransactions.length})` : ''}`
: 'Alla transaktioner'}
@@ -2138,7 +2138,7 @@ export default function TransactionsPage() {
</span>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" size="sm" className="h-7 gap-1.5 px-2 text-xs">
<Button variant="outline" size="sm" className="h-7 gap-2 px-2 text-xs">
{sourceFilter === 'all'
? t('source_all', { count: uncategorizedTransactions.length + skvUnmatched.length })
: sourceFilter === 'bank'
+1 -1
View File
@@ -46,7 +46,7 @@ export default async function OnboardingLayout({
href="/settings/account"
aria-label="Kontoinställningar"
title="Kontoinställningar"
className="fixed bottom-5 right-5 z-50 flex h-10 w-10 items-center justify-center rounded-full border border-border bg-background/80 text-muted-foreground shadow-sm backdrop-blur transition-colors hover:border-foreground/40 hover:text-foreground"
className="fixed bottom-6 right-6 z-50 flex h-10 w-10 items-center justify-center rounded-full border border-border bg-background/80 text-muted-foreground shadow-sm backdrop-blur transition-colors hover:border-foreground/40 hover:text-foreground"
>
<Settings className="h-4 w-4" />
</Link>
+4 -4
View File
@@ -5,7 +5,7 @@ import { getBranding } from '@/lib/branding/service'
export function generateMetadata(): Metadata {
return {
title: `Personuppgiftsbitradesavtal - ${getBranding().appName}`,
title: `Personuppgiftsbiträdesavtal - ${getBranding().appName}`,
}
}
@@ -16,7 +16,7 @@ export default function DPAPage() {
<div className="max-w-3xl mx-auto space-y-8">
<div className="text-center space-y-2">
<h1 className="font-display text-3xl md:text-4xl tracking-tight text-foreground">
Personuppgiftsbitradesavtal (DPA)
Personuppgiftsbiträdesavtal (DPA)
</h1>
<p className="text-sm text-muted-foreground">
Enligt GDPR Art. 28 &middot; Senast uppdaterad: 2026-06-03
@@ -112,7 +112,7 @@ export default function DPAPage() {
åtkomst</li>
<li><strong>Integritetskontroll:</strong> SHA-256 checksummor för alla dokument, med
regelbunden verifiering</li>
<li><strong>Revisionslogg:</strong> Alla ändringshandelser loggas automatiskt av databasen
<li><strong>Revisionslogg:</strong> Alla ändringshändelser loggas automatiskt av databasen
(ej redigerbara)</li>
<li><strong>Oföränderlig bokföring:</strong> Bokförda verifikationer kan inte ändras eller
raderas (databasutlösare)</li>
@@ -243,7 +243,7 @@ export default function DPAPage() {
<Card>
<CardContent className="pt-6">
<p className="text-sm text-muted-foreground text-center">
Detta personuppgiftsbitradesavtal träder i kraft när du skapar ett konto
Detta personuppgiftsbiträdesavtal träder i kraft när du skapar ett konto
{' '}{appName} och gäller länge du använder tjänsten. För frågor, kontakta oss
{privacyEmail}.
</p>
+2 -2
View File
@@ -156,7 +156,7 @@ export default function InvoiceActionPage({ params }: { params: Promise<{ token:
<div className="max-w-lg mx-auto">
{/* Header */}
<div className="text-center mb-8">
<h1 className="text-2xl font-bold text-gray-900 mb-2">
<h1 className="text-2xl font-bold text-foreground mb-2">
Betalningspåminnelse
</h1>
<p className="text-muted-foreground">
@@ -268,7 +268,7 @@ export default function InvoiceActionPage({ params }: { params: Promise<{ token:
onClick={() => handleAction('disputed')}
disabled={isSubmitting}
>
<MessageSquare className="h-5 w-5 mr-3 text-orange-600" />
<MessageSquare className="h-5 w-5 mr-3 text-muted-foreground" />
<div className="text-left">
<p className="font-medium">Kontakta avsändaren</p>
<p className="text-sm text-muted-foreground font-normal">
+2 -2
View File
@@ -15,7 +15,7 @@ export default function PrivacyPolicyPage() {
<div className="min-h-screen bg-gradient-to-b from-slate-50 to-white py-12 px-4">
<div className="max-w-3xl mx-auto space-y-6">
<div className="text-center mb-8">
<h1 className="text-3xl font-bold text-gray-900 mb-2">
<h1 className="text-3xl font-bold text-foreground mb-2">
Integritetspolicy
</h1>
<p className="text-muted-foreground">
@@ -69,7 +69,7 @@ export default function PrivacyPolicyPage() {
</li>
<li>
<strong>Berättigat intresse (Art. 6.1f):</strong> Produktförbättringar, säkerhet och
bedrägeriforbud.
bedrägeribekämpning.
</li>
<li>
<strong>Samtycke (Art. 6.1a):</strong> För AI-baserade funktioner som skickar data
+1 -2
View File
@@ -181,7 +181,6 @@ function NewCompanyContent() {
return
}
console.log(LOG, 'created company', result.companyId)
toast({
title: t('toast_company_created'),
description: t('toast_switched_to_new'),
@@ -263,7 +262,7 @@ function NewCompanyContent() {
</div>
<div key={`title-${currentStep}`} className="animate-fade-in">
<h1 className="font-display text-2xl md:text-3xl font-medium tracking-tight leading-[1.1]">
<h1 className="font-display text-2xl md:text-3xl tracking-tight leading-[1.1]">
{stepInfo.title}
</h1>
<p className="text-white/40 mt-1.5 text-sm max-w-sm leading-relaxed">
+1 -1
View File
@@ -172,7 +172,7 @@ export default function InvitePage() {
</span>
</div>
<div className="animate-fade-in">
<h1 className="font-display text-2xl md:text-3xl font-medium tracking-tight leading-[1.1]">
<h1 className="font-display text-2xl md:text-3xl tracking-tight leading-[1.1]">
{error ? t('header_invalid') : t('header_invited')}
</h1>
</div>
+1 -1
View File
@@ -28,7 +28,7 @@ export default function ChatIntakeStarter() {
return (
<>
<header className="flex items-center gap-3 border-b border-border px-5 py-4 shrink-0">
<header className="flex items-center gap-3 border-b border-border px-6 py-4 shrink-0">
<AgentAvatar avatarId={identity.avatarId} size="sm" alt={agentName} />
<div className="min-w-0">
<h1 className="font-display text-lg tracking-tight truncate">{agentName} är redo</h1>
+1 -1
View File
@@ -28,7 +28,7 @@ export default function ChatNewStarter({
return (
<>
<header className="flex items-center gap-3 border-b border-border px-5 py-4 shrink-0">
<header className="flex items-center gap-3 border-b border-border px-6 py-4 shrink-0">
<AgentAvatar avatarId={identity.avatarId} size="sm" alt={agentName} />
<div className="min-w-0">
<h1 className="font-display text-lg tracking-tight truncate">{agentName}</h1>
+2 -2
View File
@@ -441,7 +441,7 @@ export function BankIdAuth({ mode, onComplete }: BankIdAuthProps) {
className="gap-1.5 text-muted-foreground"
>
<Monitor className="h-3.5 w-3.5" />
BankID pa den har enheten
BankID den här enheten
</Button>
</>
)}
@@ -449,7 +449,7 @@ export function BankIdAuth({ mode, onComplete }: BankIdAuthProps) {
{isMobile() && (
<div className="flex flex-col items-center gap-2">
<Smartphone className="h-8 w-8 text-muted-foreground" />
<p className="text-sm text-muted-foreground">Oppnar BankID-appen...</p>
<p className="text-sm text-muted-foreground">Öppnar BankID-appen...</p>
</div>
)}
+2 -2
View File
@@ -5,7 +5,7 @@ import { useTranslations } from 'next-intl'
import { Badge } from '@/components/ui/badge'
import { Info } from 'lucide-react'
import JournalEntryStatusBadge from '@/components/bookkeeping/JournalEntryStatusBadge'
import { formatDate } from '@/lib/utils'
import { formatDate, formatCurrency } from '@/lib/utils'
import { formatVoucher } from '@/lib/bookkeeping/voucher-series-resolver'
import type { JournalEntry, JournalEntryLine } from '@/types'
@@ -89,7 +89,7 @@ export default function CorrectionChain({ currentEntryId, chain }: Props) {
</Badge>
)}
<span className="ml-auto text-sm tabular-nums text-muted-foreground">
{total.toLocaleString('sv-SE', { minimumFractionDigits: 2 })} kr
{formatCurrency(total)}
</span>
</div>
{entry.description && (
@@ -207,7 +207,7 @@ export default function DocumentUploadZone({
<div
className={`
relative border-2 border-dashed rounded-lg text-center transition-colors
${compact ? 'p-3' : 'p-5'}
${compact ? 'p-3' : 'p-6'}
${isDragging ? 'border-primary bg-primary/5' : 'border-muted-foreground/25'}
${disabled ? 'pointer-events-none opacity-50' : 'cursor-pointer hover:border-primary/50'}
`}
@@ -281,6 +281,7 @@ export default function DocumentUploadZone({
<Button
variant="ghost"
size="sm"
aria-label="Ta bort fil"
className="h-6 w-6 p-0 shrink-0"
onClick={(e) => {
e.stopPropagation()
@@ -3,6 +3,7 @@
import { Badge } from '@/components/ui/badge'
import { AccountNumber } from '@/components/ui/account-number'
import { CheckCircle2, Paperclip } from 'lucide-react'
import { formatDate } from '@/lib/utils'
interface ReviewLine {
account_number: string
@@ -58,7 +59,7 @@ export function JournalEntryReviewContent({
{!hideDate && (
<div>
<span className="text-muted-foreground">Datum</span>
<p className="font-medium">{entryDate}</p>
<p className="font-medium">{formatDate(entryDate)}</p>
</div>
)}
{voucherSeries && (
@@ -84,7 +85,7 @@ export function JournalEntryReviewContent({
{(showBalanceBadge || (attachmentCount != null && attachmentCount > 0)) && (
<div className="flex items-center gap-2">
{showBalanceBadge && (
<Badge className="bg-success/10 text-success">
<Badge variant="success">
<CheckCircle2 className="h-3 w-3 mr-1" />
Debet = Kredit
</Badge>
@@ -92,7 +93,7 @@ export function JournalEntryReviewContent({
{attachmentCount != null && attachmentCount > 0 && (
<Badge variant="outline">
<Paperclip className="h-3 w-3 mr-1" />
{attachmentCount} {attachmentCount === 1 ? 'underlag' : 'underlag'}
{attachmentCount} underlag
</Badge>
)}
</div>
@@ -308,7 +308,7 @@ function ManualEntryEditor({
{entry.kind === 'manual_prepaid_expense' && 'Förutbetald kostnad'}
{entry.kind === 'manual_accrued_expense' && 'Upplupen kostnad'}
</p>
<Button variant="ghost" size="sm" onClick={onRemove} className="h-7 px-2">
<Button variant="ghost" size="sm" aria-label="Ta bort post" onClick={onRemove} className="h-7 px-2">
<Trash2 className="h-3.5 w-3.5" />
</Button>
</div>
+1 -1
View File
@@ -18,7 +18,7 @@ export default function AgentSetupBanner({ companyName }: Props) {
href="/onboarding/agent"
className="group block rounded-lg border border-border bg-card hover:bg-secondary/60 transition-colors duration-150 mb-8"
>
<div className="flex items-center gap-4 p-5">
<div className="flex items-center gap-4 p-6">
<div className="flex h-10 w-10 items-center justify-center rounded-full bg-foreground text-background shrink-0">
<MessageCircle className="h-4 w-4" />
</div>
+2 -2
View File
@@ -120,7 +120,7 @@ export default function CompanySwitcher() {
return (
<Link
href="/select-company"
className="flex items-center gap-2 w-full text-left rounded-lg border border-dashed border-border/60 hover:border-foreground/30 hover:bg-muted/40 -mx-1 px-2 py-1.5 transition-all duration-150"
className="flex items-center gap-2 w-full text-left rounded-lg border border-dashed border-border/60 hover:border-foreground/30 hover:bg-muted/40 -mx-1 px-2 py-1.5 transition-colors duration-150"
>
<Plus className="h-3.5 w-3.5 text-muted-foreground flex-shrink-0" />
<span className="text-[13px] text-muted-foreground truncate">{t('add_company')}</span>
@@ -133,7 +133,7 @@ export default function CompanySwitcher() {
<button
ref={triggerRef}
onClick={() => setOpen(!open)}
className="flex items-center gap-1.5 w-full text-left rounded-lg border border-transparent hover:border-border/60 hover:bg-muted/40 -mx-1 px-2 py-1.5 transition-all duration-150"
className="flex items-center gap-1.5 w-full text-left rounded-lg border border-transparent hover:border-border/60 hover:bg-muted/40 -mx-1 px-2 py-1.5 transition-colors duration-150"
aria-expanded={open}
aria-haspopup="listbox"
>
@@ -14,7 +14,7 @@ export default function ConsultantEmptyState({ firstName }: ConsultantEmptyState
return (
<div className="stagger-enter">
<header className="mb-16">
<h1 className="font-display text-2xl md:text-3xl font-medium tracking-tight">
<h1 className="font-display text-2xl md:text-3xl tracking-tight">
{greeting}{firstName ? `, ${firstName}` : ''}
</h1>
</header>
@@ -24,7 +24,7 @@ export default function ConsultantEmptyState({ firstName }: ConsultantEmptyState
<Building2 className="h-5 w-5 text-muted-foreground" />
</div>
<h2 className="font-display text-lg font-medium mb-2">
<h2 className="font-display text-lg mb-2">
Inga företag ännu
</h2>
<p className="text-sm text-muted-foreground mb-8">
@@ -33,7 +33,7 @@ export default function ConsultantEmptyState({ firstName }: ConsultantEmptyState
<Link
href="/onboarding"
className="inline-flex items-center gap-2 px-5 py-2.5 rounded-lg bg-primary text-primary-foreground text-sm font-medium hover:bg-primary/90 transition-colors active:scale-[0.98]"
className="inline-flex items-center gap-2 px-4 py-2 rounded-lg bg-primary text-primary-foreground text-sm font-medium hover:bg-primary/90 transition-colors"
>
<Plus className="h-4 w-4" />
Lägg till företag
+1 -1
View File
@@ -123,7 +123,7 @@ export default function DashboardContent({ companyId, summary, worklist, suggest
<section>
<Link href="/onboarding/agent" className="block group">
<Card className="transition-colors hover:border-primary/50">
<CardContent className="p-6 flex items-center gap-5">
<CardContent className="p-6 flex items-center gap-4">
<div className="flex-shrink-0 h-10 w-10 rounded-lg flex items-center justify-center bg-foreground text-background">
<MessageCircle className="h-5 w-5" />
</div>
+4 -5
View File
@@ -225,7 +225,6 @@ export default function WelcomeOnboarding({
return
}
console.log(LOG, 'onboarding completed', result.companyId)
toast({
title: t('toast_welcome_title'),
description: t('toast_company_ready'),
@@ -253,12 +252,12 @@ export default function WelcomeOnboarding({
return (
<div className="flex flex-col items-start justify-center min-h-[60vh] animate-fade-in">
<p className="text-muted-foreground/50 text-sm mb-2">{greeting}</p>
<h1 className="font-display text-4xl md:text-5xl font-medium tracking-tight leading-[1.05] mb-10">
<h1 className="font-display text-4xl md:text-5xl tracking-tight leading-[1.05] mb-10">
{t('welcome_title', { appName: branding.appName })}
</h1>
<button
onClick={() => setStarted(true)}
className="px-5 py-2.5 rounded-lg bg-foreground text-background text-sm font-medium hover:bg-foreground/85 transition-colors duration-150 active:scale-[0.98]"
className="px-4 py-2 rounded-lg bg-foreground text-background text-sm font-medium hover:bg-foreground/85 transition-colors duration-150"
>
{hasExistingCompanies ? t('add_a_company') : t('add_first_company')}
</button>
@@ -270,7 +269,7 @@ export default function WelcomeOnboarding({
<div className="stagger-enter">
{/* Greeting header */}
<header className="mb-10">
<h1 className="font-display text-2xl md:text-3xl font-medium tracking-tight">
<h1 className="font-display text-2xl md:text-3xl tracking-tight">
{greeting}{firstName ? `, ${firstName}` : ''}
</h1>
<p className="text-muted-foreground text-sm mt-1.5">
@@ -319,7 +318,7 @@ export default function WelcomeOnboarding({
</div>
</div>
<h2 className="font-display text-lg font-medium tracking-tight leading-tight">
<h2 className="font-display text-lg tracking-tight leading-tight">
{stepInfo.title}
</h2>
<p className="text-white/40 mt-1 text-sm">
+1 -1
View File
@@ -151,7 +151,7 @@ export function DeadlineCard({
{/* Date block */}
<div className="flex-shrink-0 w-12 text-center">
<span className={cn(
'block text-lg font-display font-medium leading-none tabular-nums',
'block text-lg font-display leading-none tabular-nums',
completed && 'text-muted-foreground',
)}>
{dayNum}
@@ -395,10 +395,10 @@ function ProviderStep({
<button
key={provider.id}
disabled={isDisabled}
className={`relative flex items-center gap-4 rounded-lg border p-4 text-left transition-all ${
className={`relative flex items-center gap-4 rounded-lg border p-4 text-left transition-colors ${
isDisabled
? 'cursor-not-allowed border-border/50 opacity-60'
: 'border-border hover:border-primary/50 hover:bg-accent/50 active:scale-[0.98]'
: 'border-border hover:border-primary/50 hover:bg-accent/50'
}`}
onClick={() => !isDisabled && onSelect(provider.id)}
>
@@ -1448,7 +1448,7 @@ function ResultStep({
{/* ── Header card with overall summary ── */}
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2.5">
<CardTitle className="flex items-center gap-2">
<StatusIcon status={nothingNew ? 'success' : overallIcon} />
{nothingNew ? 'Allt är uppdaterat' :
anySieFailed ? 'Migrering delvis genomförd' :
@@ -634,7 +634,7 @@ export default function BookDirectlyDialog({ open, onOpenChange, item, onSuccess
<button
type="button"
className={cn(
'w-full flex items-center justify-between gap-3 px-3 py-2.5 text-left text-sm transition-colors',
'w-full flex items-center justify-between gap-3 px-3 py-2 text-left text-sm transition-colors',
isSelected
? 'bg-primary/10 border-l-2 border-primary'
: 'border-l-2 border-transparent hover:bg-accent/40'
@@ -45,7 +45,7 @@ export default function CalendarWorkspace({ userId }: WorkspaceComponentProps) {
setCustomers(customersData || [])
} catch {
toast({
title: 'Kunde inte hamta data',
title: 'Kunde inte hämta data',
variant: 'destructive',
})
} finally {
@@ -1225,7 +1225,7 @@ function OnboardingCard({
{
done: hasAnyItem,
title: 'Ladda upp eller maila in ett underlag',
hint: 'accounted tolkar fakturan eller kvittot åt dig och fyller i fält automatiskt.',
hint: 'Accounted tolkar fakturan eller kvittot åt dig och fyller i fält automatiskt.',
},
{
done: hasResolvedItem,
@@ -1589,7 +1589,7 @@ function FieldsRail({
AI-tolkning ingår i abonnemanget
</div>
<p className="text-xs text-muted-foreground mt-1.5 leading-relaxed">
Uppgradera för att låta accounted läsa av leverantör, belopp och
Uppgradera för att låta Accounted läsa av leverantör, belopp och
moms automatiskt. Du kan fortfarande fylla i fälten manuellt eller
koppla dokumentet till en transaktion nedan.
</p>
@@ -116,7 +116,7 @@ export default function CsvImportWizard({
<Card className={className}>
<CardHeader>
<CardTitle className="text-base flex items-center gap-2">
{step === 1 && <><Upload className="h-4 w-4" /> Steg 1: Valj fil</>}
{step === 1 && <><Upload className="h-4 w-4" /> Steg 1: Välj fil</>}
{step === 2 && <><FileText className="h-4 w-4" /> Steg 2: Kolumnmappning</>}
{step === 3 && <><Check className="h-4 w-4" /> Import klar</>}
</CardTitle>
@@ -127,11 +127,11 @@ export default function CsvImportWizard({
<div className="border-2 border-dashed rounded-lg p-8 text-center">
<Upload className="h-8 w-8 mx-auto mb-2 text-muted-foreground" />
<p className="text-sm text-muted-foreground mb-3">
Valj en CSV-fil att importera
Välj en CSV-fil att importera
</p>
<Label htmlFor="csv-upload" className="cursor-pointer">
<Button variant="outline" size="sm" asChild>
<span>Valj fil</span>
<span>Välj fil</span>
</Button>
</Label>
<input
@@ -161,7 +161,7 @@ export default function CsvImportWizard({
onValueChange={(val) => setMappings(prev => ({ ...prev, [field.key]: val }))}
>
<SelectTrigger className="w-full">
<SelectValue placeholder="Valj kolumn..." />
<SelectValue placeholder="Välj kolumn..." />
</SelectTrigger>
<SelectContent>
{headers.map(h => (
@@ -218,7 +218,7 @@ export default function CsvImportWizard({
</div>
<p className="font-medium">{importCount} rader importerades</p>
<p className="text-sm text-muted-foreground mt-1">
Fran {fileName}
Från {fileName}
</p>
<Button variant="outline" size="sm" onClick={reset} className="mt-4">
Importera fler
@@ -6,7 +6,7 @@ export default function ExtensionLoadingSkeleton() {
{/* KPI cards skeleton */}
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4">
{[1, 2, 3].map(i => (
<div key={i} className="rounded-xl border p-6 space-y-2">
<div key={i} className="rounded-lg border p-6 space-y-2">
<Skeleton className="h-4 w-24" />
<Skeleton className="h-8 w-16" />
<Skeleton className="h-3 w-32" />
@@ -14,7 +14,7 @@ export default function ExtensionLoadingSkeleton() {
))}
</div>
{/* Content skeleton */}
<div className="rounded-xl border p-6 space-y-4">
<div className="rounded-lg border p-6 space-y-4">
<Skeleton className="h-6 w-48" />
<Skeleton className="h-40 w-full" />
</div>
+1 -1
View File
@@ -17,7 +17,7 @@ export default function KPICard({ label, value, suffix, trend, className }: KPIC
<CardContent className="pt-6">
<p className="text-sm text-muted-foreground">{label}</p>
<div className="flex items-baseline gap-1 mt-1">
<span className="text-2xl font-semibold tracking-tight">{value}</span>
<span className="text-2xl font-semibold tracking-tight tabular-nums">{value}</span>
{suffix && <span className="text-sm text-muted-foreground">{suffix}</span>}
</div>
{trend && (
@@ -28,7 +28,7 @@ export default function MonthlyTrendTable({
}: MonthlyTrendTableProps) {
if (rows.length === 0) {
return (
<div className={cn('rounded-xl border p-6 text-center text-sm text-muted-foreground', className)}>
<div className={cn('rounded-lg border p-6 text-center text-sm text-muted-foreground', className)}>
Ingen data att visa
</div>
)
@@ -38,7 +38,7 @@ export default function MonthlyTrendTable({
const fmt = formatValue ?? ((v: number) => v.toLocaleString('sv-SE'))
return (
<div className={cn('rounded-xl border', className)}>
<div className={cn('rounded-lg border', className)}>
<Table>
<TableHeader>
<TableRow>
+1 -1
View File
@@ -62,7 +62,7 @@ export default function SetupPrompt({ title, description, fields, onSave }: Setu
</div>
))}
<Button type="submit" className="w-full" disabled={!allFilled || isSaving}>
{isSaving ? 'Sparar...' : 'Kom igang'}
{isSaving ? 'Sparar...' : 'Kom igång'}
</Button>
</form>
</CardContent>
+1 -1
View File
@@ -343,7 +343,7 @@ function ConfidenceBadge({
}
if (confidence >= 0.9) {
return <Badge variant="default" className="bg-success">Exakt</Badge>
return <Badge variant="success">Exakt</Badge>
}
if (confidence >= 0.7) {
+1
View File
@@ -259,6 +259,7 @@ export default function ArticlesEditStep({
variant="ghost"
size="icon"
className="h-7 w-7"
aria-label="Ta bort rad"
onClick={() => deleteRow(row.id)}
>
<Trash2 className="h-3.5 w-3.5 text-muted-foreground" />
+4 -4
View File
@@ -103,7 +103,7 @@ export default function BankFileConfirmStep({
<FileText className="h-4 w-4" />
<span className="text-xs">Transaktioner</span>
</div>
<p className="text-xl font-display font-medium tabular-nums">{stats.parsed_rows}</p>
<p className="text-xl font-display tabular-nums">{stats.parsed_rows}</p>
{stats.skipped_rows > 0 && (
<p className="text-xs text-muted-foreground mt-1">
{stats.skipped_rows} rader hoppades över
@@ -125,16 +125,16 @@ export default function BankFileConfirmStep({
<div className="flex items-center gap-2 text-muted-foreground mb-1">
<span className="text-xs">Inkomster</span>
</div>
<p className="text-xl font-display font-medium tabular-nums">
<p className="text-xl font-display tabular-nums">
{formatCurrency(stats.total_income)}
</p>
</div>
<div className="p-4 bg-muted/50 rounded-lg">
<div className="flex items-center gap-2 text-muted-foreground mb-1">
<span className="text-xs">Leverantörsfakturor</span>
<span className="text-xs">Utgifter</span>
</div>
<p className="text-xl font-display font-medium tabular-nums">
<p className="text-xl font-display tabular-nums">
{formatCurrency(stats.total_expenses)}
</p>
</div>
+6 -6
View File
@@ -48,7 +48,7 @@ export default function BankFilePreviewStep({
<FileText className="h-4 w-4" />
<span className="text-sm">Transaktioner</span>
</div>
<p className="text-2xl font-display font-medium tabular-nums">{stats.parsed_rows}</p>
<p className="text-2xl font-display tabular-nums">{stats.parsed_rows}</p>
{stats.skipped_rows > 0 && (
<p className="text-xs text-muted-foreground mt-1">
{stats.skipped_rows} rader hoppades över
@@ -75,7 +75,7 @@ export default function BankFilePreviewStep({
<TrendingUp className="h-4 w-4" />
<span className="text-sm">Inkomster</span>
</div>
<p className="text-lg font-display font-medium tabular-nums">
<p className="text-lg font-display tabular-nums">
{formatCurrency(stats.total_income)}
</p>
</CardContent>
@@ -85,9 +85,9 @@ export default function BankFilePreviewStep({
<CardContent className="pt-6">
<div className="flex items-center gap-2 text-muted-foreground mb-1">
<TrendingDown className="h-4 w-4" />
<span className="text-sm">Leverantörsfakturor</span>
<span className="text-sm">Utgifter</span>
</div>
<p className="text-lg font-display font-medium tabular-nums">
<p className="text-lg font-display tabular-nums">
{formatCurrency(stats.total_expenses)}
</p>
</CardContent>
@@ -96,9 +96,9 @@ export default function BankFilePreviewStep({
{/* Warnings */}
{warnings.length > 0 && (
<Card className="border-yellow-300">
<Card>
<CardHeader className="py-3">
<CardTitle className="text-sm flex items-center gap-2 text-yellow-600">
<CardTitle className="text-sm flex items-center gap-2 text-warning">
<AlertTriangle className="h-4 w-4" />
{warnings.length} varning{warnings.length !== 1 ? 'ar' : ''}
</CardTitle>
+6 -6
View File
@@ -50,7 +50,7 @@ export default function ImportResultStep({ result, onNewImport, onUndo }: Import
return (
<div className="space-y-6">
{/* Success/Failure header */}
<Card className={result.success ? 'border-success/50' : 'border-destructive/50'}>
<Card className={result.success ? 'border-border' : 'border-destructive/50'}>
<CardHeader>
<CardTitle className="flex items-center gap-2">
{result.success ? (
@@ -116,7 +116,7 @@ export default function ImportResultStep({ result, onNewImport, onUndo }: Import
<FileText className="h-4 w-4" />
<span className="text-sm">Verifikationer skapade</span>
</div>
<p className="text-2xl font-display font-medium tabular-nums">{result.journalEntriesCreated}</p>
<p className="text-2xl font-display tabular-nums">{result.journalEntriesCreated}</p>
</CardContent>
</Card>
@@ -125,9 +125,9 @@ export default function ImportResultStep({ result, onNewImport, onUndo }: Import
<div className="flex items-center gap-2 text-muted-foreground mb-1">
<span className="text-sm">Räkenskapsår</span>
</div>
<div className="text-2xl font-display font-medium">
<div className="text-2xl font-display">
{result.fiscalPeriodId ? (
<Badge variant="default" className="bg-success">Skapat</Badge>
<Badge variant="success">Skapat</Badge>
) : (
<Badge variant="secondary">Befintligt</Badge>
)}
@@ -140,9 +140,9 @@ export default function ImportResultStep({ result, onNewImport, onUndo }: Import
<div className="flex items-center gap-2 text-muted-foreground mb-1">
<span className="text-sm">Ingående balanser</span>
</div>
<div className="text-2xl font-display font-medium">
<div className="text-2xl font-display">
{result.openingBalanceEntryId ? (
<Badge variant="default" className="bg-success">Importerade</Badge>
<Badge variant="success">Importerade</Badge>
) : (
<Badge variant="secondary">Inga</Badge>
)}
+4 -4
View File
@@ -13,7 +13,7 @@ import {
SelectTrigger,
SelectValue,
} from '@/components/ui/select'
import { formatCurrency } from '@/lib/utils'
import { formatCurrency, formatDate } from '@/lib/utils'
import {
CheckCircle,
AlertCircle,
@@ -183,7 +183,7 @@ export default function ImportReviewStep({
{preview.voucherCount} verifikationer bearbetas
</p>
</div>
<div className="text-2xl font-display font-medium tabular-nums text-muted-foreground">
<div className="text-2xl font-display tabular-nums text-muted-foreground">
{elapsed}s
</div>
<p className="text-sm text-muted-foreground max-w-sm">
@@ -224,11 +224,11 @@ export default function ImportReviewStep({
<p className="font-medium">Räkenskapsår</p>
<p className="text-sm text-muted-foreground">
{preview.fiscalYearStart
? new Date(preview.fiscalYearStart).toLocaleDateString('sv-SE')
? formatDate(preview.fiscalYearStart)
: '?'}{' '}
-{' '}
{preview.fiscalYearEnd
? new Date(preview.fiscalYearEnd).toLocaleDateString('sv-SE')
? formatDate(preview.fiscalYearEnd)
: '?'}
</p>
</div>
+5 -5
View File
@@ -103,7 +103,7 @@ export default function SIEPreviewStep({
<FileText className="h-4 w-4" />
<span className="text-sm">Konton</span>
</div>
<p className="text-2xl font-display font-medium tabular-nums">{preview.accountCount}</p>
<p className="text-2xl font-display tabular-nums">{preview.accountCount}</p>
</CardContent>
</Card>
@@ -113,7 +113,7 @@ export default function SIEPreviewStep({
<BarChart3 className="h-4 w-4" />
<span className="text-sm">Verifikationer</span>
</div>
<p className="text-2xl font-display font-medium tabular-nums">{preview.voucherCount}</p>
<p className="text-2xl font-display tabular-nums">{preview.voucherCount}</p>
</CardContent>
</Card>
@@ -122,7 +122,7 @@ export default function SIEPreviewStep({
<div className="flex items-center gap-2 text-muted-foreground mb-1">
<span className="text-sm">Transaktionsrader</span>
</div>
<p className="text-2xl font-display font-medium tabular-nums">{preview.transactionLineCount}</p>
<p className="text-2xl font-display tabular-nums">{preview.transactionLineCount}</p>
</CardContent>
</Card>
@@ -131,7 +131,7 @@ export default function SIEPreviewStep({
<div className="flex items-center gap-2 text-muted-foreground mb-1">
<span className="text-sm">IB Summa</span>
</div>
<p className="text-2xl font-display font-medium tabular-nums">{formatCurrency(preview.openingBalanceTotal)}</p>
<p className="text-2xl font-display tabular-nums">{formatCurrency(preview.openingBalanceTotal)}</p>
</CardContent>
</Card>
</div>
@@ -161,7 +161,7 @@ export default function SIEPreviewStep({
<div>
<p className="text-sm text-muted-foreground">Status</p>
{preview.trialBalance.isBalanced ? (
<Badge variant="default" className="bg-success">Balanserar</Badge>
<Badge variant="success">Balanserar</Badge>
) : (
<Badge variant="secondary">
Diff: {formatCurrency(preview.trialBalance.totalDebit - preview.trialBalance.totalCredit)}
+2 -1
View File
@@ -193,7 +193,8 @@ export default function SuppliersEditStep({
<Button
variant="ghost"
size="icon"
className="h-7 w-7"
className="h-8 w-8"
aria-label="Ta bort rad"
onClick={() => deleteRow(row.id)}
>
<Trash2 className="h-3.5 w-3.5 text-muted-foreground" />
+1 -1
View File
@@ -1113,7 +1113,7 @@ export default function InvoiceEditor(props: InvoiceEditorProps = { mode: 'creat
<ArrowLeft className="h-5 w-5" />
</Button>
<div className="flex-1 min-w-0">
<h1 className="font-display text-2xl md:text-3xl font-medium tracking-tight">
<h1 className="font-display text-2xl md:text-3xl tracking-tight">
{titleText}
{numberPreview && !isSelfBilled && (
<span className="ml-2 text-muted-foreground tabular-nums text-xl md:text-2xl">
+3 -3
View File
@@ -4,7 +4,7 @@ import { useTranslations } from 'next-intl'
import { CalendarClock } from 'lucide-react'
import { Badge } from '@/components/ui/badge'
import { Separator } from '@/components/ui/separator'
import { formatCurrency } from '@/lib/utils'
import { formatCurrency, formatDate } from '@/lib/utils'
import { getDisplayTotal } from '@/lib/invoices/rounding'
import { itemHasAccrual } from '@/lib/bookkeeping/accruals/account-suggestions'
import type { Customer, Currency } from '@/types'
@@ -108,11 +108,11 @@ export function InvoiceReviewContent({
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4 text-sm">
<div>
<span className="text-muted-foreground">{t('invoice_date')}</span>
<p className="font-medium">{invoiceDate}</p>
<p className="font-medium">{invoiceDate ? formatDate(invoiceDate) : ''}</p>
</div>
<div>
<span className="text-muted-foreground">{t('due_date')}</span>
<p className="font-medium">{dueDate}</p>
<p className="font-medium">{dueDate ? formatDate(dueDate) : ''}</p>
</div>
</div>
+1 -1
View File
@@ -86,7 +86,7 @@ export function KPIExpenseMixChart({ composition }: KPIExpenseMixChartProps) {
{t('expense_mix_total')}
</span>
<span
className="font-display text-lg font-medium tabular-nums"
className="font-display text-lg tabular-nums"
title={formatCurrency(total)}
>
{totalCompact}
+1 -1
View File
@@ -147,7 +147,7 @@ export function KPIHeroCards({ report, preferences }: KPIHeroCardsProps) {
<p className="text-xs text-muted-foreground">{t(`def_${def.id}_label`)}</p>
</InfoTooltip>
<p
className={`font-display text-2xl font-medium tabular-nums tracking-tight mt-2 ${color}`}
className={`font-display text-2xl tabular-nums tracking-tight mt-2 ${color}`}
>
{formatted}
</p>
+2 -2
View File
@@ -106,7 +106,7 @@ export function KPISettingsDialog({ preferences, onSave, saving }: KPISettingsDi
return (
<div
key={def.id}
className="rounded-lg border border-border/60 p-3"
className="rounded-lg border border-border p-3"
>
<div className="flex items-center justify-between">
<button
@@ -139,7 +139,7 @@ export function KPISettingsDialog({ preferences, onSave, saving }: KPISettingsDi
</div>
{isExpanded && (
<div className="mt-3 ml-5.5 space-y-2.5 text-xs text-muted-foreground">
<div className="mt-3 ml-5.5 space-y-2 text-xs text-muted-foreground">
<p>{t(`def_${def.id}_description`)}</p>
<div>
<p className="font-medium text-foreground/80 mb-0.5">
@@ -114,7 +114,7 @@ export default function BankIdCompanyPicker({
return (
<div className="stagger-enter">
<header className="mb-10">
<h1 className="font-display text-2xl md:text-3xl font-medium tracking-tight">
<h1 className="font-display text-2xl md:text-3xl tracking-tight">
{greeting}{firstName ? `, ${firstName}` : ''}
</h1>
<p className="text-muted-foreground text-sm mt-1.5">
+2 -2
View File
@@ -68,7 +68,7 @@ export default function Step1EntityType({ initialData, onNext, isSaving }: Step1
>
<Card
className={cn(
'relative p-4 transition-all',
'relative p-4 transition-colors',
option.disabled
? 'opacity-50 cursor-not-allowed'
: 'cursor-pointer hover:border-primary/50',
@@ -77,7 +77,7 @@ export default function Step1EntityType({ initialData, onNext, isSaving }: Step1
>
<div className="flex items-start gap-4">
<div className={cn(
'p-2.5 rounded-lg',
'p-2 rounded-lg',
isSelected ? 'bg-primary/10' : 'bg-muted/50'
)}>
<Icon className={cn(
@@ -288,7 +288,7 @@ export default function Step2CompanyDetails({
</div>
<div className="pt-4 border-t">
<h3 className="font-medium mb-4">{t('step2_address_heading')}</h3>
<h3 className="mb-4">{t('step2_address_heading')}</h3>
<div className="space-y-4">
<div className="space-y-2">
@@ -22,7 +22,7 @@ export default function Step2SectorSelection({ onNext, onBack, isSaving }: Step2
return (
<div className="space-y-6">
<div>
<h2 className="text-lg font-semibold">Vilken bransch verkar du inom?</h2>
<h2 className="text-lg">Vilken bransch verkar du inom?</h2>
<p className="text-sm text-muted-foreground mt-1">
Vi anpassar verktyg och tillägg baserat din bransch. Du kan alltid ändra detta senare.
</p>
@@ -36,7 +36,7 @@ export default function Step2SectorSelection({ onNext, onBack, isSaving }: Step2
<Card
key={sector.slug}
className={cn(
'cursor-pointer transition-all',
'cursor-pointer transition-colors',
isSelected
? 'border-primary ring-2 ring-primary/20'
: 'hover:border-primary/50'
@@ -347,7 +347,7 @@ export default function Step3TaxRegistration({
className="text-left"
>
<Card className={cn(
'p-3 transition-all cursor-pointer hover:border-primary/50',
'p-3 transition-colors cursor-pointer hover:border-primary/50',
field.value && 'border-primary ring-2 ring-primary/20'
)}>
<div className="flex items-center justify-between">
@@ -369,7 +369,7 @@ export default function Step3TaxRegistration({
className="text-left"
>
<Card className={cn(
'p-3 transition-all cursor-pointer hover:border-primary/50',
'p-3 transition-colors cursor-pointer hover:border-primary/50',
!field.value && 'border-primary ring-2 ring-primary/20'
)}>
<div className="flex items-center justify-between">
+1 -1
View File
@@ -127,7 +127,7 @@ export default function Step4VatAccounting({
})} className="space-y-6">
{/* VAT section */}
<div className="space-y-4">
<h3 className="font-medium flex items-center gap-2">
<h3 className="flex items-center gap-2">
<InfoTooltip
content={
<div className="space-y-2">
@@ -1063,7 +1063,7 @@ export function BankReconciliationView({ periodId, periodBounds }: BankReconcili
<tbody>
{ignoredTx.map((tx) => (
<tr key={tx.id} className="border-b last:border-0 text-muted-foreground">
<td className="py-2">{tx.date}</td>
<td className="py-2 tabular-nums">{formatDate(tx.date)}</td>
<td className="py-2 truncate max-w-[300px]">{tx.description}</td>
<td className="py-2 text-xs">
<Badge variant="outline" className="text-xs">{tx.currency}</Badge>
@@ -1123,7 +1123,7 @@ export function BankReconciliationView({ periodId, periodBounds }: BankReconcili
<tbody>
{matchedTx.map((tx) => (
<tr key={tx.id} className="border-b last:border-0">
<td className="py-2">{tx.date}</td>
<td className="py-2 tabular-nums">{formatDate(tx.date)}</td>
<td className="py-2 truncate max-w-[300px]">{tx.description}</td>
<td className="py-2 text-right tabular-nums">
{formatCurrency(tx.amount)}
+6 -6
View File
@@ -116,13 +116,13 @@ export function INK2DeclarationView({ periodId }: { periodId: string }) {
<>
{/* Warnings */}
{data.warnings.length > 0 && (
<Card className="border-orange-200 bg-orange-50">
<Card className="border-border">
<CardContent className="py-4">
<div className="flex items-start gap-2">
<AlertCircle className="h-5 w-5 text-orange-600 mt-0.5" />
<AlertCircle className="h-5 w-5 text-warning mt-0.5" />
<div>
{data.warnings.map((warning, i) => (
<p key={i} className="text-sm text-orange-800">{warning}</p>
<p key={i} className="text-sm text-foreground">{warning}</p>
))}
</div>
</div>
@@ -137,7 +137,7 @@ export function INK2DeclarationView({ periodId }: { periodId: string }) {
<CardTitle>
{data.companyInfo.companyName}
</CardTitle>
<Badge className="bg-primary/10 text-primary">
<Badge variant="secondary">
{data.fiscalYear.name}
</Badge>
</div>
@@ -170,7 +170,7 @@ export function INK2DeclarationView({ periodId }: { periodId: string }) {
<tfoot>
<tr className="border-t-2 font-semibold">
<td className="py-2">Summa tillgångar</td>
<td className="py-2 text-right">
<td className="py-2 text-right tabular-nums">
{formatCurrency(data.totals.totalAssets)}
</td>
</tr>
@@ -200,7 +200,7 @@ export function INK2DeclarationView({ periodId }: { periodId: string }) {
<tfoot>
<tr className="border-t-2 font-semibold">
<td className="py-2">Summa eget kapital och skulder</td>
<td className="py-2 text-right">
<td className="py-2 text-right tabular-nums">
{formatCurrency(data.totals.totalEquityLiabilities)}
</td>
</tr>
+9 -9
View File
@@ -94,13 +94,13 @@ export function NEDeclarationView({ periodId }: { periodId: string }) {
<>
{/* Warnings */}
{data.warnings.length > 0 && (
<Card className="border-orange-200 bg-orange-50">
<Card className="border-border">
<CardContent className="py-4">
<div className="flex items-start gap-2">
<AlertCircle className="h-5 w-5 text-orange-600 mt-0.5" />
<AlertCircle className="h-5 w-5 text-warning mt-0.5" />
<div>
{data.warnings.map((warning, i) => (
<p key={i} className="text-sm text-orange-800">{warning}</p>
<p key={i} className="text-sm text-foreground">{warning}</p>
))}
</div>
</div>
@@ -115,7 +115,7 @@ export function NEDeclarationView({ periodId }: { periodId: string }) {
<CardTitle>
{data.companyInfo.companyName}
</CardTitle>
<Badge className="bg-primary/10 text-primary">
<Badge variant="secondary">
{data.fiscalYear.name}
</Badge>
</div>
@@ -152,7 +152,7 @@ export function NEDeclarationView({ periodId }: { periodId: string }) {
<tfoot>
<tr className="border-t-2 font-semibold">
<td className="py-2">Summa intäkter</td>
<td className="py-2 text-right">
<td className="py-2 text-right tabular-nums">
{formatCurrency(
data.rutor.R1 + data.rutor.R2 + data.rutor.R3 + data.rutor.R4
)}
@@ -189,7 +189,7 @@ export function NEDeclarationView({ periodId }: { periodId: string }) {
<tfoot>
<tr className="border-t-2 font-semibold">
<td className="py-2">Summa kostnader</td>
<td className="py-2 text-right">
<td className="py-2 text-right tabular-nums">
-{formatCurrency(
data.rutor.R5 + data.rutor.R6 + data.rutor.R7 +
data.rutor.R8 + data.rutor.R9 + data.rutor.R10
@@ -207,10 +207,10 @@ export function NEDeclarationView({ periodId }: { periodId: string }) {
<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 font-medium text-xl">Årets resultat</span>
<span className="font-display text-xl">Årets resultat</span>
</div>
<span
className={`font-display text-2xl font-medium tabular-nums ${
className={`font-display text-2xl tabular-nums ${
data.rutor.R11 >= 0 ? 'text-success' : 'text-destructive'
}`}
>
@@ -266,7 +266,7 @@ function NEDeclarationRow({
</span>
)}
</td>
<td className="py-2 text-right">
<td className="py-2 text-right tabular-nums">
{isExpense && amount > 0 ? '-' : ''}{formatCurrency(Math.abs(amount))}
</td>
</tr>
@@ -200,19 +200,19 @@ export function PeriodiskSammanstallningView() {
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4">
<div>
<div className="text-xs text-muted-foreground uppercase tracking-wider">Tjänster (typ 3)</div>
<div className="font-display text-xl font-medium tabular-nums">
<div className="font-display text-xl tabular-nums">
{formatAmount(data.totals.services)} kr
</div>
</div>
<div>
<div className="text-xs text-muted-foreground uppercase tracking-wider">Varor (typ 1)</div>
<div className="font-display text-xl font-medium tabular-nums">
<div className="font-display text-xl tabular-nums">
{formatAmount(data.totals.goods)} kr
</div>
</div>
<div>
<div className="text-xs text-muted-foreground uppercase tracking-wider">Trepart (typ 2)</div>
<div className="font-display text-xl font-medium tabular-nums">
<div className="font-display text-xl tabular-nums">
{formatAmount(data.totals.triangulation)} kr
</div>
</div>
@@ -260,7 +260,7 @@ export function PeriodiskSammanstallningView() {
<CardHeader>
<CardTitle className="text-base text-destructive flex items-center gap-2">
<AlertCircle className="h-4 w-4" />
{errors.length} {errors.length === 1 ? 'fel måste' : 'fel måste'} åtgärdas
{errors.length} fel måste åtgärdas
</CardTitle>
</CardHeader>
<CardContent className="space-y-2 text-sm">
+2 -2
View File
@@ -604,7 +604,7 @@ function SkatteverketPanelInner({ periodType, year, period, hasData, rutor }: Sk
{localChecks.map((c, i) => (
<div
key={`${c.code}-${i}`}
className={`flex items-start gap-2 text-sm rounded-lg p-2.5 ${
className={`flex items-start gap-2 text-sm rounded-lg p-3 ${
c.status === 'ERROR'
? 'bg-destructive/5 text-destructive'
: 'bg-amber-50 text-amber-800 dark:bg-amber-950/30 dark:text-amber-200'
@@ -756,7 +756,7 @@ function SkatteverketPanelInner({ periodType, year, period, hasData, rutor }: Sk
{kontroller.map((k, i) => (
<div
key={`${k.kod}-${i}`}
className={`flex items-start gap-2 text-sm rounded-lg p-2.5 ${
className={`flex items-start gap-2 text-sm rounded-lg p-3 ${
k.status === 'ERROR'
? 'bg-destructive/5 text-destructive'
: 'bg-amber-50 text-amber-800 dark:bg-amber-950/30 dark:text-amber-200'
+13 -13
View File
@@ -1599,7 +1599,7 @@ export function SupplierLedgerView({ periodId }: { periodId: string }) {
<CardTitle className="text-sm text-muted-foreground">Totalt utestående</CardTitle>
</CardHeader>
<CardContent>
<p className="font-display text-2xl font-medium tabular-nums">{formatAmount(ledger.total_outstanding)} kr</p>
<p className="font-display text-2xl tabular-nums">{formatAmount(ledger.total_outstanding)} kr</p>
<p className="text-xs text-muted-foreground">{ledger.unpaid_count} fakturor</p>
{ledger.unconverted_fx_count > 0 && (
<p className="mt-1 text-xs text-muted-foreground">
@@ -1613,7 +1613,7 @@ export function SupplierLedgerView({ periodId }: { periodId: string }) {
<CardTitle className="text-sm text-muted-foreground">Ej förfallet</CardTitle>
</CardHeader>
<CardContent>
<p className="font-display text-2xl font-medium tabular-nums text-success">{formatAmount(ledger.total_current)} kr</p>
<p className="font-display text-2xl tabular-nums text-success">{formatAmount(ledger.total_current)} kr</p>
</CardContent>
</Card>
<Card>
@@ -1621,7 +1621,7 @@ export function SupplierLedgerView({ periodId }: { periodId: string }) {
<CardTitle className="text-sm text-muted-foreground">Förfallet</CardTitle>
</CardHeader>
<CardContent>
<p className="font-display text-2xl font-medium tabular-nums text-destructive">{formatAmount(ledger.total_overdue)} kr</p>
<p className="font-display text-2xl tabular-nums text-destructive">{formatAmount(ledger.total_overdue)} kr</p>
</CardContent>
</Card>
</div>
@@ -1930,15 +1930,15 @@ export function GeneralLedgerView({ periodId, initialAccountFilter }: { periodId
{formatVoucher(line)}
</Link>
</td>
<td className="py-1.5">{line.date}</td>
<td className="py-1.5">{formatDate(line.date)}</td>
<td className="py-1.5 truncate max-w-[200px]">{line.description}</td>
<td className="py-1.5 text-right">
<td className="py-1.5 text-right tabular-nums">
{line.debit > 0 ? formatAmount(line.debit) : ''}
</td>
<td className="py-1.5 text-right">
<td className="py-1.5 text-right tabular-nums">
{line.credit > 0 ? formatAmount(line.credit) : ''}
</td>
<td className="py-1.5 text-right font-mono">{formatAmount(line.balance)}</td>
<td className="py-1.5 text-right font-mono tabular-nums">{formatAmount(line.balance)}</td>
</tr>
))}
</tbody>
@@ -2102,7 +2102,7 @@ export function JournalRegisterView({ periodId }: { periodId: string }) {
<td className="py-2 font-mono text-xs">
{formatVoucher(entry)}
</td>
<td className="py-2">{entry.date}</td>
<td className="py-2">{formatDate(entry.date)}</td>
<td className="py-2">
{entry.description}
{isReversed && (
@@ -2110,8 +2110,8 @@ export function JournalRegisterView({ periodId }: { periodId: string }) {
)}
</td>
<td className="py-2 text-xs text-muted-foreground">{entry.source_type}</td>
<td className="py-2 text-right">{formatAmount(entry.total_debit)}</td>
<td className="py-2 text-right">{formatAmount(entry.total_credit)}</td>
<td className="py-2 text-right tabular-nums">{formatAmount(entry.total_debit)}</td>
<td className="py-2 text-right tabular-nums">{formatAmount(entry.total_credit)}</td>
</tr>
{isExpanded && entry.lines.map((line, lineIndex) => (
<tr key={`${index}-${lineIndex}`} className="bg-muted/30 border-b last:border-0">
@@ -2366,7 +2366,7 @@ export function ARLedgerView({ periodId }: { periodId: string }) {
<CardTitle className="text-sm text-muted-foreground">Totalt utestående</CardTitle>
</CardHeader>
<CardContent>
<p className="font-display text-2xl font-medium tabular-nums">{formatAmount(ledger.total_outstanding)} kr</p>
<p className="font-display text-2xl tabular-nums">{formatAmount(ledger.total_outstanding)} kr</p>
<p className="text-xs text-muted-foreground">{ledger.unpaid_count} fakturor</p>
{ledger.unconverted_fx_count > 0 && (
<p className="mt-1 text-xs text-muted-foreground">
@@ -2380,7 +2380,7 @@ export function ARLedgerView({ periodId }: { periodId: string }) {
<CardTitle className="text-sm text-muted-foreground">Ej förfallet</CardTitle>
</CardHeader>
<CardContent>
<p className="font-display text-2xl font-medium tabular-nums text-success">{formatAmount(ledger.total_current)} kr</p>
<p className="font-display text-2xl tabular-nums text-success">{formatAmount(ledger.total_current)} kr</p>
</CardContent>
</Card>
<Card>
@@ -2388,7 +2388,7 @@ export function ARLedgerView({ periodId }: { periodId: string }) {
<CardTitle className="text-sm text-muted-foreground">Förfallet</CardTitle>
</CardHeader>
<CardContent>
<p className="font-display text-2xl font-medium tabular-nums text-destructive">{formatAmount(ledger.total_overdue)} kr</p>
<p className="font-display text-2xl tabular-nums text-destructive">{formatAmount(ledger.total_overdue)} kr</p>
</CardContent>
</Card>
</div>
+2 -2
View File
@@ -690,7 +690,7 @@ export function AGIPanel(props: AGIPanelProps) {
<span>Arbetsgivardeklaration (AGI)</span>
<span className="flex items-center gap-2 text-xs font-normal text-muted-foreground">
<span className="flex items-center gap-1">
<CheckCircle2 className="h-3.5 w-3.5 text-emerald-600" />
<CheckCircle2 className="h-3.5 w-3.5 text-success" />
Ansluten
</span>
{!readOnly && (
@@ -1005,7 +1005,7 @@ function StatusRow({
return (
<div className="flex items-center gap-2">
{ok ? (
<CheckCircle2 className="h-4 w-4 text-emerald-600" />
<CheckCircle2 className="h-4 w-4 text-success" />
) : (
<Link2Off className="h-4 w-4 text-muted-foreground" />
)}
+2 -2
View File
@@ -101,7 +101,7 @@ export function PaymentFilePanel({
<CardContent className="space-y-4">
{paymentFileFormat && paymentFileGeneratedAt && (
<div className="flex items-start gap-2 text-sm text-muted-foreground">
<CheckCircle2 className="h-4 w-4 mt-0.5 text-emerald-600 dark:text-emerald-400" />
<CheckCircle2 className="h-4 w-4 mt-0.5 text-success" />
<div>
Senast genererad:{' '}
<span className="text-foreground">
@@ -158,7 +158,7 @@ export function PaymentFilePanel({
</div>
))}
<p className="pt-1 text-muted-foreground border-t mt-2">
Filen innehåller både lön till anställda och beloppen är förberedda bankens BankID-signering är det enda steget kvar.
Beloppen är förifyllda kvar är att signera med BankID i banken.
</p>
</div>
)}
+2 -2
View File
@@ -139,7 +139,7 @@ export function TaxPaymentPanel({
{paymentFileGeneratedAt && (
<div className="flex items-start gap-2 text-sm text-muted-foreground">
<CheckCircle2 className="h-4 w-4 mt-0.5 text-emerald-600 dark:text-emerald-400" />
<CheckCircle2 className="h-4 w-4 mt-0.5 text-success" />
<div>
Betalfil senast genererad{' '}
<span className="text-foreground">
@@ -150,7 +150,7 @@ export function TaxPaymentPanel({
)}
{taxPaidAt && (
<div className="flex items-start gap-2 text-sm text-emerald-700 dark:text-emerald-400">
<div className="flex items-start gap-2 text-sm text-success">
<CheckCircle2 className="h-4 w-4 mt-0.5" />
<div>
Markerad som betald{' '}
+3 -3
View File
@@ -102,13 +102,13 @@ export function AccountDangerZone() {
return (
<>
<section className="space-y-4 border-t border-border/8 pt-8">
<section className="space-y-4 border-t border-border pt-8">
<h2 className="text-sm font-medium uppercase tracking-wider text-destructive/80">
{t('heading')}
</h2>
{hasBlockers && (
<div className="rounded-lg border border-border/60 bg-muted/30 p-4 space-y-3">
<div className="rounded-lg border border-border bg-muted/30 p-4 space-y-3">
<p className="text-sm font-medium">{t('blockers_title')}</p>
<p className="text-sm text-muted-foreground">
{t('blockers_description')}
@@ -117,7 +117,7 @@ export function AccountDangerZone() {
{blockers.map((b) => (
<li
key={b.id}
className="flex items-center justify-between rounded-md border border-border/40 bg-background px-3 py-2"
className="flex items-center justify-between rounded-md border border-border bg-background px-3 py-2"
>
<span className="text-sm font-medium">{b.name}</span>
<Button variant="ghost" size="sm" asChild>
+2 -2
View File
@@ -187,7 +187,7 @@ function CopyBlock({ text, copyAriaLabel }: { text: string; copyAriaLabel: strin
aria-label={copyAriaLabel}
>
{copied ? (
<Check className="h-3.5 w-3.5 text-green-600" />
<Check className="h-3.5 w-3.5 text-success" />
) : (
<Copy className="h-3.5 w-3.5" />
)}
@@ -700,7 +700,7 @@ export function ApiKeysPanel() {
onClick={handleCopy}
>
{copied ? (
<Check className="h-4 w-4 text-green-600" />
<Check className="h-4 w-4 text-success" />
) : (
<Copy className="h-4 w-4" />
)}
+1 -1
View File
@@ -243,7 +243,7 @@ export function BackupDownloadForm() {
/>
</div>
<div className="rounded-md border border-border/60 bg-muted/30 p-3 text-sm">
<div className="rounded-md border border-border bg-muted/30 p-3 text-sm">
<div className="flex items-center gap-2 text-muted-foreground">
<Info className="h-3.5 w-3.5" />
{isLoadingEstimate ? (
+1 -1
View File
@@ -105,7 +105,7 @@ export function BankIdSettings() {
<CardHeader>
<CardTitle className="flex items-center gap-2 text-base">
{identity ? (
<ShieldCheck className="h-4 w-4 text-green-600" />
<ShieldCheck className="h-4 w-4 text-success" />
) : (
<Shield className="h-4 w-4 text-muted-foreground" />
)}
+1 -1
View File
@@ -161,7 +161,7 @@ export function BankNameCombobox({ defaultValue = '', value: controlledValue, on
role="option"
aria-selected={highlightedIndex === i}
className={cn(
'flex items-center gap-2.5 px-3 py-2 text-sm cursor-pointer transition-colors',
'flex items-center gap-2 px-3 py-2 text-sm cursor-pointer transition-colors',
highlightedIndex === i && 'bg-accent text-accent-foreground',
)}
onMouseEnter={() => setHighlightedIndex(i)}
+1 -1
View File
@@ -81,7 +81,7 @@ export function CompanyDangerZone() {
return (
<>
<section className="space-y-4 border-t border-border/8 pt-8">
<section className="space-y-4 border-t border-border pt-8">
<h2 className="text-sm font-medium uppercase tracking-wider text-destructive/80">
{t('danger_heading')}
</h2>
@@ -21,8 +21,8 @@ function formatDate(iso: string | null) {
}
function confidenceColor(c: number): string {
if (c >= 0.8) return 'text-emerald-600 dark:text-emerald-400'
if (c >= 0.5) return 'text-amber-600 dark:text-amber-400'
if (c >= 0.8) return 'text-success'
if (c >= 0.5) return 'text-warning'
return 'text-muted-foreground'
}
+2 -2
View File
@@ -174,7 +174,7 @@ export function FiscalPeriodEditor() {
return (
<>
<section className="space-y-4 border-t border-border/8 pt-8">
<section className="space-y-4 border-t border-border pt-8">
<div className="space-y-1">
<h2 className="text-sm font-medium uppercase tracking-wider text-muted-foreground">
{t('fp_heading')}
@@ -286,7 +286,7 @@ function BlockedState({
: t('fp_blocked_reason_posted', { count: postedCount })
return (
<div className="rounded-lg border border-border/60 bg-muted/30 p-4 space-y-3">
<div className="rounded-lg border border-border bg-muted/30 p-4 space-y-3">
<div className="flex gap-2">
<Lock className="h-4 w-4 text-muted-foreground flex-shrink-0 mt-0.5" />
<div className="space-y-1">
+2 -2
View File
@@ -117,7 +117,7 @@ export function LogoUpload({ logoUrl, onUpdate }: LogoUploadProps) {
{preview ? (
<div className="space-y-3">
<div className="inline-block rounded-lg border border-border/60 bg-muted/30 p-4">
<div className="inline-block rounded-lg border border-border bg-muted/30 p-4">
{/* eslint-disable-next-line @next/next/no-img-element */}
<img
src={preview}
@@ -157,7 +157,7 @@ export function LogoUpload({ logoUrl, onUpdate }: LogoUploadProps) {
onDragLeave={handleDragLeave}
disabled={isUploading}
className={`flex flex-col items-center justify-center w-full max-w-xs rounded-lg border-2 border-dashed py-8 px-4 text-center transition-colors disabled:opacity-50 ${
isDragging ? 'border-foreground bg-muted/40' : 'border-border/60 hover:border-border hover:bg-muted/20'
isDragging ? 'border-foreground bg-muted/40' : 'border-border hover:border-border hover:bg-muted/20'
}`}
>
{isUploading ? (
+4 -4
View File
@@ -272,11 +272,11 @@ export function SecuritySettings() {
</div>
) : hasMfa ? (
<div className="space-y-4">
<div className="flex items-center gap-3 p-4 rounded-lg border bg-green-50 dark:bg-green-950/20 border-green-200 dark:border-green-900">
<ShieldCheck className="h-5 w-5 text-green-600 dark:text-green-500" />
<div className="flex items-center gap-3 p-4 rounded-lg border border-border bg-secondary">
<ShieldCheck className="h-5 w-5 text-success" />
<div>
<p className="font-medium text-green-900 dark:text-green-100">{t('mfa_active_title')}</p>
<p className="text-sm text-green-700 dark:text-green-400">
<p className="font-medium text-foreground">{t('mfa_active_title')}</p>
<p className="text-sm text-muted-foreground">
{t('mfa_active_description')}
</p>
</div>
+1 -1
View File
@@ -67,7 +67,7 @@ export function SettingsRail({ variant, display, activeId }: SettingsRailProps)
}
return (
<nav aria-label={t('aria_label')} className="space-y-5">
<nav aria-label={t('aria_label')} className="space-y-6">
{groups.map((g) => (
<div key={g.key} className="space-y-1">
<p className="px-3 text-[11px] font-medium uppercase tracking-wider text-muted-foreground">
@@ -108,7 +108,7 @@ export function SkatteverketConnectPanel() {
</CardHeader>
<CardContent className="space-y-4">
{status?.disabled && (
<div className="flex gap-2 rounded-md border border-amber-200 bg-amber-50 p-3 text-sm text-amber-900 dark:border-amber-800 dark:bg-amber-950 dark:text-amber-100">
<div className="flex gap-2 rounded-md border border-border bg-secondary/40 p-3 text-sm text-foreground">
<ShieldAlert className="h-4 w-4 mt-0.5 shrink-0" />
<p>{t('disabled_message')}</p>
</div>
@@ -211,7 +211,7 @@ export function SkatteverketConnectPanel() {
</div>
{status.disabled && (
<div className="flex gap-2 rounded-md border border-amber-200 bg-amber-50 p-3 text-sm text-amber-900 dark:border-amber-800 dark:bg-amber-950 dark:text-amber-100">
<div className="flex gap-2 rounded-md border border-border bg-secondary/40 p-3 text-sm text-foreground">
<ShieldAlert className="h-4 w-4 mt-0.5 shrink-0" />
<p>{t('disabled_filings_message')}</p>
</div>
+4 -4
View File
@@ -45,7 +45,7 @@ export function TaxSettingsForm({ settings }: TaxSettingsFormProps) {
</section>
{/* F-skatt */}
<section className="border-t border-border/8 pt-8 space-y-4">
<section className="border-t border-border pt-8 space-y-4">
<h2 className="text-sm font-medium uppercase tracking-wider text-muted-foreground">
{t('tax_vat_heading')}
</h2>
@@ -140,7 +140,7 @@ export function TaxSettingsForm({ settings }: TaxSettingsFormProps) {
</section>
{/* Tax contact — required for SKV-filings */}
<section className="border-t border-border/8 pt-8 space-y-4">
<section className="border-t border-border pt-8 space-y-4">
<h2 className="text-sm font-medium uppercase tracking-wider text-muted-foreground">
{t('tax_contact_heading')}
</h2>
@@ -181,7 +181,7 @@ export function TaxSettingsForm({ settings }: TaxSettingsFormProps) {
</section>
{/* Fiscal year & salaries */}
<section className="border-t border-border/8 pt-8 space-y-4">
<section className="border-t border-border pt-8 space-y-4">
<h2 className="text-sm font-medium uppercase tracking-wider text-muted-foreground">
{t('fiscal_year_salaries_heading')}
</h2>
@@ -235,7 +235,7 @@ export function TaxSettingsForm({ settings }: TaxSettingsFormProps) {
</section>
{/* Preliminary tax */}
<section className="border-t border-border/8 pt-8 space-y-4">
<section className="border-t border-border pt-8 space-y-4">
<h2 className="text-sm font-medium uppercase tracking-wider text-muted-foreground">
{t('preliminary_tax_heading')}
</h2>
@@ -164,7 +164,7 @@ export function AccountSettingsContent() {
key={value}
type="button"
onClick={() => setTheme(value)}
className={`flex items-center gap-2 rounded-lg border-2 px-4 py-2.5 text-sm font-medium transition-colors ${
className={`flex items-center gap-2 rounded-lg border-2 px-4 py-3 text-sm font-medium transition-colors ${
theme === value
? 'border-primary bg-primary/5'
: 'border-border hover:border-primary/40'
@@ -193,7 +193,7 @@ export function AccountSettingsContent() {
type="button"
onClick={() => handleLocaleChange(value)}
disabled={savingLocale}
className={`flex items-center gap-2 rounded-lg border-2 px-4 py-2.5 text-sm font-medium transition-colors disabled:opacity-50 ${
className={`flex items-center gap-2 rounded-lg border-2 px-4 py-3 text-sm font-medium transition-colors disabled:opacity-50 ${
activeLocale === value
? 'border-primary bg-primary/5'
: 'border-border hover:border-primary/40'
@@ -21,7 +21,7 @@ import {
TableRow,
} from '@/components/ui/table'
import { useToast } from '@/components/ui/use-toast'
import { formatCurrency } from '@/lib/utils'
import { formatCurrency, formatDate } from '@/lib/utils'
import { formatVoucher } from '@/lib/bookkeeping/voucher-series-resolver'
import type { StoredSkattekontoTransaction } from '@/types/skatteverket'
@@ -135,7 +135,7 @@ export function SkattekontoMatchDialog({
<DialogDescription>
{row && (
<>
{row.transaktionsdatum} {row.transaktionstext} {' '}
{formatDate(row.transaktionsdatum)} {row.transaktionstext} {' '}
<span className="tabular-nums">
{formatCurrency(Number(row.belopp_skatteverket))}
</span>
@@ -176,7 +176,7 @@ export function SkattekontoMatchDialog({
<TableBody>
{candidates.map(c => (
<TableRow key={c.journal_entry_id}>
<TableCell className="tabular-nums">{c.entry_date}</TableCell>
<TableCell className="tabular-nums">{formatDate(c.entry_date)}</TableCell>
<TableCell className="tabular-nums">
{formatVoucher(c)}
</TableCell>
+1 -1
View File
@@ -243,7 +243,7 @@ export default function SupplierForm({
/>
</div>
<div className="space-y-2">
<Label htmlFor="default_currency">{t('default_account_label')}</Label>
<Label htmlFor="default_currency">{t('default_currency_label')}</Label>
<Controller
name="default_currency"
control={control}
@@ -5,7 +5,7 @@ import { CalendarClock } from 'lucide-react'
import { Badge } from '@/components/ui/badge'
import { Separator } from '@/components/ui/separator'
import { AccountNumber } from '@/components/ui/account-number'
import { formatCurrency } from '@/lib/utils'
import { formatCurrency, formatDate } from '@/lib/utils'
import {
resolveReverseChargeRate,
isReverseChargeBasisAccount,
@@ -276,16 +276,16 @@ export function SupplierInvoiceReviewContent({
<div className="grid grid-cols-2 sm:grid-cols-3 gap-3 sm:gap-4 text-sm">
<div>
<span className="text-muted-foreground">{t('invoice_date_label')}</span>
<p className="font-medium">{invoiceDate}</p>
<p className="font-medium">{formatDate(invoiceDate)}</p>
</div>
<div>
<span className="text-muted-foreground">{t('due_date_label')}</span>
<p className="font-medium">{dueDate}</p>
<p className="font-medium">{formatDate(dueDate)}</p>
</div>
{deliveryDate && (
<div>
<span className="text-muted-foreground">{t('delivery_date_label')}</span>
<p className="font-medium">{deliveryDate}</p>
<p className="font-medium">{formatDate(deliveryDate)}</p>
</div>
)}
</div>
@@ -380,21 +380,21 @@ export function SupplierInvoiceReviewContent({
<div className="space-y-2 text-sm">
<div className="flex justify-between">
<span className="text-muted-foreground">{t('net_excl_vat')}</span>
<span>{formatCurrency(subtotal, currency)}</span>
<span className="tabular-nums">{formatCurrency(subtotal, currency)}</span>
</div>
<div className="flex justify-between">
<span className="text-muted-foreground">{reverseCharge ? t('vat_reverse_charge') : t('vat_label_short')}</span>
<span>{formatCurrency(totalVat, currency)}</span>
<span className="tabular-nums">{formatCurrency(totalVat, currency)}</span>
</div>
<Separator />
<div className="flex justify-between font-bold text-xl sm:text-2xl">
<span>{t('total_label')}</span>
<span>{formatCurrency(total, currency)}</span>
<span className="tabular-nums">{formatCurrency(total, currency)}</span>
</div>
{currency !== 'SEK' && exchangeRate && (
<div className="flex justify-between text-muted-foreground">
<span>{t('review_sek_amount_at_rate', { rate: exchangeRate })}</span>
<span>{formatCurrency(total * parseFloat(exchangeRate))}</span>
<span className="tabular-nums">{formatCurrency(total * parseFloat(exchangeRate))}</span>
</div>
)}
</div>

Some files were not shown because too many files have changed in this diff Show More