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>
135 lines
4.2 KiB
TypeScript
135 lines
4.2 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">
|
|
<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>
|
|
)
|
|
}
|