feat: UI polish, lint fixes, onboarding redesign, help page expansion, and test improvements

Broad update across dashboard pages, components, extensions, and lib code. Includes ESLint config additions, onboarding flow redesign, settings page refactor, help page content expansion, dead code removal, and test mock fixes. Adds dev docs and public assets.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Jakob Wennberg
2026-03-05 23:05:49 +01:00
co-authored by Claude Opus 4.6
parent 02ebe50f52
commit 091d043c85
106 changed files with 1177 additions and 326 deletions
+4 -4
View File
@@ -29,10 +29,6 @@ export default function ChartOfAccounts() {
const [editingSRU, setEditingSRU] = useState<string | null>(null)
const [sruValue, setSruValue] = useState('')
useEffect(() => {
fetchAccounts()
}, [])
async function fetchAccounts() {
const res = await fetch('/api/bookkeeping/accounts')
const { data } = await res.json()
@@ -40,6 +36,10 @@ export default function ChartOfAccounts() {
setLoading(false)
}
useEffect(() => {
fetchAccounts()
}, [])
async function updateSRUCode(accountId: string, newSruCode: string) {
const supabase = createClient()
const trimmed = newSruCode.trim() || null
+5 -5
View File
@@ -70,11 +70,6 @@ export default function JournalEntryForm({
uploadedFiles.length > 0
useUnsavedChanges(hasContent)
useEffect(() => {
fetchPeriods()
fetchAccounts()
}, [])
async function fetchPeriods() {
const res = await fetch('/api/bookkeeping/fiscal-periods')
const { data } = await res.json()
@@ -90,6 +85,11 @@ export default function JournalEntryForm({
setAccounts(data || [])
}
useEffect(() => {
fetchPeriods()
fetchAccounts()
}, [])
const addLine = () => {
setLines([...lines, { ...BLANK_LINE }])
}
+4 -4
View File
@@ -49,10 +49,6 @@ export default function JournalEntryList({ periodId }: Props) {
}
}, [])
useEffect(() => {
fetchEntries()
}, [periodId, page])
async function fetchEntries() {
setLoading(true)
const params = new URLSearchParams({
@@ -73,6 +69,10 @@ export default function JournalEntryList({ periodId }: Props) {
fetchAttachmentCounts(ids)
}
useEffect(() => {
fetchEntries()
}, [periodId, page])
const handleAttachmentCountChange = useCallback((entryId: string, count: number) => {
setAttachmentCounts((prev) => ({ ...prev, [entryId]: count }))
}, [])
+2 -2
View File
@@ -5,7 +5,7 @@ import { ChatMessage } from './ChatMessage'
import { ChatInput } from './ChatInput'
import { useChatStream } from './useChatStream'
import { useToast } from '@/components/ui/use-toast'
import { Bot, Trash2, RotateCcw } from 'lucide-react'
import { Bot, RotateCcw } from 'lucide-react'
import { Button } from '@/components/ui/button'
interface ChatPanelProps {
@@ -20,7 +20,7 @@ export function ChatPanel({ className }: ChatPanelProps) {
messages,
isLoading,
isStreaming,
error,
error: _error,
toolsExecuting,
sendMessage,
clearChat,
@@ -18,8 +18,6 @@ interface ChatAgingBucketsProps {
export function ChatAgingBuckets({ artifact }: ChatAgingBucketsProps) {
const { title, buckets, total } = artifact
const maxAmount = Math.max(...buckets.map((b) => b.amount), 1)
return (
<div className="w-full">
<div className="flex items-baseline justify-between mb-3">
+1 -1
View File
@@ -6,7 +6,7 @@ interface ChatDataTableProps {
artifact: TableArtifact
}
function formatCell(value: string | number, align?: 'left' | 'right'): string {
function formatCell(value: string | number, _align?: 'left' | 'right'): string {
if (typeof value === 'number') {
return new Intl.NumberFormat('sv-SE', {
minimumFractionDigits: value % 1 !== 0 ? 2 : 0,
+1
View File
@@ -8,6 +8,7 @@ import ExtensionToggleButton from './ExtensionToggleButton'
import Link from 'next/link'
export default function ExtensionCard({ extension }: { extension: ExtensionDefinition }) {
const Icon = resolveIcon(extension.icon)
return (
@@ -16,6 +16,7 @@ export default function ExtensionWorkspaceLoader({
definition: ExtensionDefinition
userId: string
}) {
const WorkspaceComponent = getWorkspaceComponent(sector, slug)
return (
@@ -12,6 +12,7 @@ export default function ExtensionWorkspaceShell({
definition: ExtensionDefinition
children: React.ReactNode
}) {
const Icon = resolveIcon(definition.icon)
const sector = getSector(definition.sector)
+1
View File
@@ -4,6 +4,7 @@ import { resolveIcon } from '@/lib/extensions/icon-resolver'
import type { Sector } from '@/lib/extensions/types'
export default function SectorCard({ sector }: { sector: Sector }) {
const Icon = resolveIcon(sector.icon)
return (
-1
View File
@@ -8,7 +8,6 @@ import {
XCircle,
ArrowRight,
RotateCcw,
ExternalLink,
} from 'lucide-react'
import type { IngestResult } from '@/lib/transactions/ingest'
-1
View File
@@ -2,7 +2,6 @@
import { useState, useCallback } from 'react'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Button } from '@/components/ui/button'
import { Badge } from '@/components/ui/badge'
import { Progress } from '@/components/ui/progress'
import {
+2 -9
View File
@@ -45,15 +45,8 @@ export default function Step1EntityType({ initialData, onNext, isSaving }: Step1
}
return (
<div className="space-y-6">
<div className="text-center">
<h1 className="text-3xl font-bold tracking-tight">Välkommen!</h1>
<p className="text-muted-foreground mt-2">
Låt oss börja med att ställa in din verksamhet. Vilken företagsform har du?
</p>
</div>
<div className="grid gap-3 max-w-lg mx-auto">
<div className="space-y-8">
<div className="grid gap-3">
{entityOptions.map((option) => {
const Icon = option.icon
const isSelected = selected === option.value
@@ -57,13 +57,6 @@ export default function Step2CompanyDetails({
return (
<div className="space-y-6">
<div className="text-center">
<h1 className="text-3xl font-bold tracking-tight">Företagsuppgifter</h1>
<p className="text-muted-foreground mt-2">
Fyll i uppgifter om din verksamhet. Dessa används på fakturor.
</p>
</div>
<Card>
<CardHeader>
<CardTitle>Grunduppgifter</CardTitle>
@@ -263,13 +263,6 @@ export default function Step3TaxRegistration({
return (
<div className="space-y-6">
<div className="text-center">
<h1 className="text-3xl font-bold tracking-tight">Skatteregistrering</h1>
<p className="text-muted-foreground mt-2">
Ange information om din skatteregistrering.
</p>
</div>
<Card>
<CardHeader>
<CardTitle>F-skatt och räkenskapsår</CardTitle>
@@ -50,13 +50,6 @@ export default function Step4PreliminaryTax({
return (
<div className="space-y-6">
<div className="text-center">
<h1 className="text-3xl font-bold tracking-tight">Preliminärskatt</h1>
<p className="text-muted-foreground mt-2">
Ange din månatliga F-skatt för att få bättre överblick över din ekonomi.
</p>
</div>
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
@@ -57,14 +57,6 @@ export default function Step6ConnectBank({
return (
<div className="space-y-6">
<div className="text-center">
<h1 className="text-3xl font-bold tracking-tight">Bankuppgifter</h1>
<p className="text-muted-foreground mt-2">
Ange dina bankuppgifter så visas de på dina fakturor.
Du kan importera transaktioner från din bank efteråt.
</p>
</div>
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
+1 -2
View File
@@ -1,8 +1,7 @@
import * as React from "react"
import { cn } from "@/lib/utils"
export interface InputProps
extends React.InputHTMLAttributes<HTMLInputElement> {}
export type InputProps = React.InputHTMLAttributes<HTMLInputElement>
const Input = React.forwardRef<HTMLInputElement, InputProps>(
({ className, type, ...props }, ref) => {
+1 -2
View File
@@ -1,8 +1,7 @@
import * as React from "react"
import { cn } from "@/lib/utils"
export interface TextareaProps
extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {}
export type TextareaProps = React.TextareaHTMLAttributes<HTMLTextAreaElement>
const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
({ className, ...props }, ref) => {