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 [bankConnectionError, setBankConnectionError] = 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 [deleteConfirmText, setDeleteConfirmText] = useState('')
|
||||
const [isDeleting, setIsDeleting] = useState(false)
|
||||
@@ -148,10 +150,40 @@ export default function SettingsPage() {
|
||||
e.preventDefault()
|
||||
if (!settings) return
|
||||
|
||||
setIsSaving(true)
|
||||
|
||||
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,
|
||||
// so only include company_name/org_number when not locked
|
||||
const updates: Record<string, unknown> = {
|
||||
@@ -392,6 +424,8 @@ export default function SettingsPage() {
|
||||
<Input
|
||||
id="bank_name"
|
||||
name="bank_name"
|
||||
placeholder="t.ex. Nordea"
|
||||
maxLength={100}
|
||||
defaultValue={settings?.bank_name || ''}
|
||||
/>
|
||||
</div>
|
||||
@@ -400,16 +434,58 @@ export default function SettingsPage() {
|
||||
<Input
|
||||
id="clearing_number"
|
||||
name="clearing_number"
|
||||
inputMode="numeric"
|
||||
placeholder="XXXX"
|
||||
maxLength={5}
|
||||
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 className="space-y-2">
|
||||
<Label htmlFor="account_number">Kontonummer</Label>
|
||||
<Input
|
||||
id="account_number"
|
||||
name="account_number"
|
||||
inputMode="numeric"
|
||||
placeholder="XXXXXXX"
|
||||
maxLength={12}
|
||||
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 className="max-w-xs space-y-2">
|
||||
|
||||
@@ -104,7 +104,6 @@ export default function AccountCombobox({ value, accounts, onChange }: AccountCo
|
||||
onChange(accountNumber)
|
||||
setSearch(accountNumber)
|
||||
setIsOpen(false)
|
||||
inputRef.current?.blur()
|
||||
},
|
||||
[onChange]
|
||||
)
|
||||
|
||||
+4
-4
@@ -369,10 +369,10 @@ export const UpdateSettingsSchema = z.object({
|
||||
moms_period: MomsPeriodSchema.nullable().optional(),
|
||||
fiscal_year_start_month: z.number().int().min(1).max(12).optional(),
|
||||
preliminary_tax_monthly: z.number().nullable().optional(),
|
||||
bank_name: z.string().optional(),
|
||||
clearing_number: z.string().optional(),
|
||||
account_number: z.string().optional(),
|
||||
bankgiro: z.string().nullable().optional(),
|
||||
bank_name: z.string().max(100, 'Banknamn får vara max 100 tecken').optional(),
|
||||
clearing_number: z.string().regex(/^\d{4,5}$/, 'Clearingnummer måste vara 4-5 siffror').optional().or(z.literal('')),
|
||||
account_number: z.string().regex(/^\d{6,12}$/, 'Kontonummer måste vara 6-12 siffror').optional().or(z.literal('')),
|
||||
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(),
|
||||
bic: z.string().optional(),
|
||||
accounting_method: AccountingMethodSchema.optional(),
|
||||
|
||||
Reference in New Issue
Block a user