Añadido Dockerfil docker-compose.yml
This commit is contained in:
Regular → Executable
+122
-22
@@ -1,40 +1,140 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
export const PacienteSchema = z.object({
|
||||
// ─── Enums matching DB constraints ─────────────────────────────────────
|
||||
|
||||
const patientStatusEnum = z.enum(["prospecto", "activo", "inactivo"]);
|
||||
const slotStatusEnum = z.enum(["available", "confirmed", "cancelled", "completed", "reschedule", "blocked"]);
|
||||
const blockTypeEnum = z.enum(["surgery", "admin", "vacation", "event", "appointment"]);
|
||||
const messageDirectionEnum = z.enum(["in", "out"]);
|
||||
const messageTypeEnum = z.enum(["text", "audio", "image", "document"]);
|
||||
const conversationStatusEnum = z.enum(["active", "pending_human", "resolved"]);
|
||||
const channelEnum = z.enum(["whatsapp", "messenger", "instagram", "tiktok", "web", "manual"]);
|
||||
const templateTypeEnum = z.enum(["confirmation", "reminder", "reschedule", "post_op", "notification", "news", "surgeon_instructions"]);
|
||||
const notificationTypeEnum = z.enum([
|
||||
"new_appointment",
|
||||
"appointment_cancelled",
|
||||
"human_handoff",
|
||||
"new_patient",
|
||||
"appointment_reminder_2h",
|
||||
"daily_summary",
|
||||
"appointment_rescheduled",
|
||||
"appointment_reminder_next_day",
|
||||
]);
|
||||
|
||||
// ─── Domain Schemas ─────────────────────────────────────────────────────
|
||||
|
||||
const phoneRegex = /^[+]?[\d\s\-().]{7,30}$/;
|
||||
|
||||
export const PatientSchema = z.object({
|
||||
id: z.string().uuid().optional(),
|
||||
nombre: z.string().min(2, "El nombre debe tener al menos 2 caracteres"),
|
||||
telefono: z.string().optional(),
|
||||
email: z.string().email("Correo inválido").optional(),
|
||||
notas_clinicas: z.string().optional(),
|
||||
name: z.string().min(2, "Name must be at least 2 characters").max(100, "Name too long").trim(),
|
||||
phone: z.string().regex(phoneRegex, "Invalid phone format").max(30).optional().nullable(),
|
||||
email: z.string().email("Invalid email").max(255).optional().nullable(),
|
||||
status: patientStatusEnum.default("prospecto"),
|
||||
});
|
||||
|
||||
export const ProcedimientoSchema = z.object({
|
||||
export const PatientProfileSchema = z.object({
|
||||
id: z.string().uuid().optional(),
|
||||
nombre: z.string().min(2, "Procedimiento requerido"),
|
||||
duracion_minutos: z.number().int().positive("Obligatorio"),
|
||||
precio: z.number().positive().optional(),
|
||||
patient_id: z.string().uuid(),
|
||||
interested_procedures: z.array(z.string().max(100)).default([]),
|
||||
notes: z.string().max(5000).optional().nullable(),
|
||||
last_interaction: z.string().datetime().optional().nullable(),
|
||||
});
|
||||
|
||||
export const CitaSchema = z.object({
|
||||
export const ProcedureSchema = z.object({
|
||||
id: z.string().uuid().optional(),
|
||||
paciente_id: z.string().uuid(),
|
||||
procedimiento_id: z.string().uuid().optional(),
|
||||
start_time: z.string().datetime(),
|
||||
end_time: z.string().datetime(),
|
||||
status: z.enum(["agendada", "completada", "cancelada"]).default("agendada"),
|
||||
medico_id: z.string().uuid(),
|
||||
name: z.string().min(2, "Procedure name is required").max(120),
|
||||
description: z.string().max(2000).optional().nullable(),
|
||||
duration_min: z.number().int().positive("Duration is required"),
|
||||
preparation_notes: z.string().max(2000).optional().nullable(),
|
||||
is_active: z.boolean().default(true),
|
||||
});
|
||||
|
||||
export const InteraccionSchema = z.object({
|
||||
export const SlotSchema = z.object({
|
||||
id: z.string().uuid().optional(),
|
||||
paciente_id: z.string().uuid(),
|
||||
plataforma: z.enum(["whatsapp", "instagram", "tiktok", "web"]),
|
||||
request_payload: z.any(),
|
||||
start_at: z.string().datetime(),
|
||||
end_at: z.string().datetime(),
|
||||
block_type: blockTypeEnum,
|
||||
patient_id: z.string().uuid().optional().nullable(),
|
||||
procedure_id: z.string().uuid().optional().nullable(),
|
||||
status: slotStatusEnum.default("available"),
|
||||
channel: channelEnum.optional().nullable(),
|
||||
notes: z.string().max(2000).optional().nullable(),
|
||||
});
|
||||
|
||||
export const MessageSchema = z.object({
|
||||
id: z.string().uuid().optional(),
|
||||
patient_id: z.string().uuid(),
|
||||
conversation_id: z.string().uuid().optional().nullable(),
|
||||
channel: channelEnum,
|
||||
direction: messageDirectionEnum,
|
||||
type: messageTypeEnum,
|
||||
content: z.string().max(4000).optional().nullable(),
|
||||
audio_url: z.string().url().optional().nullable(),
|
||||
transcript: z.string().max(4000).optional().nullable(),
|
||||
created_at: z.string().datetime().optional(),
|
||||
});
|
||||
|
||||
export const ConversationSchema = z.object({
|
||||
id: z.string().uuid().optional(),
|
||||
patient_id: z.string().uuid(),
|
||||
channel: channelEnum,
|
||||
status: conversationStatusEnum.default("active"),
|
||||
ai_context: z.record(z.string(), z.unknown()).optional().nullable(),
|
||||
last_message_at: z.string().datetime().optional(),
|
||||
});
|
||||
|
||||
export const StaffSchema = z.object({
|
||||
id: z.string().uuid().optional(),
|
||||
name: z.string().min(2).max(100).trim(),
|
||||
role: z.enum(["admin", "staff"]),
|
||||
email: z.string().trim().email().max(255).toLowerCase(),
|
||||
is_active: z.boolean().default(true),
|
||||
});
|
||||
|
||||
export const StaffNotificationSchema = z.object({
|
||||
id: z.string().uuid().optional(),
|
||||
staff_id: z.string().uuid(),
|
||||
type: notificationTypeEnum,
|
||||
payload: z.record(z.string(), z.unknown()).default({}),
|
||||
is_read: z.boolean().default(false),
|
||||
created_at: z.string().datetime().optional(),
|
||||
});
|
||||
|
||||
const timeRegex = /^([01]\d|2[0-3]):([0-5]\d)$/;
|
||||
|
||||
// WorkingHours: day-keyed object matching DB structure { "mon": ["08:00","18:00"], ... }
|
||||
export const WorkingHoursSchema = z.record(
|
||||
z.string(),
|
||||
z.tuple([z.string().regex(timeRegex), z.string().regex(timeRegex)]).nullable()
|
||||
);
|
||||
|
||||
export const FaqItemSchema = z.object({
|
||||
q: z.string().min(1).max(500),
|
||||
a: z.string().min(1).max(2000),
|
||||
});
|
||||
|
||||
export const AgentConfigSchema = z.object({
|
||||
id: z.string().uuid().optional(),
|
||||
system_prompt: z.string().min(1).max(8000),
|
||||
working_hours: WorkingHoursSchema,
|
||||
faq: z.array(FaqItemSchema).default([]),
|
||||
enabled_channels: z.array(channelEnum).default(["whatsapp", "web"]),
|
||||
updated_at: z.string().datetime().optional(),
|
||||
});
|
||||
|
||||
export const MessageTemplateSchema = z.object({
|
||||
id: z.string().uuid().optional(),
|
||||
name: z.string().min(1).max(120),
|
||||
type: templateTypeEnum,
|
||||
content: z.string().min(1).max(4000),
|
||||
channel: channelEnum.optional().nullable(),
|
||||
is_active: z.boolean().default(true),
|
||||
});
|
||||
|
||||
export const ChatStatusSchema = z.object({
|
||||
id: z.string().uuid().optional(),
|
||||
paciente_id: z.string().uuid(),
|
||||
patient_id: z.string().uuid(),
|
||||
ia_activa: z.boolean().default(true),
|
||||
last_updated: z.string().datetime().optional()
|
||||
last_updated: z.string().datetime().optional().nullable(),
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user