feat: improve UX across reports, navigation, invoicing, and settings

- Reorganize reports page from flat 11-tab bar into 4-column categorized
  grid (Bokslut, Skatt & moms, Huvudböcker, Avstämning)
- Move Import from Övrigt to Finans nav group for better discoverability
- Merge standalone Marketplace link into Tillägg section as "Utforska fler..."
- Rename abbreviated "Lev.fakturor" to full "Leverantörsfakturor"
- Add uncategorized transaction count badge to nav (desktop pill + mobile dot)
- Strengthen credit note confirmation with destructive styling and
  type-to-confirm pattern requiring exact invoice number
- Clarify invoice send vs mark-sent with "Skickad manuellt" label,
  visual hierarchy, and explanatory helper text
- Show Banking and Notifications settings tabs always, with placeholder
  message and link to extensions when not enabled

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Jakob Wennberg
2026-02-27 15:11:03 +01:00
co-authored by Claude Opus 4.6
parent ecd95ccb82
commit aabe527f68
6 changed files with 292 additions and 160 deletions
+32 -6
View File
@@ -5,11 +5,12 @@ import { useRouter } from 'next/navigation'
import { createClient } from '@/lib/supabase/client'
import { Button } from '@/components/ui/button'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Input } from '@/components/ui/input'
import { Textarea } from '@/components/ui/textarea'
import { Label } from '@/components/ui/label'
import { Separator } from '@/components/ui/separator'
import { useToast } from '@/components/ui/use-toast'
import { formatCurrency, formatDate } from '@/lib/utils'
import { cn, formatCurrency, formatDate } from '@/lib/utils'
import { getVatTreatmentLabel } from '@/lib/invoices/vat-rules'
import { Loader2, ArrowLeft, AlertTriangle } from 'lucide-react'
import type { Invoice, InvoiceItem, Customer } from '@/types'
@@ -29,6 +30,7 @@ export default function CreateCreditNotePage({ params }: { params: Promise<{ id:
const [isLoading, setIsLoading] = useState(true)
const [isSubmitting, setIsSubmitting] = useState(false)
const [reason, setReason] = useState('')
const [confirmText, setConfirmText] = useState('')
useEffect(() => {
fetchInvoice()
@@ -157,14 +159,14 @@ export default function CreateCreditNotePage({ params }: { params: Promise<{ id:
</div>
{/* Warning */}
<Card className="border-warning/50 bg-warning/5">
<Card className="border-destructive/50 bg-destructive/5">
<CardContent className="flex items-start gap-4 pt-6">
<AlertTriangle className="h-5 w-5 text-warning flex-shrink-0 mt-0.5" />
<AlertTriangle className="h-5 w-5 text-destructive flex-shrink-0 mt-0.5" />
<div>
<p className="font-medium">Viktigt om kreditfakturor</p>
<p className="font-medium text-destructive">Oåterkallelig åtgärd</p>
<p className="text-sm text-muted-foreground mt-1">
En kreditfaktura makulerar den ursprungliga fakturan helt.
Alla belopp blir negativa och den ursprungliga fakturan markeras som krediterad.
Alla belopp blir negativa, en bokföringsverifikation skapas, och den ursprungliga fakturan markeras som krediterad.
Denna åtgärd kan inte ångras.
</p>
</div>
@@ -295,12 +297,36 @@ export default function CreateCreditNotePage({ params }: { params: Promise<{ id:
</CardContent>
</Card>
{/* Confirmation */}
<Card>
<CardHeader>
<CardTitle>Bekräfta</CardTitle>
<CardDescription>
Skriv fakturanumret <span className="font-mono font-semibold text-foreground">{invoice.invoice_number}</span> för att bekräfta
</CardDescription>
</CardHeader>
<CardContent>
<Input
value={confirmText}
onChange={(e) => setConfirmText(e.target.value)}
placeholder={invoice.invoice_number}
className={cn(
confirmText && confirmText !== invoice.invoice_number && 'border-destructive'
)}
/>
</CardContent>
</Card>
{/* Actions */}
<div className="flex justify-end gap-4">
<Button variant="outline" onClick={() => router.back()}>
Avbryt
</Button>
<Button onClick={handleSubmit} disabled={isSubmitting}>
<Button
variant="destructive"
onClick={handleSubmit}
disabled={isSubmitting || confirmText !== invoice.invoice_number}
>
{isSubmitting ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
+37 -20
View File
@@ -406,16 +406,16 @@ export default function InvoiceDetailPage({ params }: { params: Promise<{ id: st
Skicka via e-post
</Button>
) : (
<Button onClick={() => updateStatus('sent')} disabled={isUpdating}>
<Button variant="secondary" onClick={() => updateStatus('sent')} disabled={isUpdating}>
<Send className="mr-2 h-4 w-4" />
Markera som skickad
Skickad manuellt
</Button>
)
)}
{isDeliveryNote && invoice.status === 'draft' && (
<Button onClick={() => updateStatus('sent')} disabled={isUpdating}>
<Button variant="secondary" onClick={() => updateStatus('sent')} disabled={isUpdating}>
<Send className="mr-2 h-4 w-4" />
Markera som skickad
Skickad manuellt
</Button>
)}
{(invoice.status === 'sent' || invoice.status === 'overdue') && isRealInvoice && (
@@ -824,24 +824,38 @@ export default function InvoiceDetailPage({ params }: { params: Promise<{ id: st
{!isProforma && invoice.status === 'draft' && (
<>
{!isDeliveryNote && customerHasEmail ? (
<Button
className="w-full"
onClick={sendInvoiceEmail}
disabled={isSendingEmail}
>
{isSendingEmail ? (
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
) : (
<Mail className="mr-2 h-4 w-4" />
)}
Skicka via e-post
</Button>
<>
<Button
className="w-full"
onClick={sendInvoiceEmail}
disabled={isSendingEmail}
>
{isSendingEmail ? (
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
) : (
<Mail className="mr-2 h-4 w-4" />
)}
Skicka via e-post
</Button>
<Button
variant="ghost"
className="w-full text-muted-foreground"
onClick={() => updateStatus('sent')}
disabled={isUpdating}
>
<Send className="mr-2 h-4 w-4" />
Skickad manuellt
</Button>
<p className="text-[11px] text-muted-foreground/60 px-1 -mt-1">
Använd om du redan skickat fakturan på annat sätt (post, annat system)
</p>
</>
) : (
<>
{!isDeliveryNote && (
<div className="flex items-start gap-2 p-3 bg-yellow-50 border border-yellow-200 rounded-lg mb-2">
<AlertTriangle className="h-4 w-4 text-yellow-600 mt-0.5 flex-shrink-0" />
<p className="text-xs text-yellow-700">
<div className="flex items-start gap-2 p-3 bg-yellow-50 border border-yellow-200 rounded-lg mb-2 dark:bg-yellow-950/30 dark:border-yellow-800">
<AlertTriangle className="h-4 w-4 text-yellow-600 dark:text-yellow-500 mt-0.5 flex-shrink-0" />
<p className="text-xs text-yellow-700 dark:text-yellow-400">
Kunden saknar e-postadress. Lägg till e-post för att kunna skicka fakturan digitalt.
</p>
</div>
@@ -852,8 +866,11 @@ export default function InvoiceDetailPage({ params }: { params: Promise<{ id: st
disabled={isUpdating}
>
<Send className="mr-2 h-4 w-4" />
Markera som skickad
Skickad manuellt
</Button>
<p className="text-[11px] text-muted-foreground/60 px-1 -mt-1">
Markerar fakturan som skickad och skapar bokföringsverifikation
</p>
</>
)}
<Button
+13 -5
View File
@@ -29,11 +29,18 @@ export default async function DashboardLayout({
const entityType = (settings.entity_type as EntityType) || 'enskild_firma'
const { data: enabledToggles } = await supabase
.from('extension_toggles')
.select('sector_slug, extension_slug')
.eq('user_id', user.id)
.eq('enabled', true)
const [{ data: enabledToggles }, { count: uncategorizedCount }] = await Promise.all([
supabase
.from('extension_toggles')
.select('sector_slug, extension_slug')
.eq('user_id', user.id)
.eq('enabled', true),
supabase
.from('transactions')
.select('*', { count: 'exact', head: true })
.eq('user_id', user.id)
.is('is_business', null),
])
return (
<div className="min-h-screen bg-background">
@@ -48,6 +55,7 @@ export default async function DashboardLayout({
companyName={settings.company_name || 'Min verksamhet'}
entityType={entityType}
enabledExtensions={enabledToggles || []}
uncategorizedTransactionCount={uncategorizedCount ?? 0}
/>
<main id="main-content" className="pb-20 md:pb-0 md:pl-[232px]" role="main">
<div className="max-w-5xl mx-auto px-5 py-8 md:px-8 md:py-10">
+83 -48
View File
@@ -103,54 +103,89 @@ export default function ReportsPage() {
{selectedPeriod ? (
<Tabs value={activeTab} onValueChange={setActiveTab}>
<TabsList className="h-auto w-full flex-wrap justify-start gap-1 p-1">
<TabsTrigger value="trial-balance">
<Scale className="h-4 w-4 mr-1" />
Saldobalans
</TabsTrigger>
<TabsTrigger value="income-statement">
<TrendingUp className="h-4 w-4 mr-1" />
Resultaträkning
</TabsTrigger>
<TabsTrigger value="balance-sheet">
<FileText className="h-4 w-4 mr-1" />
Balansräkning
</TabsTrigger>
<TabsTrigger value="vat-declaration">
<Receipt className="h-4 w-4 mr-1" />
Momsdeklaration
</TabsTrigger>
{isEnskildFirma && (
<TabsTrigger value="ne-declaration">
<Briefcase className="h-4 w-4 mr-1" />
NE-bilaga
</TabsTrigger>
)}
<TabsTrigger value="sru-export">
<FileDown className="h-4 w-4 mr-1" />
SRU-export
</TabsTrigger>
<TabsTrigger value="huvudbok">
<BookOpen className="h-4 w-4 mr-1" />
Huvudbok
</TabsTrigger>
<TabsTrigger value="grundbok">
<List className="h-4 w-4 mr-1" />
Grundbok
</TabsTrigger>
<TabsTrigger value="kundreskontra">
<Users className="h-4 w-4 mr-1" />
Kundreskontra
</TabsTrigger>
<TabsTrigger value="supplier-ledger">
<Building2 className="h-4 w-4 mr-1" />
Lev.reskontra
</TabsTrigger>
<TabsTrigger value="bank-reconciliation">
<ArrowLeftRight className="h-4 w-4 mr-1" />
Bankavstämning
</TabsTrigger>
</TabsList>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 mb-2">
{/* Bokslut (Financial Statements) */}
<div className="space-y-1">
<p className="text-[10px] font-semibold text-muted-foreground/70 uppercase tracking-[0.08em] px-1">
Bokslut
</p>
<TabsList className="flex flex-col h-auto w-full gap-0.5 p-1">
<TabsTrigger value="trial-balance" className="w-full justify-start">
<Scale className="h-4 w-4 mr-1.5" />
Saldobalans
</TabsTrigger>
<TabsTrigger value="income-statement" className="w-full justify-start">
<TrendingUp className="h-4 w-4 mr-1.5" />
Resultaträkning
</TabsTrigger>
<TabsTrigger value="balance-sheet" className="w-full justify-start">
<FileText className="h-4 w-4 mr-1.5" />
Balansräkning
</TabsTrigger>
</TabsList>
</div>
{/* Skatt & moms (Tax & VAT) */}
<div className="space-y-1">
<p className="text-[10px] font-semibold text-muted-foreground/70 uppercase tracking-[0.08em] px-1">
Skatt & moms
</p>
<TabsList className="flex flex-col h-auto w-full gap-0.5 p-1">
<TabsTrigger value="vat-declaration" className="w-full justify-start">
<Receipt className="h-4 w-4 mr-1.5" />
Momsdeklaration
</TabsTrigger>
{isEnskildFirma && (
<TabsTrigger value="ne-declaration" className="w-full justify-start">
<Briefcase className="h-4 w-4 mr-1.5" />
NE-bilaga
</TabsTrigger>
)}
<TabsTrigger value="sru-export" className="w-full justify-start">
<FileDown className="h-4 w-4 mr-1.5" />
SRU-export
</TabsTrigger>
</TabsList>
</div>
{/* Huvudböcker (Ledgers) */}
<div className="space-y-1">
<p className="text-[10px] font-semibold text-muted-foreground/70 uppercase tracking-[0.08em] px-1">
Huvudböcker
</p>
<TabsList className="flex flex-col h-auto w-full gap-0.5 p-1">
<TabsTrigger value="huvudbok" className="w-full justify-start">
<BookOpen className="h-4 w-4 mr-1.5" />
Huvudbok
</TabsTrigger>
<TabsTrigger value="grundbok" className="w-full justify-start">
<List className="h-4 w-4 mr-1.5" />
Grundbok
</TabsTrigger>
<TabsTrigger value="kundreskontra" className="w-full justify-start">
<Users className="h-4 w-4 mr-1.5" />
Kundreskontra
</TabsTrigger>
<TabsTrigger value="supplier-ledger" className="w-full justify-start">
<Building2 className="h-4 w-4 mr-1.5" />
Leverantörsreskontra
</TabsTrigger>
</TabsList>
</div>
{/* Avstämning (Reconciliation) */}
<div className="space-y-1">
<p className="text-[10px] font-semibold text-muted-foreground/70 uppercase tracking-[0.08em] px-1">
Avstämning
</p>
<TabsList className="flex flex-col h-auto w-full gap-0.5 p-1">
<TabsTrigger value="bank-reconciliation" className="w-full justify-start">
<ArrowLeftRight className="h-4 w-4 mr-1.5" />
Bankavstämning
</TabsTrigger>
</TabsList>
</div>
</div>
<TabsContent value="trial-balance">
<TrialBalanceView periodId={selectedPeriod} />
+67 -30
View File
@@ -2,6 +2,7 @@
import { useState, useEffect } from 'react'
import { useRouter, useSearchParams } from 'next/navigation'
import Link from 'next/link'
import { createClient } from '@/lib/supabase/client'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Button } from '@/components/ui/button'
@@ -22,6 +23,7 @@ import {
Moon,
Monitor,
Palette,
ExternalLink,
} from 'lucide-react'
import { useTheme } from 'next-themes'
import type { CompanySettings } from '@/types'
@@ -41,6 +43,7 @@ export default function SettingsPage() {
const [isSaving, setIsSaving] = useState(false)
const [settings, setSettings] = useState<CompanySettings | null>(null)
const [hasBankingExtension, setHasBankingExtension] = useState(false)
const [hasNotificationExtension, setHasNotificationExtension] = useState(false)
const { theme, setTheme } = useTheme()
const [mounted, setMounted] = useState(false)
@@ -93,25 +96,31 @@ export default function SettingsPage() {
setSettings(settingsData)
// Check if Enable Banking extension is enabled
try {
const toggleRes = await fetch('/api/extensions/toggles/general/enable-banking')
if (toggleRes.ok) {
const { data } = await toggleRes.json()
setHasBankingExtension(data?.enabled || false)
}
} catch {
// If toggle check fails, also check for existing connections
// Check extension toggles in parallel
const [bankingToggleRes, notificationToggleRes] = await Promise.all([
fetch('/api/extensions/toggles/general/enable-banking').catch(() => null),
fetch('/api/extensions/toggles/general/push-notifications').catch(() => null),
])
if (bankingToggleRes?.ok) {
const { data } = await bankingToggleRes.json()
setHasBankingExtension(data?.enabled || false)
} else {
// Fallback: check for existing bank connections
const { data: connections } = await supabase
.from('bank_connections')
.select('id')
.eq('user_id', user.id)
.eq('status', 'active')
.limit(1)
setHasBankingExtension((connections && connections.length > 0) || false)
}
if (notificationToggleRes?.ok) {
const { data } = await notificationToggleRes.json()
setHasNotificationExtension(data?.enabled || false)
}
setIsLoading(false)
}
@@ -197,18 +206,14 @@ export default function SettingsPage() {
<Building className="mr-2 h-4 w-4" />
Företag
</TabsTrigger>
{hasBankingExtension && BankingPanel && (
<TabsTrigger value="banking">
<CreditCard className="mr-2 h-4 w-4" />
Bank (PSD2)
</TabsTrigger>
)}
{NotificationPanel && (
<TabsTrigger value="notifications">
<Bell className="mr-2 h-4 w-4" />
Aviseringar
</TabsTrigger>
)}
<TabsTrigger value="banking">
<CreditCard className="mr-2 h-4 w-4" />
Bank (PSD2)
</TabsTrigger>
<TabsTrigger value="notifications">
<Bell className="mr-2 h-4 w-4" />
Aviseringar
</TabsTrigger>
<TabsTrigger value="calendar">
<Calendar className="mr-2 h-4 w-4" />
Kalender
@@ -436,18 +441,50 @@ export default function SettingsPage() {
</TabsContent>
{/* Banking settings — loaded dynamically from extension */}
{hasBankingExtension && BankingPanel && (
<TabsContent value="banking" className="space-y-6">
<TabsContent value="banking" className="space-y-6">
{hasBankingExtension && BankingPanel ? (
<BankingPanel />
</TabsContent>
)}
) : (
<Card>
<CardContent className="flex flex-col items-center justify-center py-12 text-center">
<CreditCard className="h-10 w-10 text-muted-foreground/40 mb-4" />
<p className="font-medium mb-1">Bankintegration (PSD2) är inte aktiverad</p>
<p className="text-sm text-muted-foreground mb-4 max-w-md">
Aktivera tillägget Enable Banking för att koppla ditt bankkonto och automatiskt hämta transaktioner.
</p>
<Button variant="outline" asChild>
<Link href="/extensions">
<ExternalLink className="mr-2 h-4 w-4" />
Gå till Tillägg
</Link>
</Button>
</CardContent>
</Card>
)}
</TabsContent>
{/* Notification settings — loaded dynamically from extension */}
{NotificationPanel && (
<TabsContent value="notifications">
<TabsContent value="notifications">
{hasNotificationExtension && NotificationPanel ? (
<NotificationPanel />
</TabsContent>
)}
) : (
<Card>
<CardContent className="flex flex-col items-center justify-center py-12 text-center">
<Bell className="h-10 w-10 text-muted-foreground/40 mb-4" />
<p className="font-medium mb-1">Push-notiser är inte aktiverade</p>
<p className="text-sm text-muted-foreground mb-4 max-w-md">
Aktivera tillägget Push-notiser för att få aviseringar om förfallna fakturor, deadlines och andra händelser.
</p>
<Button variant="outline" asChild>
<Link href="/extensions">
<ExternalLink className="mr-2 h-4 w-4" />
Gå till Tillägg
</Link>
</Button>
</CardContent>
</Card>
)}
</TabsContent>
{/* Calendar feed settings */}
<TabsContent value="calendar">
+60 -51
View File
@@ -33,6 +33,7 @@ interface DashboardNavProps {
companyName: string
entityType: EntityType
enabledExtensions?: { sector_slug: string; extension_slug: string }[]
uncategorizedTransactionCount?: number
}
interface NavItem {
@@ -50,11 +51,11 @@ const navItems: NavItem[] = [
{ href: '/invoices', label: 'Fakturor', icon: Receipt, group: 'finans' },
{ href: '/customers', label: 'Kunder', icon: Users, group: 'finans' },
{ href: '/suppliers', label: 'Leverantörer', icon: Building2, group: 'finans' },
{ href: '/supplier-invoices', label: 'Lev.fakturor', icon: FileInput, group: 'finans' },
{ href: '/supplier-invoices', label: 'Leverantörsfakturor', icon: FileInput, group: 'finans' },
{ href: '/transactions', label: 'Transaktioner', icon: ArrowLeftRight, group: 'finans' },
{ href: '/bookkeeping', label: 'Bokföring', icon: BookOpen, group: 'finans' },
{ href: '/reports', label: 'Rapporter', icon: BarChart3, group: 'finans' },
{ href: '/import', label: 'Importera', icon: Upload, group: 'övrigt' },
{ href: '/import', label: 'Importera', icon: Upload, group: 'finans' },
{ href: '/help', label: 'Hjälp', icon: HelpCircle, group: 'övrigt' },
{ href: '/settings', label: 'Inställningar', icon: Settings, group: 'övrigt' },
]
@@ -65,17 +66,17 @@ const groupLabels: Record<string, string> = {
övrigt: 'Övrigt',
}
export default function DashboardNav({ companyName, entityType, enabledExtensions }: DashboardNavProps) {
export default function DashboardNav({ companyName, entityType, enabledExtensions, uncategorizedTransactionCount = 0 }: DashboardNavProps) {
const pathname = usePathname()
const router = useRouter()
const supabase = createClient()
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false)
// Auto-expand Övrigt when the user is on one of its pages, or when manually toggled
const isOnOvrigtPage = ['/import', '/help', '/settings'].some(p => pathname.startsWith(p))
const isOnOvrigtPage = ['/help', '/settings'].some(p => pathname.startsWith(p))
const [manualOvrigtExpanded, setManualOvrigtExpanded] = useState(false)
const isOvrigtExpanded = isOnOvrigtPage || manualOvrigtExpanded
// Auto-expand Tillägg when on an extension page, or when manually toggled (persisted)
const isOnExtensionPage = pathname.startsWith('/e/')
// Auto-expand Tillägg when on an extension page or marketplace, or when manually toggled (persisted)
const isOnExtensionPage = pathname.startsWith('/e/') || pathname.startsWith('/extensions')
const [manualTillaggExpanded, setManualTillaggExpanded] = useState(false)
const isTillaggExpanded = isOnExtensionPage || manualTillaggExpanded
@@ -200,6 +201,9 @@ export default function DashboardNav({ companyName, entityType, enabledExtension
{finansItems.map((item) => {
const Icon = item.icon
const active = isActive(item.href)
const badge = item.href === '/transactions' && uncategorizedTransactionCount > 0
? uncategorizedTransactionCount
: null
return (
<Link
key={item.href}
@@ -215,35 +219,19 @@ export default function DashboardNav({ companyName, entityType, enabledExtension
"mr-2.5 h-[15px] w-[15px] flex-shrink-0",
active ? "text-primary" : "text-muted-foreground/70 group-hover:text-muted-foreground"
)} />
{item.label}
<span className="flex-1">{item.label}</span>
{badge !== null && (
<span className="ml-auto min-w-[18px] h-[18px] flex items-center justify-center rounded-full bg-primary/15 text-primary text-[10px] font-semibold px-1">
{badge > 99 ? '99+' : badge}
</span>
)}
</Link>
)
})}
</div>
</div>
{/* Marketplace - standalone link */}
<div className="mb-4">
<div className="space-y-px">
<Link
href="/extensions"
className={cn(
'group flex items-center px-3 py-[7px] text-[13px] transition-colors duration-150 rounded-lg',
isActive('/extensions')
? 'bg-primary/8 text-foreground font-medium'
: 'text-muted-foreground hover:text-foreground hover:bg-muted/40'
)}
>
<Store className={cn(
"mr-2.5 h-[15px] w-[15px] flex-shrink-0",
isActive('/extensions') ? "text-primary" : "text-muted-foreground/70 group-hover:text-muted-foreground"
)} />
Marketplace
</Link>
</div>
</div>
{/* Tillägg - collapsible */}
{/* Tillägg - collapsible, with marketplace link */}
<div className="mb-4">
<button
onClick={toggleTillagg}
@@ -286,6 +274,21 @@ export default function DashboardNav({ companyName, entityType, enabledExtension
Inga tillägg aktiverade
</p>
)}
<Link
href="/extensions"
className={cn(
'group flex items-center px-3 py-[7px] text-[13px] transition-colors duration-150 rounded-lg',
isActive('/extensions')
? 'bg-primary/8 text-foreground font-medium'
: 'text-muted-foreground/60 hover:text-muted-foreground hover:bg-muted/40'
)}
>
<Store className={cn(
"mr-2.5 h-[15px] w-[15px] flex-shrink-0",
isActive('/extensions') ? "text-primary" : "text-muted-foreground/50 group-hover:text-muted-foreground"
)} />
Utforska fler...
</Link>
</div>
)}
</div>
@@ -352,22 +355,32 @@ export default function DashboardNav({ companyName, entityType, enabledExtension
{mobileNavItems.map((item) => {
const Icon = item.icon
const active = isActive(item.href)
const badge = item.href === '/transactions' && uncategorizedTransactionCount > 0
? uncategorizedTransactionCount
: null
return (
<Link
key={item.href}
href={item.href}
className={cn(
'flex flex-col items-center justify-center flex-1 h-full text-xs transition-colors duration-200',
'relative flex flex-col items-center justify-center flex-1 h-full text-xs transition-colors duration-200',
active
? 'text-primary'
: 'text-muted-foreground'
)}
>
<Icon className={cn(
"h-5 w-5 mb-1",
active && "text-primary"
)} />
<div className="relative">
<Icon className={cn(
"h-5 w-5 mb-1",
active && "text-primary"
)} />
{badge !== null && (
<span className="absolute -top-1.5 -right-2.5 min-w-[16px] h-[16px] flex items-center justify-center rounded-full bg-primary text-primary-foreground text-[9px] font-semibold px-0.5">
{badge > 99 ? '99+' : badge}
</span>
)}
</div>
<span className={cn(
"truncate",
active && "font-medium"
@@ -473,23 +486,6 @@ export default function DashboardNav({ companyName, entityType, enabledExtension
})}
</div>
{/* Marketplace */}
<div className="mb-4">
<Link
href="/extensions"
onClick={closeMobileMenu}
className={cn(
'flex items-center gap-3 px-3 py-2.5 rounded-lg transition-colors',
isActive('/extensions')
? 'bg-primary/10 text-primary font-medium'
: 'text-muted-foreground hover:bg-secondary/50 hover:text-foreground'
)}
>
<Store className="h-5 w-5" />
Marketplace
</Link>
</div>
{/* Tillägg */}
<div className="mb-4">
<p className="px-3 py-2 text-xs font-medium text-muted-foreground uppercase tracking-wider">
@@ -522,6 +518,19 @@ export default function DashboardNav({ companyName, entityType, enabledExtension
Inga tillägg aktiverade
</p>
)}
<Link
href="/extensions"
onClick={closeMobileMenu}
className={cn(
'flex items-center gap-3 px-3 py-2.5 rounded-lg transition-colors',
isActive('/extensions')
? 'bg-primary/10 text-primary font-medium'
: 'text-muted-foreground/60 hover:bg-secondary/50 hover:text-foreground'
)}
>
<Store className="h-5 w-5" />
Utforska fler...
</Link>
</div>
{/* Other section */}