feat: sessão #3 — lib (db/auth/email/validations), API routes, NextAuth v5, middleware, páginas account/shelters/shelter-dashboard, Prisma v7 fix
This commit is contained in:
32
lib/validations/animal.ts
Normal file
32
lib/validations/animal.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
export const animalFilterSchema = z.object({
|
||||
district: z.string().optional(),
|
||||
species: z.enum(['DOG', 'CAT', 'OTHER']).optional(),
|
||||
sex: z.enum(['MALE', 'FEMALE']).optional(),
|
||||
sterilized: z.coerce.boolean().optional(),
|
||||
urgent: z.coerce.boolean().optional(),
|
||||
status: z.enum(['AVAILABLE', 'RESERVED', 'ADOPTED']).optional(),
|
||||
page: z.coerce.number().int().positive().default(1),
|
||||
limit: z.coerce.number().int().min(1).max(50).default(12),
|
||||
});
|
||||
|
||||
export const createAnimalSchema = z.object({
|
||||
name: z.string().min(1, 'Nome obrigatório.').max(80),
|
||||
species: z.enum(['DOG', 'CAT', 'OTHER']),
|
||||
breed: z.string().max(80).optional(),
|
||||
ageMonths: z.number().int().min(0).max(300),
|
||||
sex: z.enum(['MALE', 'FEMALE']),
|
||||
sterilized: z.boolean(),
|
||||
urgent: z.boolean().default(false),
|
||||
description: z.string().max(2000).optional(),
|
||||
shelterId: z.string().cuid(),
|
||||
});
|
||||
|
||||
export const updateAnimalSchema = createAnimalSchema
|
||||
.partial()
|
||||
.extend({ status: z.enum(['AVAILABLE', 'RESERVED', 'ADOPTED']).optional() });
|
||||
|
||||
export type AnimalFilter = z.infer<typeof animalFilterSchema>;
|
||||
export type CreateAnimalInput = z.infer<typeof createAnimalSchema>;
|
||||
export type UpdateAnimalInput = z.infer<typeof updateAnimalSchema>;
|
||||
55
lib/validations/auth.ts
Normal file
55
lib/validations/auth.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
const DISTRITOS_PT = [
|
||||
'Aveiro', 'Beja', 'Braga', 'Bragança', 'Castelo Branco', 'Coimbra',
|
||||
'Évora', 'Faro', 'Guarda', 'Leiria', 'Lisboa', 'Portalegre', 'Porto',
|
||||
'Santarém', 'Setúbal', 'Viana do Castelo', 'Vila Real', 'Viseu',
|
||||
'Açores', 'Madeira',
|
||||
] as const;
|
||||
|
||||
export const loginSchema = z.object({
|
||||
email: z.string().email('Email inválido.'),
|
||||
password: z.string().min(1, 'Palavra-passe obrigatória.'),
|
||||
});
|
||||
|
||||
export const registerSchema = z
|
||||
.object({
|
||||
name: z.string().min(2, 'Nome deve ter pelo menos 2 caracteres.').max(100),
|
||||
email: z.string().email('Email inválido.'),
|
||||
password: z.string()
|
||||
.min(8, 'A palavra-passe deve ter pelo menos 8 caracteres.')
|
||||
.regex(/[A-Z]/, 'Deve conter pelo menos uma letra maiúscula.')
|
||||
.regex(/[0-9]/, 'Deve conter pelo menos um número.'),
|
||||
confirmPassword: z.string(),
|
||||
birthdate: z.string().refine((v) => !isNaN(new Date(v).getTime()), 'Data inválida.'),
|
||||
district: z.enum(DISTRITOS_PT, { error: 'Selecciona um distrito válido.' }),
|
||||
terms: z.literal(true, { error: 'Tens de aceitar os termos.' }),
|
||||
})
|
||||
.refine((d) => d.password === d.confirmPassword, {
|
||||
message: 'As palavras-passe não coincidem.',
|
||||
path: ['confirmPassword'],
|
||||
});
|
||||
|
||||
export const forgotPasswordSchema = z.object({
|
||||
email: z.string().email('Email inválido.'),
|
||||
});
|
||||
|
||||
export const resetPasswordSchema = z
|
||||
.object({
|
||||
token: z.string().min(1),
|
||||
password: z.string()
|
||||
.min(8, 'A palavra-passe deve ter pelo menos 8 caracteres.')
|
||||
.regex(/[A-Z]/, 'Deve conter pelo menos uma letra maiúscula.')
|
||||
.regex(/[0-9]/, 'Deve conter pelo menos um número.'),
|
||||
confirmPassword: z.string(),
|
||||
})
|
||||
.refine((d) => d.password === d.confirmPassword, {
|
||||
message: 'As palavras-passe não coincidem.',
|
||||
path: ['confirmPassword'],
|
||||
});
|
||||
|
||||
export type LoginInput = z.infer<typeof loginSchema>;
|
||||
export type RegisterInput = z.infer<typeof registerSchema>;
|
||||
export type ForgotPasswordInput = z.infer<typeof forgotPasswordSchema>;
|
||||
export type ResetPasswordInput = z.infer<typeof resetPasswordSchema>;
|
||||
export const DISTRITOS = DISTRITOS_PT;
|
||||
35
lib/validations/donation.ts
Normal file
35
lib/validations/donation.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
export const createDonationSchema = z.discriminatedUnion('type', [
|
||||
z.object({
|
||||
type: z.literal('MONETARY'),
|
||||
shelterId: z.string().cuid(),
|
||||
details: z.object({
|
||||
amount: z.number().min(1, 'Mínimo 1€.').max(10000),
|
||||
currency: z.literal('EUR').default('EUR'),
|
||||
deliveryMethod: z.literal('payment'),
|
||||
}),
|
||||
}),
|
||||
z.object({
|
||||
type: z.literal('FOOD'),
|
||||
shelterId: z.string().cuid(),
|
||||
details: z.object({
|
||||
foodType: z.enum(['dry', 'wet']),
|
||||
animalType: z.enum(['dog', 'cat']),
|
||||
ageGroup: z.enum(['adult', 'puppy']),
|
||||
deliveryMethod: z.enum(['pickup', 'home_delivery']),
|
||||
address: z.string().max(200).optional(),
|
||||
}),
|
||||
}),
|
||||
z.object({
|
||||
type: z.literal('TOYS'),
|
||||
shelterId: z.string().cuid(),
|
||||
details: z.object({
|
||||
category: z.enum(['chew', 'plush', 'interactive']),
|
||||
deliveryMethod: z.enum(['pickup', 'home_delivery']),
|
||||
address: z.string().max(200).optional(),
|
||||
}),
|
||||
}),
|
||||
]);
|
||||
|
||||
export type CreateDonationInput = z.infer<typeof createDonationSchema>;
|
||||
17
lib/validations/reservation.ts
Normal file
17
lib/validations/reservation.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
export const createReservationSchema = z.object({
|
||||
animalId: z.string().cuid('ID de animal inválido.'),
|
||||
date: z
|
||||
.string()
|
||||
.refine((v) => !isNaN(new Date(v).getTime()), 'Data inválida.')
|
||||
.refine((v) => new Date(v) > new Date(), 'A data tem de ser no futuro.'),
|
||||
notes: z.string().max(500).optional(),
|
||||
});
|
||||
|
||||
export const updateReservationSchema = z.object({
|
||||
status: z.enum(['CONFIRMED', 'CANCELLED', 'COMPLETED']),
|
||||
});
|
||||
|
||||
export type CreateReservationInput = z.infer<typeof createReservationSchema>;
|
||||
export type UpdateReservationInput = z.infer<typeof updateReservationSchema>;
|
||||
Reference in New Issue
Block a user