Input validation globally (#86)
* added validation * fixed closure issure
This commit is contained in:
@@ -56,6 +56,8 @@ export default function SettingsPage() {
|
|||||||
const hasMcpExtension = ENABLED_EXTENSION_IDS.has('mcp-server')
|
const hasMcpExtension = ENABLED_EXTENSION_IDS.has('mcp-server')
|
||||||
const [bankConnectionError, setBankConnectionError] = useState<string | null>(null)
|
const [bankConnectionError, setBankConnectionError] = useState<string | null>(null)
|
||||||
const [bankgiroError, setBankgiroError] = useState<string | null>(null)
|
const [bankgiroError, setBankgiroError] = useState<string | null>(null)
|
||||||
|
const [clearingError, setClearingError] = useState<string | null>(null)
|
||||||
|
const [accountNumberError, setAccountNumberError] = useState<string | null>(null)
|
||||||
const [showDeleteDialog, setShowDeleteDialog] = useState(false)
|
const [showDeleteDialog, setShowDeleteDialog] = useState(false)
|
||||||
const [deleteConfirmText, setDeleteConfirmText] = useState('')
|
const [deleteConfirmText, setDeleteConfirmText] = useState('')
|
||||||
const [isDeleting, setIsDeleting] = useState(false)
|
const [isDeleting, setIsDeleting] = useState(false)
|
||||||
@@ -148,10 +150,40 @@ export default function SettingsPage() {
|
|||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
if (!settings) return
|
if (!settings) return
|
||||||
|
|
||||||
setIsSaving(true)
|
|
||||||
|
|
||||||
const formData = new FormData(e.currentTarget)
|
const formData = new FormData(e.currentTarget)
|
||||||
|
|
||||||
|
// Validate bank fields before saving
|
||||||
|
const clearingVal = (formData.get('clearing_number') as string || '').trim()
|
||||||
|
const accountVal = (formData.get('account_number') as string || '').trim()
|
||||||
|
const bankgiroVal = (formData.get('bankgiro') as string || '').trim()
|
||||||
|
|
||||||
|
let hasErrors = false
|
||||||
|
|
||||||
|
if (clearingVal && !/^\d{4,5}$/.test(clearingVal)) {
|
||||||
|
setClearingError('Clearingnummer måste vara 4-5 siffror')
|
||||||
|
hasErrors = true
|
||||||
|
} else {
|
||||||
|
setClearingError(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (accountVal && !/^\d{6,12}$/.test(accountVal)) {
|
||||||
|
setAccountNumberError('Kontonummer måste vara 6-12 siffror')
|
||||||
|
hasErrors = true
|
||||||
|
} else {
|
||||||
|
setAccountNumberError(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bankgiroVal && !validateBankgiroNumber(bankgiroVal)) {
|
||||||
|
setBankgiroError('Ogiltigt bankgironummer (7-8 siffror med kontrollsiffra)')
|
||||||
|
hasErrors = true
|
||||||
|
} else {
|
||||||
|
setBankgiroError(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasErrors) return
|
||||||
|
|
||||||
|
setIsSaving(true)
|
||||||
|
|
||||||
// Disabled inputs are excluded from FormData by the browser,
|
// Disabled inputs are excluded from FormData by the browser,
|
||||||
// so only include company_name/org_number when not locked
|
// so only include company_name/org_number when not locked
|
||||||
const updates: Record<string, unknown> = {
|
const updates: Record<string, unknown> = {
|
||||||
@@ -392,6 +424,8 @@ export default function SettingsPage() {
|
|||||||
<Input
|
<Input
|
||||||
id="bank_name"
|
id="bank_name"
|
||||||
name="bank_name"
|
name="bank_name"
|
||||||
|
placeholder="t.ex. Nordea"
|
||||||
|
maxLength={100}
|
||||||
defaultValue={settings?.bank_name || ''}
|
defaultValue={settings?.bank_name || ''}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -400,16 +434,58 @@ export default function SettingsPage() {
|
|||||||
<Input
|
<Input
|
||||||
id="clearing_number"
|
id="clearing_number"
|
||||||
name="clearing_number"
|
name="clearing_number"
|
||||||
|
inputMode="numeric"
|
||||||
|
placeholder="XXXX"
|
||||||
|
maxLength={5}
|
||||||
defaultValue={settings?.clearing_number || ''}
|
defaultValue={settings?.clearing_number || ''}
|
||||||
|
onChange={(e) => {
|
||||||
|
e.target.value = e.target.value.replace(/\D/g, '')
|
||||||
|
}}
|
||||||
|
onBlur={(e) => {
|
||||||
|
const val = e.target.value.trim()
|
||||||
|
if (!val) {
|
||||||
|
setClearingError(null)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!/^\d{4,5}$/.test(val)) {
|
||||||
|
setClearingError('Clearingnummer måste vara 4-5 siffror')
|
||||||
|
} else {
|
||||||
|
setClearingError(null)
|
||||||
|
}
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
|
{clearingError && (
|
||||||
|
<p className="text-xs text-destructive">{clearingError}</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label htmlFor="account_number">Kontonummer</Label>
|
<Label htmlFor="account_number">Kontonummer</Label>
|
||||||
<Input
|
<Input
|
||||||
id="account_number"
|
id="account_number"
|
||||||
name="account_number"
|
name="account_number"
|
||||||
|
inputMode="numeric"
|
||||||
|
placeholder="XXXXXXX"
|
||||||
|
maxLength={12}
|
||||||
defaultValue={settings?.account_number || ''}
|
defaultValue={settings?.account_number || ''}
|
||||||
|
onChange={(e) => {
|
||||||
|
e.target.value = e.target.value.replace(/\D/g, '')
|
||||||
|
}}
|
||||||
|
onBlur={(e) => {
|
||||||
|
const val = e.target.value.trim()
|
||||||
|
if (!val) {
|
||||||
|
setAccountNumberError(null)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!/^\d{6,12}$/.test(val)) {
|
||||||
|
setAccountNumberError('Kontonummer måste vara 6-12 siffror')
|
||||||
|
} else {
|
||||||
|
setAccountNumberError(null)
|
||||||
|
}
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
|
{accountNumberError && (
|
||||||
|
<p className="text-xs text-destructive">{accountNumberError}</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="max-w-xs space-y-2">
|
<div className="max-w-xs space-y-2">
|
||||||
|
|||||||
@@ -104,7 +104,6 @@ export default function AccountCombobox({ value, accounts, onChange }: AccountCo
|
|||||||
onChange(accountNumber)
|
onChange(accountNumber)
|
||||||
setSearch(accountNumber)
|
setSearch(accountNumber)
|
||||||
setIsOpen(false)
|
setIsOpen(false)
|
||||||
inputRef.current?.blur()
|
|
||||||
},
|
},
|
||||||
[onChange]
|
[onChange]
|
||||||
)
|
)
|
||||||
|
|||||||
+4
-4
@@ -369,10 +369,10 @@ export const UpdateSettingsSchema = z.object({
|
|||||||
moms_period: MomsPeriodSchema.nullable().optional(),
|
moms_period: MomsPeriodSchema.nullable().optional(),
|
||||||
fiscal_year_start_month: z.number().int().min(1).max(12).optional(),
|
fiscal_year_start_month: z.number().int().min(1).max(12).optional(),
|
||||||
preliminary_tax_monthly: z.number().nullable().optional(),
|
preliminary_tax_monthly: z.number().nullable().optional(),
|
||||||
bank_name: z.string().optional(),
|
bank_name: z.string().max(100, 'Banknamn får vara max 100 tecken').optional(),
|
||||||
clearing_number: z.string().optional(),
|
clearing_number: z.string().regex(/^\d{4,5}$/, 'Clearingnummer måste vara 4-5 siffror').optional().or(z.literal('')),
|
||||||
account_number: z.string().optional(),
|
account_number: z.string().regex(/^\d{6,12}$/, 'Kontonummer måste vara 6-12 siffror').optional().or(z.literal('')),
|
||||||
bankgiro: z.string().nullable().optional(),
|
bankgiro: z.string().regex(/^(\d{3,4}-\d{4}|\d{7,8})$/, 'Ogiltigt bankgironummer (7-8 siffror)').nullable().optional().or(z.literal('')),
|
||||||
iban: z.string().optional(),
|
iban: z.string().optional(),
|
||||||
bic: z.string().optional(),
|
bic: z.string().optional(),
|
||||||
accounting_method: AccountingMethodSchema.optional(),
|
accounting_method: AccountingMethodSchema.optional(),
|
||||||
|
|||||||
Reference in New Issue
Block a user