refactor: extract validation schemas to shared module

This commit is contained in:
2026-03-19 23:15:49 +01:00
parent 4ea87c0cf7
commit dd1e3d9053
6 changed files with 77 additions and 61 deletions

View File

@@ -1,9 +1,10 @@
<script setup lang="ts"> <script setup lang="ts">
import type { AuthFormField, FormSubmitEvent } from '@nuxt/ui' import type { AuthFormField, FormSubmitEvent } from '@nuxt/ui'
import type * as v from 'valibot'
import { useForm } from '@inertiajs/vue3' import { useForm } from '@inertiajs/vue3'
import * as v from 'valibot'
import { computed } from 'vue' import { computed } from 'vue'
import AuthLayout from '@/layouts/AuthLayout.vue' import AuthLayout from '@/layouts/AuthLayout.vue'
import { completeProfileSchema } from '@/validation/auth'
const props = defineProps<{ const props = defineProps<{
socialiteUser: { socialiteUser: {
@@ -46,16 +47,7 @@ const fields: AuthFormField[] = [
}, },
] ]
const schema = v.object({ const schema = completeProfileSchema
username: v.pipe(
v.string('Username is required'),
v.nonEmpty('Username is required'),
v.minLength(3, 'Username must be at least 3 characters'),
v.regex(/^[\w-]+$/, 'Username can only contain letters, numbers, dashes and underscores'),
),
first_name: v.pipe(v.string('First name is required'), v.nonEmpty('First name is required')),
last_name: v.pipe(v.string('Last name is required'), v.nonEmpty('Last name is required')),
})
type Schema = v.InferOutput<typeof schema> type Schema = v.InferOutput<typeof schema>

View File

@@ -1,10 +1,11 @@
<script setup lang="ts"> <script setup lang="ts">
import type { AuthFormField, FormSubmitEvent } from '@nuxt/ui' import type { AuthFormField, FormSubmitEvent } from '@nuxt/ui'
import type * as v from 'valibot'
import { useForm } from '@inertiajs/vue3' import { useForm } from '@inertiajs/vue3'
import * as v from 'valibot'
import { computed } from 'vue' import { computed } from 'vue'
import { useAuth } from '@/composables/useAuth' import { useAuth } from '@/composables/useAuth'
import AuthLayout from '@/layouts/AuthLayout.vue' import AuthLayout from '@/layouts/AuthLayout.vue'
import { forgotPasswordSchema } from '@/validation/auth'
const { config, flash } = useAuth() const { config, flash } = useAuth()
@@ -22,9 +23,7 @@ const fields: AuthFormField[] = [
}, },
] ]
const schema = v.object({ const schema = forgotPasswordSchema
email: v.pipe(v.string('Email is required'), v.nonEmpty('Email is required'), v.email('Please enter a valid email')),
})
type Schema = v.InferOutput<typeof schema> type Schema = v.InferOutput<typeof schema>

View File

@@ -1,10 +1,11 @@
<script setup lang="ts"> <script setup lang="ts">
import type { AuthFormField, FormSubmitEvent } from '@nuxt/ui' import type { AuthFormField, FormSubmitEvent } from '@nuxt/ui'
import type * as v from 'valibot'
import { useForm } from '@inertiajs/vue3' import { useForm } from '@inertiajs/vue3'
import * as v from 'valibot'
import { computed, ref } from 'vue' import { computed, ref } from 'vue'
import { useAuth } from '@/composables/useAuth' import { useAuth } from '@/composables/useAuth'
import AuthLayout from '@/layouts/AuthLayout.vue' import AuthLayout from '@/layouts/AuthLayout.vue'
import { loginSchema } from '@/validation/auth'
const { config, flash } = useAuth() const { config, flash } = useAuth()
@@ -41,11 +42,7 @@ const providers = computed(() =>
})), })),
) )
const schema = v.object({ const schema = loginSchema
login: v.pipe(v.string('Email or username is required'), v.nonEmpty('Email or username is required')),
password: v.pipe(v.string('Password is required'), v.nonEmpty('Password is required')),
remember: v.optional(v.boolean()),
})
type Schema = v.InferOutput<typeof schema> type Schema = v.InferOutput<typeof schema>

View File

@@ -1,10 +1,11 @@
<script setup lang="ts"> <script setup lang="ts">
import type { AuthFormField, FormSubmitEvent } from '@nuxt/ui' import type { AuthFormField, FormSubmitEvent } from '@nuxt/ui'
import type * as v from 'valibot'
import { useForm } from '@inertiajs/vue3' import { useForm } from '@inertiajs/vue3'
import * as v from 'valibot'
import { computed } from 'vue' import { computed } from 'vue'
import { useAuth } from '@/composables/useAuth' import { useAuth } from '@/composables/useAuth'
import AuthLayout from '@/layouts/AuthLayout.vue' import AuthLayout from '@/layouts/AuthLayout.vue'
import { registerSchema } from '@/validation/auth'
const { config } = useAuth() const { config } = useAuth()
@@ -70,29 +71,7 @@ const providers = computed(() =>
})), })),
) )
const schema = v.pipe( const schema = registerSchema
v.object({
username: v.pipe(
v.string('Username is required'),
v.nonEmpty('Username is required'),
v.minLength(3, 'Username must be at least 3 characters'),
v.regex(/^[\w-]+$/, 'Username can only contain letters, numbers, dashes and underscores'),
),
first_name: v.pipe(v.string('First name is required'), v.nonEmpty('First name is required')),
last_name: v.pipe(v.string('Last name is required'), v.nonEmpty('Last name is required')),
email: v.pipe(v.string('Email is required'), v.nonEmpty('Email is required'), v.email('Please enter a valid email')),
password: v.pipe(v.string('Password is required'), v.nonEmpty('Password is required'), v.minLength(8, 'Password must be at least 8 characters')),
password_confirmation: v.pipe(v.string('Please confirm your password'), v.nonEmpty('Please confirm your password')),
}),
v.forward(
v.partialCheck(
[['password'], ['password_confirmation']],
input => input.password === input.password_confirmation,
'Passwords do not match',
),
['password_confirmation'],
),
)
type Schema = v.InferOutput<typeof schema> type Schema = v.InferOutput<typeof schema>

