Files
accounted/components/onboarding/Step4PreliminaryTax.tsx
T
Jakob WennbergandClaude Opus 4.6 f3ec634a46 feat: open-source under AGPL-3.0, redesign UI to grayscale palette, add uncategorize API, fix VAT account names
Add LICENSE (AGPL-3.0-or-later), CONTRIBUTING.md, SECURITY.md, DCO, and NOTICE files.
Rewrite README for open-source audience with self-hosting instructions.
Redesign color palette to grayscale chrome theme across all components.
Add transaction uncategorize API route with tests.
Fix VAT account name mismatches in migration 052.
Improve import page with SIE file support and loading skeleton.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 18:18:11 +01:00

142 lines
4.5 KiB
TypeScript

'use client'
import { useForm } from 'react-hook-form'
import { zodResolver } from '@hookform/resolvers/zod'
import { z } from 'zod'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { Loader2, ArrowRight, ArrowLeft, Calculator, SkipForward } from 'lucide-react'
const schema = z.object({
preliminary_tax_monthly: z.number().min(0).optional().nullable(),
})
type FormData = z.infer<typeof schema>
interface Step4Props {
initialData: Partial<FormData>
onNext: (data: FormData) => void
onBack: () => void
onSkip: () => void
isSaving: boolean
}
export default function Step4PreliminaryTax({
initialData,
onNext,
onBack,
onSkip,
isSaving,
}: Step4Props) {
const {
register,
handleSubmit,
formState: { errors },
} = useForm<FormData>({
resolver: zodResolver(schema),
mode: 'onTouched',
defaultValues: {
preliminary_tax_monthly: initialData.preliminary_tax_monthly ?? undefined,
},
})
const onSubmit = (data: FormData) => {
onNext({
preliminary_tax_monthly: data.preliminary_tax_monthly || null,
})
}
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">
<Calculator className="h-5 w-5" />
Månatlig F-skatt
</CardTitle>
<CardDescription>
Detta är frivilligt men hjälper oss visa varningar om du betalar för lite skatt.
</CardDescription>
</CardHeader>
<CardContent>
<form onSubmit={handleSubmit(onSubmit)} className="space-y-6">
<div className="space-y-2">
<Label htmlFor="preliminary_tax_monthly">
Preliminärskatt per månad (kr)
</Label>
<Input
id="preliminary_tax_monthly"
type="number"
placeholder="0"
{...register('preliminary_tax_monthly', { valueAsNumber: true })}
/>
{errors.preliminary_tax_monthly && (
<p className="text-sm text-destructive">
{errors.preliminary_tax_monthly.message}
</p>
)}
<p className="text-sm text-muted-foreground">
Beloppet hittar du på ditt F-skattsedelsbeslut från Skatteverket.
</p>
</div>
<div className="bg-muted/50 rounded-lg p-4">
<h4 className="font-medium mb-2">Varför är detta användbart?</h4>
<ul className="text-sm text-muted-foreground space-y-1">
<li>• Vi jämför din inbetalda skatt mot beräknad skatt</li>
<li>• Du får varningar om du riskerar restskatt</li>
<li>• Lättare att planera för slutskattebeskedet</li>
</ul>
</div>
<div className="flex justify-between pt-4">
<Button
type="button"
variant="ghost"
onClick={onBack}
disabled={isSaving}
>
<ArrowLeft className="mr-2 h-4 w-4" />
Tillbaka
</Button>
<div className="flex gap-2">
<Button
type="button"
variant="outline"
onClick={onSkip}
disabled={isSaving}
>
<SkipForward className="mr-2 h-4 w-4" />
Hoppa över
</Button>
<Button type="submit" disabled={isSaving}>
{isSaving ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
Sparar...
</>
) : (
<>
Fortsätt
<ArrowRight className="ml-2 h-4 w-4" />
</>
)}
</Button>
</div>
</div>
</form>
</CardContent>
</Card>
</div>
)
}