refactor(ui): app-wide UI/UX consistency pass (#436)
* refactor(ui): app-wide UI/UX consistency pass
Net: +1,159 / −1,373 LOC across 77 files. No new features, no behavior
changes. Locks in a uniform design system across every dashboard surface.
What changed:
- **Foundation**: sidebar width 232→256px (md:w-64), spacing scale locked
(Tailwind 1/2/3/4/6/8/10/12; 2.5/5 forbidden), card padding p-6 default
(p-4 for compact metric cards), space-y-8 between page sections.
- **Tables unified**: all 33 thead blocks now share the Resultatrapport
pattern via shadcn Table primitive (text-[11px] font-medium uppercase
tracking-wider text-muted-foreground). Hand-rolled <table> instances
converted where they were data tables; form/edit grids kept distinct.
- **Status badges unified**: every status indicator routes through
shadcn <Badge variant>. Eliminated raw Tailwind colors
(bg-amber-100, bg-emerald-500/10, bg-blue-100, bg-purple-100, etc.)
in favor of the gnubok semantic palette (success=sage, warning=ochre,
destructive=terracotta).
- **Empty states unified**: list pages migrated from hand-rolled
"flex flex-col items-center py-12" divs to the EmptyState primitive.
- **Loading skeletons unified**: hand-rolled bg-muted rounded animate-pulse
divs replaced with shadcn <Skeleton> across 15 files.
- **Touch targets**: 6 back-buttons + edit-pencil + inbox delete bumped
from 24/32/36px to shadcn's 40px icon default. Added aria-labels on
9 icon-only navigation buttons.
- **Date formatting**: formatDate() for accounting data (ISO yyyy-MM-dd,
table-friendly) vs formatDateLong() for metadata (Swedish long form).
Raw {x.invoice_date} renderings routed through formatDate() in 18 sites.
- **Toast titles**: eliminated 33 generic "Fel" titles. Each toast title
now carries the action ("Kunde inte skapa lönekörning" etc.) with
description carrying the error detail.
- **Page-level cleanups**:
- Dashboard: dropped greeting hero + Snabbåtgärder/Att hantera nav
duplicates + Visa detaljer collapsible.
- Reports: 5-col mega-menu replaced with left-rail layout
(new ReportsNav component).
- Bookkeeping: fixed layout jump between Verifikationer/Ny verifikation
tabs (moved FiscalYearSelector inside journal tab).
- Bookkeeping: added voucher sort (A1 first / latest first) alongside
existing date sort. Required matching API param sort_by.
- KPI page: FiscalYearSelector instead of raw <select>; InfoTooltip
instead of inline info-button toggle; bigger numbers.
- Salary section: enum values translated to Swedish labels, mobile
table collapses to Anställd+Netto on <md, KPI typography aligned
with dashboard.
- Invoice forms: styled RequiredMark + aria-required, tabular-nums
on amount inputs.
- **CLAUDE.md**: new "Design System Tokens" subsection documents the
locked spacing scale, primitives table, typography rules, date helpers,
and forbidden patterns so future contributors don't drift.
Tests: 2,906 passing (unchanged). Lint: unchanged from main baseline.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix: address PR review feedback (Greptile + compliance bot)
- **formatDate / formatDateLong timezone fix**: switch from new Date() to
parseISO. Bare yyyy-MM-dd strings are now parsed as local midnight rather
than UTC midnight, eliminating the off-by-one display in west-of-UTC
timezones flagged by Greptile.
- **DashboardContentProps cleanup**: removed unused firstName and settings
fields from the interface, and the corresponding fetch (profiles table)
+ computation in app/(dashboard)/page.tsx. The greeting was dropped in
the dashboard cleanup; these props were dead weight.
- **Voucher sort behavior documented**: extended the comment in the journal
entries API route to explain why voucher sort intentionally uses strict
fiscal_period_id filtering (BFL 5 kap 6–7 §§ — voucher numbers are
series-scoped within a fiscal year). The row-count delta between date
sort and voucher sort is now a documented design choice.
- **delete_last_voucher migration + draft-delete test included**: the UI
already shipped the "Radera utkast" path in the previous commit; this
pulls in the backing RPC migration that allows draft deletes (with the
full safety logic — drafts skip series/period checks since they have
voucher_number=0, posted entries go through the existing unchanged
path). This was originally meant for a separate PR but the UI shipped
half the feature without it.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(migration): rename to match applied version
The function delete_last_voucher is already applied to the production DB
under version 20260509103736 (verified via pg_get_functiondef — exact
byte-for-byte match to file content). The previous file timestamp
20260509120000 would cause a fresh `supabase db push` to attempt re-applying
under a different version row. Renaming the file aligns local tracking
with what the database actually has.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix: address compliance bot findings (payroll label + VAT visibility)
- sick_karens label: drop "(första sjukdagen)" qualifier. Per sjuklönelagen
6 §, karensavdrag is a single calculated amount (20% of one week's
sjuklön) deducted from the first sick day's pay — not bounded to the
first day. The qualifier could mislead users when the first sick day
and return-to-work span a weekend. Swedish-payroll bot recommendation.
- Omvänd skattskyldighet badge: variant outline → warning. The reverse-
charge indicator is compliance-critical (ML 16 kap) — missing it leads
to incorrect input VAT deduction. Outline was too subtle; warning's
ochre fill matches its semantic weight.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
ed9bdc4c00
commit
d0fbc2b616
@@ -12,23 +12,17 @@ import {
|
||||
ArrowLeftRight,
|
||||
ChevronDown,
|
||||
ChevronRight,
|
||||
Users,
|
||||
Landmark,
|
||||
CheckCircle2,
|
||||
FileWarning,
|
||||
Clock,
|
||||
} from 'lucide-react'
|
||||
import { getAllExtensions } from '@/lib/extensions/sectors'
|
||||
import { resolveIcon } from '@/lib/extensions/icon-resolver'
|
||||
import type { QuickActionDefinition } from '@/lib/extensions/types'
|
||||
import type { CompanySettings, Deadline, ReceiptQueueSummary, OnboardingProgress } from '@/types'
|
||||
import type { Deadline, ReceiptQueueSummary, OnboardingProgress } from '@/types'
|
||||
|
||||
const setupFreshStartKey = (companyId: string) => `erp_setup_fresh_start:${companyId}`
|
||||
|
||||
interface DashboardContentProps {
|
||||
firstName?: string | null
|
||||
companyId: string
|
||||
settings: CompanySettings | null
|
||||
summary: {
|
||||
ytd: { income: number; expenses: number; net: number }
|
||||
mtd: { income: number; expenses: number; net: number }
|
||||
@@ -49,20 +43,12 @@ interface DashboardContentProps {
|
||||
onboardingProgress?: OnboardingProgress
|
||||
}
|
||||
|
||||
export default function DashboardContent({ firstName, companyId, settings, summary, onboardingProgress }: DashboardContentProps) {
|
||||
export default function DashboardContent({ companyId, summary, onboardingProgress }: DashboardContentProps) {
|
||||
const [showAllAlerts, setShowAllAlerts] = useState(false)
|
||||
const [showMore, setShowMore] = useState(false)
|
||||
const [greeting, setGreeting] = useState('Hej')
|
||||
|
||||
// Setup gate — blocks dashboard until user imports data or chooses fresh start
|
||||
const needsSetup = onboardingProgress && !onboardingProgress.hasBankConnected && !onboardingProgress.hasSIEImport
|
||||
const [setupGateActive, setSetupGateActive] = useState(!!needsSetup)
|
||||
|
||||
useEffect(() => {
|
||||
const hour = new Date().getHours()
|
||||
setGreeting(hour < 5 ? 'God natt' : hour < 10 ? 'Godmorgon' : hour < 14 ? 'Hej' : hour < 18 ? 'God eftermiddag' : 'God kväll')
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (!needsSetup) {
|
||||
setSetupGateActive(false)
|
||||
@@ -100,7 +86,6 @@ export default function DashboardContent({ firstName, companyId, settings, summa
|
||||
}).format(amount)
|
||||
}
|
||||
|
||||
// Build alert items for "Att hantera" section
|
||||
const alertItems: React.ReactNode[] = []
|
||||
|
||||
if (summary.overdueInvoicesCount > 0) {
|
||||
@@ -228,19 +213,6 @@ export default function DashboardContent({ firstName, companyId, settings, summa
|
||||
const visibleAlerts = showAllAlerts ? alertItems : alertItems.slice(0, MAX_VISIBLE_ALERTS)
|
||||
const hasMoreAlerts = alertItems.length > MAX_VISIBLE_ALERTS
|
||||
|
||||
// Build extension quick actions from all compiled extensions
|
||||
const extensionQuickActions: (QuickActionDefinition & { key: string })[] = getAllExtensions()
|
||||
.filter(def => def.quickAction)
|
||||
.map(def => ({ ...def.quickAction!, key: `${def.sector}/${def.slug}` }))
|
||||
.sort((a, b) => (a.order ?? 0) - (b.order ?? 0))
|
||||
|
||||
// Quick action items
|
||||
const quickActions = [
|
||||
{ href: '/invoices/new', icon: Receipt, label: 'Ny faktura', desc: 'Skapa och skicka', accent: true },
|
||||
{ href: '/customers', icon: Users, label: 'Ny kund', desc: 'Lägg till kunduppgifter' },
|
||||
{ href: '/transactions', icon: ArrowLeftRight, label: 'Transaktioner', desc: 'Bokför' },
|
||||
]
|
||||
|
||||
const passedDeadlinesCount = summary.deadlines.filter(d => !d.is_completed && new Date(d.due_date) <= new Date()).length
|
||||
const pendingReceiptsCount = summary.receiptQueue
|
||||
? summary.receiptQueue.pending_review_count + summary.receiptQueue.unmatched_receipts_count
|
||||
@@ -248,18 +220,10 @@ export default function DashboardContent({ firstName, companyId, settings, summa
|
||||
const todoCount = summary.uncategorizedCount + summary.overdueInvoicesCount + pendingReceiptsCount + passedDeadlinesCount
|
||||
|
||||
return (
|
||||
<div className="stagger-enter">
|
||||
{/* Header */}
|
||||
<header className="mb-12">
|
||||
<h1 className="font-display text-2xl md:text-3xl font-medium tracking-tight">
|
||||
{greeting}{firstName ? `, ${firstName}` : ''}
|
||||
</h1>
|
||||
</header>
|
||||
|
||||
{/* 4 Key Summary Cards */}
|
||||
<section className="mb-10">
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-3">
|
||||
{/* Card 1: Resultat */}
|
||||
<div className="stagger-enter space-y-8">
|
||||
{/* Key metrics — 4 compact cards */}
|
||||
<section>
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
||||
<Card>
|
||||
<CardContent className="p-4">
|
||||
<p className="text-xs text-muted-foreground mb-2">Resultat</p>
|
||||
@@ -276,7 +240,6 @@ export default function DashboardContent({ firstName, companyId, settings, summa
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Card 2: Att få betalt */}
|
||||
<Link href="/invoices?status=unpaid">
|
||||
<Card className="h-full hover:border-primary/50 transition-colors cursor-pointer">
|
||||
<CardContent className="p-4">
|
||||
@@ -295,7 +258,6 @@ export default function DashboardContent({ firstName, companyId, settings, summa
|
||||
</Card>
|
||||
</Link>
|
||||
|
||||
{/* Card 3: Banksaldo */}
|
||||
{summary.bankBalance !== null ? (
|
||||
<Card>
|
||||
<CardContent className="p-4">
|
||||
@@ -315,27 +277,20 @@ export default function DashboardContent({ firstName, companyId, settings, summa
|
||||
<ChevronRight className="h-3.5 w-3.5 text-muted-foreground/50" />
|
||||
</div>
|
||||
<p className="text-sm font-medium text-primary">Koppla bank</p>
|
||||
<p className="text-xs text-muted-foreground mt-1">Importera transaktioner</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Link>
|
||||
)}
|
||||
|
||||
{/* Card 4: Att göra */}
|
||||
<Card>
|
||||
<CardContent className="p-4">
|
||||
<p className="text-xs text-muted-foreground mb-2">Att göra</p>
|
||||
<div role="status" aria-live="polite">
|
||||
{todoCount > 0 ? (
|
||||
<>
|
||||
<p className="font-display text-xl font-medium tabular-nums leading-tight text-warning-foreground">
|
||||
{todoCount}
|
||||
<span className="text-sm ml-0.5 text-muted-foreground font-normal">st</span>
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground mt-1">
|
||||
Behöver åtgärdas
|
||||
</p>
|
||||
</>
|
||||
<p className="font-display text-xl font-medium tabular-nums leading-tight text-warning-foreground">
|
||||
{todoCount}
|
||||
<span className="text-sm ml-0.5 text-muted-foreground font-normal">st</span>
|
||||
</p>
|
||||
) : (
|
||||
<div className="flex items-center gap-1.5">
|
||||
<CheckCircle2 className="h-4 w-4 text-success" />
|
||||
@@ -348,78 +303,46 @@ export default function DashboardContent({ firstName, companyId, settings, summa
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Quick actions */}
|
||||
<section id="quick-actions" className="mb-10">
|
||||
<h2 className="font-display text-lg font-medium mb-4">Snabbåtgärder</h2>
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-2.5">
|
||||
{quickActions.map((action) => {
|
||||
const Icon = action.icon
|
||||
return (
|
||||
<Link key={action.href} href={action.href} className="group">
|
||||
<div className={cn(
|
||||
'flex items-center gap-3 px-4 py-3 rounded-xl border transition-colors duration-150 active:scale-[0.98]',
|
||||
action.accent
|
||||
? 'border-primary/20 bg-primary/[0.03] hover:bg-primary/[0.06]'
|
||||
: 'border-border/40 hover:bg-muted/30'
|
||||
)}>
|
||||
<div className="min-w-0">
|
||||
<p className="text-sm font-medium truncate flex items-center gap-1.5">
|
||||
<Icon className={cn(
|
||||
'h-3.5 w-3.5 flex-shrink-0',
|
||||
action.accent ? 'text-primary' : 'text-muted-foreground'
|
||||
)} />
|
||||
{action.label}
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground truncate md:block">{action.desc}</p>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
)
|
||||
})}
|
||||
{/* Extension quick actions */}
|
||||
{extensionQuickActions.map((action) => {
|
||||
const Icon = resolveIcon(action.icon)
|
||||
if (action.href) {
|
||||
return (
|
||||
<Link key={action.key} href={action.href} className="group">
|
||||
<div className="flex items-center gap-3 px-4 py-3 rounded-xl border border-border/40 hover:bg-muted/30 active:scale-[0.98] transition-colors duration-150">
|
||||
<div className="min-w-0">
|
||||
<p className="text-sm font-medium truncate flex items-center gap-1.5">
|
||||
<Icon className="h-3.5 w-3.5 flex-shrink-0 text-muted-foreground" />
|
||||
{action.label}
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground truncate md:block">{action.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<button
|
||||
key={action.key}
|
||||
onClick={() => window.dispatchEvent(new Event(action.event!))}
|
||||
className="group text-left"
|
||||
>
|
||||
<div className="flex items-center gap-3 px-4 py-3 rounded-xl border border-border/40 hover:bg-muted/30 active:scale-[0.98] transition-colors duration-150">
|
||||
<div className="min-w-0">
|
||||
<p className="text-sm font-medium truncate flex items-center gap-1.5">
|
||||
<Icon className="h-3.5 w-3.5 flex-shrink-0 text-muted-foreground" />
|
||||
{action.label}
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground truncate md:block">{action.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
)
|
||||
})}
|
||||
{/* Resultat — intäkter / kostnader (always visible) */}
|
||||
<section>
|
||||
<div className="grid md:grid-cols-2 gap-4">
|
||||
<Card>
|
||||
<CardContent className="p-6">
|
||||
<p className="text-sm text-muted-foreground mb-3">Intäkter</p>
|
||||
<p className="font-display text-2xl font-medium tabular-nums leading-tight">
|
||||
{formatLargeNumber(summary.mtd.income)}
|
||||
<span className="text-base ml-1 text-muted-foreground font-normal">kr</span>
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground mt-0.5">denna månad</p>
|
||||
<div className="mt-4 pt-3 border-t border-border/30 flex items-baseline justify-between">
|
||||
<p className="text-xs text-muted-foreground">I år</p>
|
||||
<p className="text-sm font-medium tabular-nums">{formatCurrency(summary.ytd.income)}</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardContent className="p-6">
|
||||
<p className="text-sm text-muted-foreground mb-3">Kostnader</p>
|
||||
<p className="font-display text-2xl font-medium tabular-nums leading-tight">
|
||||
{formatLargeNumber(summary.mtd.expenses)}
|
||||
<span className="text-base ml-1 text-muted-foreground font-normal">kr</span>
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground mt-0.5">denna månad</p>
|
||||
<div className="mt-4 pt-3 border-t border-border/30 flex items-baseline justify-between">
|
||||
<p className="text-xs text-muted-foreground">I år</p>
|
||||
<p className="text-sm font-medium tabular-nums">{formatCurrency(summary.ytd.expenses)}</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Alerts section */}
|
||||
{/* Att hantera */}
|
||||
{alertItems.length > 0 && (
|
||||
<section id="alerts-section" className="mb-10">
|
||||
<section id="alerts-section">
|
||||
<h2 className="font-display text-lg font-medium mb-4">Att hantera</h2>
|
||||
<div id="alerts-list" className="grid gap-3 md:grid-cols-2">
|
||||
<div id="alerts-list" className="grid gap-4 md:grid-cols-2">
|
||||
{visibleAlerts}
|
||||
</div>
|
||||
{hasMoreAlerts && (
|
||||
@@ -436,104 +359,19 @@ export default function DashboardContent({ firstName, companyId, settings, summa
|
||||
</section>
|
||||
)}
|
||||
|
||||
{/* Upcoming deadlines — always visible */}
|
||||
{/* Upcoming deadlines */}
|
||||
{summary.deadlines && summary.deadlines.length > 0 && (
|
||||
<section className="mb-8">
|
||||
<section>
|
||||
<UpcomingDeadlinesWidget deadlines={summary.deadlines} maxItems={8} />
|
||||
</section>
|
||||
)}
|
||||
|
||||
{/* Tax todo widget — visible when there are incomplete tax deadlines */}
|
||||
{/* Tax todo */}
|
||||
{summary.deadlines?.some(d => d.deadline_type === 'tax' && !d.is_completed) && (
|
||||
<section className="mb-10">
|
||||
<section>
|
||||
<TaxTodoWidget deadlines={summary.deadlines} />
|
||||
</section>
|
||||
)}
|
||||
|
||||
{/* Collapsible details section */}
|
||||
<button
|
||||
onClick={() => setShowMore(!showMore)}
|
||||
aria-expanded={showMore}
|
||||
aria-controls="details-section"
|
||||
className="mb-6 py-1.5 text-sm text-muted-foreground hover:text-foreground transition-colors flex items-center gap-1.5"
|
||||
>
|
||||
{showMore ? 'Dölj detaljer' : 'Visa detaljer'}
|
||||
<ChevronDown className={cn('h-3.5 w-3.5 transition-transform', showMore && 'rotate-180')} />
|
||||
</button>
|
||||
|
||||
{showMore && (
|
||||
<div id="details-section">
|
||||
{/* Uncategorized transactions warning */}
|
||||
{summary.uncategorizedCount > 0 && (summary.uncategorizedIncome > 0 || summary.uncategorizedExpenses > 0) && (
|
||||
<section className="mb-10">
|
||||
<Link href="/transactions?tab=uncategorized" className="group">
|
||||
<div className="flex items-start gap-3 px-4 py-3.5 rounded-xl border border-warning/30 bg-warning/[0.03] hover:bg-warning/[0.06] transition-colors">
|
||||
<ArrowLeftRight className="h-4 w-4 text-warning-foreground flex-shrink-0 mt-0.5" />
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="font-medium text-sm">
|
||||
{summary.uncategorizedCount} obokförda transaktioner
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground mt-0.5">
|
||||
{summary.uncategorizedIncome > 0 && (
|
||||
<span>{formatCurrency(summary.uncategorizedIncome)} intäkter</span>
|
||||
)}
|
||||
{summary.uncategorizedIncome > 0 && summary.uncategorizedExpenses > 0 && ', '}
|
||||
{summary.uncategorizedExpenses > 0 && (
|
||||
<span>{formatCurrency(summary.uncategorizedExpenses)} kostnader</span>
|
||||
)}
|
||||
{' '}saknas i resultatet
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
</section>
|
||||
)}
|
||||
|
||||
{/* Income/Expenses */}
|
||||
<section className="mb-10">
|
||||
<h2 className="font-display text-lg font-medium mb-4">Resultat</h2>
|
||||
<div className="grid md:grid-cols-2 gap-3">
|
||||
<Card>
|
||||
<CardContent className="p-5">
|
||||
<p className="text-sm text-muted-foreground mb-3">Intäkter</p>
|
||||
<div>
|
||||
<p className="font-display text-2xl font-medium tabular-nums leading-tight">
|
||||
{formatLargeNumber(summary.mtd.income)}
|
||||
<span className="text-base ml-1 text-muted-foreground font-normal">kr</span>
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground mt-0.5">denna månad</p>
|
||||
</div>
|
||||
<div className="mt-4 pt-3 border-t border-border/30">
|
||||
<div className="flex items-baseline justify-between">
|
||||
<p className="text-xs text-muted-foreground">I år</p>
|
||||
<p className="text-sm font-medium tabular-nums">{formatCurrency(summary.ytd.income)}</p>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardContent className="p-5">
|
||||
<p className="text-sm text-muted-foreground mb-3">Kostnader</p>
|
||||
<div>
|
||||
<p className="font-display text-2xl font-medium tabular-nums leading-tight">
|
||||
{formatLargeNumber(summary.mtd.expenses)}
|
||||
<span className="text-base ml-1 text-muted-foreground font-normal">kr</span>
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground mt-0.5">denna månad</p>
|
||||
</div>
|
||||
<div className="mt-4 pt-3 border-t border-border/30">
|
||||
<div className="flex items-baseline justify-between">
|
||||
<p className="text-xs text-muted-foreground">I år</p>
|
||||
<p className="text-sm font-medium tabular-nums">{formatCurrency(summary.ytd.expenses)}</p>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -187,7 +187,7 @@ export default function DashboardNav({ companyName: _companyName, entityType, un
|
||||
return (
|
||||
<>
|
||||
{/* Desktop sidebar */}
|
||||
<aside className="hidden md:fixed md:inset-y-0 md:flex md:w-[232px] md:flex-col">
|
||||
<aside className="hidden md:fixed md:inset-y-0 md:flex md:w-64 md:flex-col">
|
||||
<div className="flex min-h-0 flex-1 flex-col border-r border-border/30 bg-card/90">
|
||||
<div className="flex flex-1 flex-col overflow-y-auto pt-7 pb-4">
|
||||
{/* Company switcher */}
|
||||
|
||||
@@ -174,7 +174,7 @@ export default function WelcomeOnboarding({
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : String(err)
|
||||
logError('create company action threw', { error: message })
|
||||
toast({ title: 'Fel', description: 'Ett oväntat fel uppstod. Försök igen.', variant: 'destructive' })
|
||||
toast({ title: 'Ett oväntat fel uppstod. Försök igen.', variant: 'destructive' })
|
||||
} finally {
|
||||
setIsSaving(false)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user