Vat onboarding fixes (#39)

* fix: enhance validation for moms_period and conditionally set vat_number and moms_period based on vat_registered

* Enhance preprocessing for first year fields, fiscal year end month, and accounting method in schema validation

* fix: simplify schema validation by removing unnecessary preprocessors

* fix: prevent setting invalid values in select components across multiple forms

* fix: improve layout responsiveness and conditional rendering in onboarding steps
This commit is contained in:
Mattsson
2026-03-17 14:28:58 +01:00
committed by GitHub
parent 3e82295cce
commit 8976cd812d
11 changed files with 42 additions and 42 deletions
+1 -13
View File
@@ -786,20 +786,8 @@ export default function ImportPage() {
</p>
</div>
{mode === null && (
{mode === null && hasBankingExtension !== null && (
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
{hasBankingExtension === null && (
<Card className="animate-pulse">
<CardContent className="pt-6 pb-6 flex flex-col items-center text-center space-y-3">
<div className="h-12 w-12 rounded-full bg-muted" />
<div className="space-y-2 w-full flex flex-col items-center">
<div className="h-5 bg-muted rounded w-24" />
<div className="h-4 bg-muted rounded w-48" />
<div className="h-4 bg-muted rounded w-40" />
</div>
</CardContent>
</Card>
)}
{hasBankingExtension === true && (
<Card
role="button"
+7 -3
View File
@@ -84,15 +84,19 @@ export default function AccountCombobox({ value, accounts, onChange }: AccountCo
}
}, [highlightedIndex, isOpen])
// Close dropdown when clicking outside
// Close dropdown when clicking/tapping outside
useEffect(() => {
function handleClickOutside(e: MouseEvent) {
function handleClickOutside(e: MouseEvent | TouchEvent) {
if (containerRef.current && !containerRef.current.contains(e.target as Node)) {
setIsOpen(false)
}
}
document.addEventListener('mousedown', handleClickOutside)
return () => document.removeEventListener('mousedown', handleClickOutside)
document.addEventListener('touchstart', handleClickOutside)
return () => {
document.removeEventListener('mousedown', handleClickOutside)
document.removeEventListener('touchstart', handleClickOutside)
}
}, [])
const selectAccount = useCallback(
+1 -1
View File
@@ -146,7 +146,7 @@ export function AddAccountDialog({ open, onOpenChange, onCreated }: AddAccountDi
</div>
<div className="space-y-2">
<Label>Normal saldo</Label>
<Select value={normalBalance} onValueChange={(v) => setNormalBalance(v as 'debit' | 'credit')}>
<Select value={normalBalance} onValueChange={(v) => { if (v) setNormalBalance(v as 'debit' | 'credit') }}>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
+1 -1
View File
@@ -136,7 +136,7 @@ export default function CustomerForm({
name="customer_type"
control={control}
render={({ field }) => (
<Select value={field.value} onValueChange={field.onChange}>
<Select value={field.value} onValueChange={(v) => { if (v) field.onChange(v) }}>
<SelectTrigger>
<SelectValue placeholder="Välj kundtyp" />
</SelectTrigger>
+3 -3
View File
@@ -176,7 +176,7 @@ export function DeadlineForm({
<Label>Typ *</Label>
<Select
value={formData.deadline_type}
onValueChange={(v) => updateField('deadline_type', v as DeadlineType)}
onValueChange={(v) => { if (v) updateField('deadline_type', v as DeadlineType) }}
>
<SelectTrigger>
<SelectValue />
@@ -194,7 +194,7 @@ export function DeadlineForm({
<Label>Prioritet</Label>
<Select
value={formData.priority}
onValueChange={(v) => updateField('priority', v as DeadlinePriority)}
onValueChange={(v) => { if (v) updateField('priority', v as DeadlinePriority) }}
>
<SelectTrigger>
<SelectValue />
@@ -216,7 +216,7 @@ export function DeadlineForm({
<Label>Kund (valfritt)</Label>
<Select
value={formData.customer_id || 'none'}
onValueChange={(v) => updateField('customer_id', v === 'none' ? '' : v)}
onValueChange={(v) => { if (v) updateField('customer_id', v === 'none' ? '' : v) }}
>
<SelectTrigger>
<SelectValue placeholder="Välj kund..." />
@@ -82,7 +82,7 @@ export default function BankFileColumnMappingStep({
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div className="space-y-2">
<Label>Avgränsare</Label>
<Select value={delimiter} onValueChange={setDelimiter}>
<Select value={delimiter} onValueChange={(v) => { if (v) setDelimiter(v) }}>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
@@ -95,7 +95,7 @@ export default function BankFileColumnMappingStep({
</div>
<div className="space-y-2">
<Label>Decimalavgränsare</Label>
<Select value={decimalSep} onValueChange={(v) => setDecimalSep(v as ',' | '.')}>
<Select value={decimalSep} onValueChange={(v) => { if (v) setDecimalSep(v as ',' | '.') }}>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
+18 -11
View File
@@ -47,6 +47,13 @@ const schema = z.object({
})
}
}
if (data.vat_registered && !data.moms_period) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: 'Välj momsredovisningsperiod.',
path: ['moms_period'],
})
}
})
type FormData = z.infer<typeof schema>
@@ -272,8 +279,8 @@ export default function Step3TaxRegistration({
...(firstStart && { first_year_start: firstStart }),
...(firstEnd && { first_year_end: firstEnd }),
vat_registered: data.vat_registered,
vat_number: data.vat_number,
moms_period: data.moms_period,
vat_number: data.vat_registered ? data.vat_number : undefined,
moms_period: data.vat_registered ? data.moms_period : undefined,
accounting_method: data.accounting_method,
}
@@ -431,10 +438,10 @@ export default function Step3TaxRegistration({
}
return (
<div className="grid grid-cols-2 gap-2">
<div className="grid grid-cols-1 sm:grid-cols-2 gap-2">
<Select
value={startMonth ? startMonth.toString() : ''}
onValueChange={(v) => handleMonthChange(parseInt(v))}
onValueChange={(v) => { if (v) handleMonthChange(parseInt(v)) }}
>
<SelectTrigger>
<SelectValue placeholder="Månad" />
@@ -447,7 +454,7 @@ export default function Step3TaxRegistration({
</Select>
<Select
value={startYear ? startYear.toString() : ''}
onValueChange={(v) => handleYearChange(parseInt(v))}
onValueChange={(v) => { if (v) handleYearChange(parseInt(v)) }}
>
<SelectTrigger>
<SelectValue placeholder="År" />
@@ -476,7 +483,7 @@ export default function Step3TaxRegistration({
<Label>Räkenskapsåret slutar (månad)</Label>
<Select
value={abEndMonth.toString()}
onValueChange={(v) => setAbEndMonth(parseInt(v))}
onValueChange={(v) => { if (v) setAbEndMonth(parseInt(v)) }}
>
<SelectTrigger>
<SelectValue placeholder="Välj månad" />
@@ -502,7 +509,7 @@ export default function Step3TaxRegistration({
render={({ field }) => (
<Select
value={field.value || ''}
onValueChange={field.onChange}
onValueChange={(v) => { if (v) field.onChange(v) }}
>
<SelectTrigger>
<SelectValue placeholder="Välj slutdatum" />
@@ -565,7 +572,7 @@ export default function Step3TaxRegistration({
render={({ field }) => (
<Select
value={field.value?.toString() || '12'}
onValueChange={(value) => field.onChange(parseInt(value))}
onValueChange={(v) => { if (v) field.onChange(parseInt(v)) }}
>
<SelectTrigger>
<SelectValue placeholder="Välj månad" />
@@ -644,7 +651,7 @@ export default function Step3TaxRegistration({
</div>
{vatRegistered && (
<div className="space-y-4 pl-7">
<div className="space-y-4 pl-0 sm:pl-7">
<div className="space-y-2">
<Label htmlFor="vat_number">Momsregistreringsnummer</Label>
<Input
@@ -680,7 +687,7 @@ export default function Step3TaxRegistration({
render={({ field }) => (
<Select
value={field.value}
onValueChange={field.onChange}
onValueChange={(v) => { if (v) field.onChange(v) }}
>
<SelectTrigger>
<SelectValue placeholder="Välj period" />
@@ -716,7 +723,7 @@ export default function Step3TaxRegistration({
render={({ field }) => (
<Select
value={field.value}
onValueChange={field.onChange}
onValueChange={(v) => { if (v) field.onChange(v) }}
>
<SelectTrigger>
<SelectValue placeholder="Välj metod" />
@@ -95,7 +95,7 @@ export default function Step4PreliminaryTax({
</ul>
</div>
<div className="flex justify-between pt-4">
<div className="flex flex-col-reverse sm:flex-row sm:justify-between gap-3 pt-4">
<Button
type="button"
variant="ghost"
@@ -105,17 +105,18 @@ export default function Step4PreliminaryTax({
<ArrowLeft className="mr-2 h-4 w-4" />
Tillbaka
</Button>
<div className="flex gap-2">
<div className="flex flex-col sm:flex-row gap-2">
<Button
type="button"
variant="outline"
onClick={onSkip}
disabled={isSaving}
className="w-full sm:w-auto"
>
<SkipForward className="mr-2 h-4 w-4" />
Hoppa över
</Button>
<Button type="submit" disabled={isSaving}>
<Button type="submit" disabled={isSaving} className="w-full sm:w-auto">
{isSaving ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
+2 -2
View File
@@ -1,6 +1,5 @@
'use client'
import { useState } from 'react'
import { useForm } from 'react-hook-form'
import { zodResolver } from '@hookform/resolvers/zod'
import { z } from 'zod'
@@ -179,7 +178,7 @@ export default function Step6ConnectBank({
</Button>
</form>
<div className="flex justify-between pt-4">
<div className="flex flex-col-reverse sm:flex-row sm:justify-between gap-3 pt-4">
<Button
type="button"
variant="ghost"
@@ -192,6 +191,7 @@ export default function Step6ConnectBank({
type="button"
variant="outline"
onClick={onSkip}
className="w-full sm:w-auto"
>
<SkipForward className="mr-2 h-4 w-4" />
Hoppa över
+2 -2
View File
@@ -91,7 +91,7 @@ export default function SupplierForm({
name="supplier_type"
control={control}
render={({ field }) => (
<Select value={field.value} onValueChange={field.onChange}>
<Select value={field.value} onValueChange={(v) => { if (v) field.onChange(v) }}>
<SelectTrigger>
<SelectValue placeholder="Välj typ" />
</SelectTrigger>
@@ -244,7 +244,7 @@ export default function SupplierForm({
name="default_currency"
control={control}
render={({ field }) => (
<Select value={field.value} onValueChange={field.onChange}>
<Select value={field.value} onValueChange={(v) => { if (v) field.onChange(v) }}>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
@@ -21,7 +21,7 @@ export default function VatTreatmentSelect({
return (
<Select
value={value}
onValueChange={(v) => onValueChange(v as VatTreatment | 'none')}
onValueChange={(v) => { if (v) onValueChange(v as VatTreatment | 'none') }}
disabled={disabled}
>
<SelectTrigger className="h-9">