Feat/skv integration full (#284)
* feat: add script to import Skatteverket monthly tax tables as fallback TypeScript module
- Implemented a new script `import-tax-tables.ts` to parse fixed-width TXT tax tables from Skatteverket (SKV 434).
- The script generates a TypeScript module for emergency fallback when the Skatteverket open-data API is unavailable.
- Supports command-line argument for specifying the year and handles parsing of B-rows only.
- Outputs a structured TypeScript file containing tax data for specified years.
* feat: gate salary module behind dev-only flag
Temporarily disable the Lön module in production while the feature is
being completed. Sidebar entries ("Löner", "Anställda") still render but
are not clickable and show a "Kommer snart" badge. Middleware redirects
/salary* to / and returns 404 on /api/salary/* so the feature can't be
reached by direct URL. All gates check NODE_ENV === 'development' so
local dev keeps full access for continued development.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat: refactor bank file import wizard to streamline column mapping and enhance CSV handling
* fix: bump migration timestamp to avoid collision with logos_bucket
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat: enhance AGI generation and salary entry calculations with improved status checks and error handling
---------
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
7a0214c053
commit
11621bb79f
@@ -56,6 +56,7 @@ interface NavItem {
|
||||
group: string
|
||||
modes?: EntityType[] // If set, only visible for these entity types. If not set, visible to all.
|
||||
hidden?: boolean // Temporarily hide from sidebar
|
||||
comingSoon?: boolean // Visible but disabled; shows "Kommer snart" badge
|
||||
}
|
||||
|
||||
// All nav items for sidebar and mobile drawer
|
||||
@@ -77,9 +78,10 @@ const navItems: NavItem[] = [
|
||||
{ href: '/bookkeeping', label: 'Bokföring', icon: BookOpen, group: 'redovisning' },
|
||||
{ href: '/reports', label: 'Rapporter', icon: BarChart3, group: 'redovisning' },
|
||||
{ href: '/import', label: 'Importera', icon: Upload, group: 'redovisning' },
|
||||
// Personal
|
||||
{ href: '/salary', label: 'Löner', icon: HandCoins, group: 'personal', modes: ['aktiebolag'] },
|
||||
{ href: '/salary/employees', label: 'Anställda', icon: Users, group: 'personal', modes: ['aktiebolag'] },
|
||||
// Personal — temporarily disabled in production pending feature completion.
|
||||
// Still clickable in local dev (NODE_ENV === 'development') so we can test.
|
||||
{ href: '/salary', label: 'Löner', icon: HandCoins, group: 'personal', modes: ['aktiebolag'], comingSoon: process.env.NODE_ENV !== 'development' },
|
||||
{ href: '/salary/employees', label: 'Anställda', icon: Users, group: 'personal', modes: ['aktiebolag'], comingSoon: process.env.NODE_ENV !== 'development' },
|
||||
{ href: '/help', label: 'Hjälp', icon: HelpCircle, group: 'övrigt' },
|
||||
{ href: '/settings', label: 'Inställningar', icon: Settings, group: 'övrigt' },
|
||||
]
|
||||
@@ -246,7 +248,7 @@ export default function DashboardNav({ companyName: _companyName, entityType, un
|
||||
{items.map((item) => {
|
||||
const Icon = item.icon
|
||||
const active = isActive(item.href)
|
||||
const enabled = isItemEnabled(item.href)
|
||||
const enabled = isItemEnabled(item.href) && !item.comingSoon
|
||||
const badge = item.href === '/transactions' && uncategorizedTransactionCount > 0
|
||||
? uncategorizedTransactionCount
|
||||
: item.href === '/pending' && pendingOperationsCount > 0
|
||||
@@ -259,7 +261,11 @@ export default function DashboardNav({ companyName: _companyName, entityType, un
|
||||
active ? "text-primary" : "text-muted-foreground group-hover:text-foreground"
|
||||
)} />
|
||||
<span className="flex-1">{item.label}</span>
|
||||
{badge !== null && (
|
||||
{item.comingSoon ? (
|
||||
<span className="ml-auto rounded-full bg-muted/60 text-muted-foreground/70 text-[9px] font-medium uppercase tracking-wider px-1.5 py-0.5">
|
||||
Kommer snart
|
||||
</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>
|
||||
@@ -286,7 +292,7 @@ export default function DashboardNav({ companyName: _companyName, entityType, un
|
||||
key={item.href}
|
||||
className={baseClass}
|
||||
aria-disabled="true"
|
||||
title="Lägg till ett företag för att aktivera"
|
||||
title={item.comingSoon ? 'Kommer snart' : 'Lägg till ett företag för att aktivera'}
|
||||
>
|
||||
{content}
|
||||
</div>
|
||||
@@ -572,7 +578,7 @@ export default function DashboardNav({ companyName: _companyName, entityType, un
|
||||
{items.map((item) => {
|
||||
const Icon = item.icon
|
||||
const active = isActive(item.href)
|
||||
const enabled = isItemEnabled(item.href)
|
||||
const enabled = isItemEnabled(item.href) && !item.comingSoon
|
||||
const badge = item.href === '/transactions' && uncategorizedTransactionCount > 0
|
||||
? uncategorizedTransactionCount
|
||||
: item.href === '/pending' && pendingOperationsCount > 0
|
||||
@@ -582,7 +588,11 @@ export default function DashboardNav({ companyName: _companyName, entityType, un
|
||||
<>
|
||||
<Icon className={cn("h-[18px] w-[18px] flex-shrink-0", active ? "text-primary" : "text-muted-foreground")} />
|
||||
<span className="text-sm flex-1">{item.label}</span>
|
||||
{badge !== null && (
|
||||
{item.comingSoon ? (
|
||||
<span className="rounded-full bg-muted/60 text-muted-foreground/70 text-[9px] font-medium uppercase tracking-wider px-1.5 py-0.5">
|
||||
Kommer snart
|
||||
</span>
|
||||
) : badge !== null && (
|
||||
<span className="min-w-[20px] h-[20px] flex items-center justify-center rounded-full bg-primary/15 text-primary text-[10px] font-semibold px-1.5">
|
||||
{badge > 99 ? '99+' : badge}
|
||||
</span>
|
||||
|
||||
Reference in New Issue
Block a user