refactor(forms): use arktype schemas for form validation
Replaces imperative validate() blocks in TransactionCreate, signup, and AccountNew with schema-driven validation. Drops the dead "(!currency && !date && payFor.length === 0)" branch in TransactionCreate that was hiding behind the generic error.
This commit is contained in:
@@ -14,11 +14,12 @@ import TransactionType from '@/enums/TransactionType'
|
||||
import TransactionTag, { TransactionTagLabel } from '@/enums/TransactionTag'
|
||||
import exchangeService from '@/services/ExchangeService'
|
||||
import transactionService from '@/services/TransactionService'
|
||||
import queueNotifService from '@/services/QueueNotifService'
|
||||
import mapService from '@/services/MapService'
|
||||
import { money } from '@/utils/format'
|
||||
import { findContrastColor } from '@/utils'
|
||||
import notif from '@/utils/notif'
|
||||
import { validateAndNotify } from '@/utils/validate'
|
||||
import { transactionFormSchema, MAX_TRANSACTION_AMOUNT } from '@/schemas/transaction'
|
||||
import FabButton from '@/components/FabButton.vue'
|
||||
import TransactionSplit from '@/components/TransactionSplit.vue'
|
||||
import TransactionTagUpdate from '@/components/TransactionTagUpdate.vue'
|
||||
@@ -46,7 +47,7 @@ const date = ref(today)
|
||||
const currency = ref<ICurrency | null>(null)
|
||||
const payBy = ref('')
|
||||
const payFor = reactive<ISplit[]>([])
|
||||
const maxAmount = 1_000_000
|
||||
const maxAmount = MAX_TRANSACTION_AMOUNT
|
||||
const displayLocationModal = ref(false)
|
||||
const isSetLocation = ref(false)
|
||||
const location = ref<ILocation | null>(null)
|
||||
@@ -120,23 +121,12 @@ const validLocation = async () => {
|
||||
const validate = (): boolean => {
|
||||
const el = document.querySelector(':focus') as HTMLInputElement | null
|
||||
el?.blur()
|
||||
if (!name.value || !amount.value || !payBy.value || (!currency.value && !date.value && payFor.length === 0)) {
|
||||
if (amount.value === 0) {
|
||||
queueNotifService.error('Le montant doit être supérieur à 0.')
|
||||
} else {
|
||||
queueNotifService.error('Tous les champs sont requis.')
|
||||
}
|
||||
return false
|
||||
}
|
||||
if (isNaN(amount.value) || amount.value > maxAmount) {
|
||||
queueNotifService.error(`Veuillez saisir un montant numérique inférieur à ${money(maxAmount, currency.value)}.`)
|
||||
return false
|
||||
}
|
||||
if (amount.value < 0) {
|
||||
queueNotifService.error('Le montant doit être supérieur à 0.')
|
||||
return false
|
||||
}
|
||||
return true
|
||||
return validateAndNotify(
|
||||
transactionFormSchema,
|
||||
{ name: name.value, amount: amount.value, payBy: payBy.value },
|
||||
t,
|
||||
{ amount: { max: money(maxAmount, currency.value) } }
|
||||
)
|
||||
}
|
||||
|
||||
const submitTransaction = async (finish: () => void) => {
|
||||
|
||||
@@ -7,6 +7,8 @@ import type IUser from '@/models/IUser'
|
||||
import userService from '@/services/UserService'
|
||||
import queueNotifService from '@/services/QueueNotifService'
|
||||
import { slug } from '@/utils'
|
||||
import { validateAndNotify } from '@/utils/validate'
|
||||
import { signupFormSchema } from '@/schemas/user'
|
||||
import LangChanger from '@/components/LangChanger.vue'
|
||||
import AccountList from '@/components/AccountList.vue'
|
||||
import ConfirmButton from '@/components/ConfirmButton.vue'
|
||||
@@ -51,17 +53,16 @@ const signin = async () => {
|
||||
isLoading.value = false
|
||||
}
|
||||
|
||||
const validateRegistration = (): boolean => {
|
||||
if (!userId.value) {
|
||||
queueNotifService.error('Un pseudo est obligatoire.')
|
||||
return false
|
||||
}
|
||||
if (password.value !== confirmPassword.value) {
|
||||
queueNotifService.error('Les mots de passe doivent être identique.')
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
const validateRegistration = (): boolean =>
|
||||
validateAndNotify(
|
||||
signupFormSchema,
|
||||
{
|
||||
userId: userId.value,
|
||||
password: password.value,
|
||||
confirmPassword: confirmPassword.value
|
||||
},
|
||||
t
|
||||
)
|
||||
|
||||
const register = async (confirmed: boolean) => {
|
||||
if (!validateRegistration()) return
|
||||
|
||||
@@ -11,6 +11,8 @@ import type IUser from '@/models/IUser'
|
||||
import accountService from '@/services/AccountService'
|
||||
import queueNotifService from '@/services/QueueNotifService'
|
||||
import { slug } from '@/utils'
|
||||
import { validateAndNotify } from '@/utils/validate'
|
||||
import { accountFormSchema } from '@/schemas/account'
|
||||
import ColorPicker from '@/components/ColorPicker.vue'
|
||||
import AccountUserNew from '@/components/AccountUserNew.vue'
|
||||
|
||||
@@ -55,27 +57,13 @@ watch(
|
||||
)
|
||||
|
||||
const validate = (allUsers: IUser[]): boolean => {
|
||||
if (!name.value) {
|
||||
queueNotifService.error('Le nom doit être rempli.')
|
||||
return false
|
||||
}
|
||||
const aliases = allUsers.map((u) => (u.alias ? u.alias.toLowerCase() : ''))
|
||||
if (aliases.length === 0) {
|
||||
queueNotifService.error('Au moins une personne doit être ajoutée au compte.')
|
||||
return false
|
||||
}
|
||||
if (aliases.length !== new Set(aliases).size) {
|
||||
queueNotifService.error('Les alias doivent être uniques.')
|
||||
return false
|
||||
}
|
||||
if (user.value) {
|
||||
const userIds = allUsers.map((u) => u.userId)
|
||||
if (userIds.length !== new Set(userIds).size) {
|
||||
queueNotifService.error('Les identifiants doivent être uniques.')
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
const userIds = user.value ? allUsers.map((u) => u.userId) : undefined
|
||||
return validateAndNotify(
|
||||
accountFormSchema,
|
||||
{ name: name.value, aliases, userIds },
|
||||
t
|
||||
)
|
||||
}
|
||||
|
||||
const submitAccount = async () => {
|
||||
|
||||
Reference in New Issue
Block a user