View File

@@ -1,10 +1,11 @@
<script setup lang="ts"> <script setup lang="ts">
import type { AuthFormField, FormSubmitEvent } from '@nuxt/ui' import type { AuthFormField, FormSubmitEvent } from '@nuxt/ui'
import type * as v from 'valibot'
import { useForm } from '@inertiajs/vue3' import { useForm } from '@inertiajs/vue3'
import * as v from 'valibot'
import { computed } from 'vue' import { computed } from 'vue'
import { useAuth } from '@/composables/useAuth' import { useAuth } from '@/composables/useAuth'
import AuthLayout from '@/layouts/AuthLayout.vue' import AuthLayout from '@/layouts/AuthLayout.vue'
import { resetPasswordSchema } from '@/validation/auth'
const props = defineProps<{ const props = defineProps<{
email: string email: string
@@ -44,21 +45,7 @@ const fields: AuthFormField[] = [
}, },
] ]
const schema = v.pipe( const schema = resetPasswordSchema
v.object({
email: v.pipe(v.string('Email is required'), v.nonEmpty('Email is required'), v.email('Please enter a valid email')),
password: v.pipe(v.string('Password is required'), v.nonEmpty('Password is required'), v.minLength(8, 'Password must be at least 8 characters')),
password_confirmation: v.pipe(v.string('Please confirm your password'), v.nonEmpty('Please confirm your password')),
}),
v.forward(
v.partialCheck(
[['password'], ['password_confirmation']],
input => input.password === input.password_confirmation,
'Passwords do not match',
),
['password_confirmation'],
),
)
type Schema = v.InferOutput<typeof schema> type Schema = v.InferOutput<typeof schema>

View File

@@ -0,0 +1,62 @@
import * as v from 'valibot'
export const loginSchema = v.object({
login: v.pipe(v.string('Email or username is required'), v.nonEmpty('Email or username is required')),
password: v.pipe(v.string('Password is required'), v.nonEmpty('Password is required')),
remember: v.optional(v.boolean()),
})
export const registerSchema = v.pipe(
v.object({
username: v.pipe(
v.string('Username is required'),
v.nonEmpty('Username is required'),
v.minLength(3, 'Username must be at least 3 characters'),
v.regex(/^[\w-]+$/, 'Username can only contain letters, numbers, dashes and underscores'),
),
first_name: v.pipe(v.string('First name is required'), v.nonEmpty('First name is required')),
last_name: v.pipe(v.string('Last name is required'), v.nonEmpty('Last name is required')),
email: v.pipe(v.string('Email is required'), v.nonEmpty('Email is required'), v.email('Please enter a valid email')),
password: v.pipe(v.string('Password is required'), v.nonEmpty('Password is required'), v.minLength(8, 'Password must be at least 8 characters')),
password_confirmation: v.pipe(v.string('Please confirm your password'), v.nonEmpty('Please confirm your password')),
}),
v.forward(
v.partialCheck(
[['password'], ['password_confirmation']],
input => input.password === input.password_confirmation,
'Passwords do not match',
),
['password_confirmation'],
),
)
export const forgotPasswordSchema = v.object({
email: v.pipe(v.string('Email is required'), v.nonEmpty('Email is required'), v.email('Please enter a valid email')),
})
export const resetPasswordSchema = v.pipe(
v.object({
email: v.pipe(v.string('Email is required'), v.nonEmpty('Email is required'), v.email('Please enter a valid email')),
password: v.pipe(v.string('Password is required'), v.nonEmpty('Password is required'), v.minLength(8, 'Password must be at least 8 characters')),
password_confirmation: v.pipe(v.string('Please confirm your password'), v.nonEmpty('Please confirm your password')),
}),
v.forward(
v.partialCheck(
[['password'], ['password_confirmation']],
input => input.password === input.password_confirmation,
'Passwords do not match',
),
['password_confirmation'],
),
)
export const completeProfileSchema = v.object({
username: v.pipe(
v.string('Username is required'),
v.nonEmpty('Username is required'),
v.minLength(3, 'Username must be at least 3 characters'),
v.regex(/^[\w-]+$/, 'Username can only contain letters, numbers, dashes and underscores'),
),
first_name: v.pipe(v.string('First name is required'), v.nonEmpty('First name is required')),
last_name: v.pipe(v.string('Last name is required'), v.nonEmpty('Last name is required')),
})