feat: basic onboarding flow\
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
export * from './lib/problem-details.js';
|
||||
export * from './lib/result.js';
|
||||
export * from './schemas/enums.js';
|
||||
export * from './schemas/groups.js';
|
||||
export * from './schemas/health-check.js';
|
||||
export * from './schemas/onboarding.js';
|
||||
export * from './schemas/pagination.js';
|
||||
export * from './schemas/payments.js';
|
||||
export * from './schemas/problem-details.js';
|
||||
|
||||
21
packages/shared/src/schemas/enums.ts
Normal file
21
packages/shared/src/schemas/enums.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
// Días de la semana en que se dictan las clases.
|
||||
export const weekdaySchema = z.enum([
|
||||
'MONDAY',
|
||||
'TUESDAY',
|
||||
'WEDNESDAY',
|
||||
'THURSDAY',
|
||||
'FRIDAY',
|
||||
'SATURDAY',
|
||||
'SUNDAY',
|
||||
]);
|
||||
export type WeekDay = z.output<typeof weekdaySchema>;
|
||||
|
||||
// Procesadores de pago soportados.
|
||||
export const paymentProviderSchema = z.enum(['MERCADO_PAGO', 'STRIPE']);
|
||||
export type PaymentProvider = z.output<typeof paymentProviderSchema>;
|
||||
|
||||
// Frecuencia de cobro de un grupo.
|
||||
export const billingTypeSchema = z.enum(['MONTHLY', 'PER_CLASS']);
|
||||
export type BillingType = z.output<typeof billingTypeSchema>;
|
||||
@@ -1,4 +1,5 @@
|
||||
import { z } from 'zod';
|
||||
import { billingTypeSchema, weekdaySchema } from './enums.js';
|
||||
import { createPageSchema, createPageSizeSchema, createPaginationSchema } from './pagination.js';
|
||||
|
||||
const isoDateTimeSchema = z.string().datetime();
|
||||
@@ -13,6 +14,13 @@ export const GroupDtoSchema = z.object({
|
||||
createdById: z.string(),
|
||||
createdAt: isoDateTimeSchema,
|
||||
updatedAt: isoDateTimeSchema,
|
||||
// Horario y cobro: quedan vacíos si el grupo aún no tiene configurado el plan.
|
||||
days: z.array(weekdaySchema).nullable(),
|
||||
time: z.string().nullable(),
|
||||
capacity: z.number().int().min(1).nullable(),
|
||||
price: z.number().min(0).nullable(),
|
||||
billingType: billingTypeSchema.nullable(),
|
||||
dueDay: z.number().int().min(1).max(28).nullable(),
|
||||
});
|
||||
export type GroupDto = z.output<typeof GroupDtoSchema>;
|
||||
|
||||
|
||||
76
packages/shared/src/schemas/onboarding.ts
Normal file
76
packages/shared/src/schemas/onboarding.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
import { z } from 'zod';
|
||||
import { billingTypeSchema, paymentProviderSchema, weekdaySchema } from './enums.js';
|
||||
import { GroupDtoSchema } from './groups.js';
|
||||
|
||||
export type { BillingType, PaymentProvider, WeekDay } from './enums.js';
|
||||
export { billingTypeSchema, paymentProviderSchema, weekdaySchema } from './enums.js';
|
||||
|
||||
// Paso 2: vincula la cuenta de cobro del profesor.
|
||||
export const ConnectPaymentSchema = z.strictObject({
|
||||
provider: paymentProviderSchema,
|
||||
// Para el MVP se conecta en modo simulación / sandbox.
|
||||
sandbox: z.boolean().default(true),
|
||||
accessToken: z.string().optional(),
|
||||
});
|
||||
export type ConnectPayment = z.output<typeof ConnectPaymentSchema>;
|
||||
|
||||
export const ConnectPaymentResultSchema = z.object({
|
||||
provider: paymentProviderSchema,
|
||||
sandbox: z.boolean(),
|
||||
connected: z.boolean(),
|
||||
});
|
||||
export type ConnectPaymentResult = z.output<typeof ConnectPaymentResultSchema>;
|
||||
|
||||
// Paso 3: crea el primer grupo del profesor.
|
||||
export const CreateFirstGroupSchema = z.strictObject({
|
||||
name: z
|
||||
.string()
|
||||
.trim()
|
||||
.min(3, 'El nombre debe tener al menos 3 caracteres')
|
||||
.max(60, 'El nombre es demasiado largo (máximo 60 caracteres)'),
|
||||
days: z.array(weekdaySchema).min(1, 'Elegí al menos un día de clase'),
|
||||
time: z
|
||||
.string()
|
||||
.regex(/^([01]?\d|2[0-3]):[0-5]\d$/, 'Elegí un horario válido (ej: 09:00)'),
|
||||
capacity: z
|
||||
.number({ invalid_type_error: 'Ingresá un cupo válido' })
|
||||
.int({ message: 'El cupo debe ser un número entero' })
|
||||
.min(1, 'El cupo debe ser al menos 1'),
|
||||
price: z
|
||||
.number({ invalid_type_error: 'Ingresá un precio válido' })
|
||||
.min(0, 'El precio no puede ser negativo'),
|
||||
billingType: billingTypeSchema,
|
||||
dueDay: z
|
||||
.number({ invalid_type_error: 'Ingresá un día válido' })
|
||||
.int({ message: 'El día debe ser un número entero' })
|
||||
.min(1, 'El día de vencimiento debe estar entre 1 y 28')
|
||||
.max(28, 'El día de vencimiento debe estar entre 1 y 28'),
|
||||
});
|
||||
export type CreateFirstGroup = z.output<typeof CreateFirstGroupSchema>;
|
||||
|
||||
export const OnboardingGroupDtoSchema = GroupDtoSchema.extend({
|
||||
days: z.array(weekdaySchema).nullable(),
|
||||
time: z.string().nullable(),
|
||||
capacity: z.number().int().min(1).nullable(),
|
||||
price: z.number().min(0).nullable(),
|
||||
billingType: billingTypeSchema.nullable(),
|
||||
dueDay: z.number().int().min(1).max(28).nullable(),
|
||||
});
|
||||
export type OnboardingGroupDto = z.output<typeof OnboardingGroupDtoSchema>;
|
||||
|
||||
export const CreateFirstGroupResultSchema = z.object({
|
||||
group: OnboardingGroupDtoSchema,
|
||||
onboardingCompleted: z.boolean(),
|
||||
});
|
||||
export type CreateFirstGroupResult = z.output<typeof CreateFirstGroupResultSchema>;
|
||||
|
||||
// Estado del onboarding para retomar el flujo si se interrumpió.
|
||||
export const onboardingStepSchema = z.enum(['NOT_STARTED', 'PAYMENT_CONNECTED', 'COMPLETED']);
|
||||
export type OnboardingStep = z.output<typeof onboardingStepSchema>;
|
||||
|
||||
export const OnboardingStatusDtoSchema = z.object({
|
||||
step: onboardingStepSchema,
|
||||
paymentConnected: z.boolean(),
|
||||
completed: z.boolean(),
|
||||
});
|
||||
export type OnboardingStatusDto = z.output<typeof OnboardingStatusDtoSchema>;
|
||||
Reference in New Issue
Block a user