Appointment en schema.prismaPOST /api/appointments - Crear cita (paciente)GET /api/appointments - Listar citas (filtro por rol)GET /api/appointments/[id] - Detalle de citaPATCH /api/appointments/[id]/approve - Aprobar (médico) - NO genera roomNamePOST /api/appointments/[id]/start-meeting - Iniciar videollamada (genera roomName)PATCH /api/appointments/[id]/reject - Rechazar (médico)PATCH /api/appointments/[id]/complete - Marcar completadaDELETE /api/appointments/[id] - Cancelar (paciente)AppointmentCard - Card para mostrar citaAppointmentForm - Formulario nueva citaAppointmentsList - Lista de citasAppointmentStatusBadge - Badge de estadoJitsiMeetRoom - Componente para videollamadaAppointmentActions - Botones aprobar/rechazar/appointments - Vista paciente (crear/ver citas)/appointments/doctor - Vista médico (aprobar citas)/appointments/[id] - Detalle de cita/appointments/[id]/meet - Sala Jitsiprisma/
schema.prisma (+ modelo Appointment)
src/app/api/appointments/
route.ts (GET, POST)
[id]/route.ts (GET, PATCH, DELETE)
[id]/approve/route.ts
[id]/reject/route.ts
src/app/appointments/
page.tsx (paciente)
doctor/page.tsx (médico)
[id]/page.tsx (detalle)
[id]/meet/page.tsx (sala Jitsi)
src/components/appointments/
AppointmentCard.tsx
AppointmentForm.tsx
AppointmentsList.tsx
AppointmentStatusBadge.tsx
AppointmentActions.tsx
JitsiMeetRoom.tsx
src/hooks/
useAppointments.ts
model Appointment {
id String @id @default(cuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relaciones
pacienteId String
paciente User @relation("PatientAppointments", fields: [pacienteId], references: [id])
medicoId String?
medico User? @relation("DoctorAppointments", fields: [medicoId], references: [id])
chatReportId String? @unique
chatReport ChatReport? @relation(fields: [chatReportId], references: [id])
// Info de la cita
fechaSolicitada DateTime
estado String // PENDIENTE, APROBADA, RECHAZADA, COMPLETADA, CANCELADA
motivoConsulta String
motivoRechazo String?
// Jitsi
roomName String? @unique
@@index([pacienteId])
@@index([medicoId])
@@index([estado])
}
FASE 1 - MVP (3-4h)
FASE 2 - Mejoras (2-3h)
FASE 3 - Pulido (1-2h)
const domain = 'meet.jit.si';
const options = {
roomName: `appointment-${appointmentId}-${Date.now()}`,
width: '100%',
height: 700,
parentNode: document.querySelector('#jitsi-container'),
configOverwrite: {
startWithAudioMuted: true,
startWithVideoMuted: false,
},
interfaceConfigOverwrite: {
TOOLBAR_BUTTONS: [
'microphone', 'camera', 'closedcaptions', 'desktop',
'fullscreen', 'fodeviceselection', 'hangup',
'chat', 'recording', 'etherpad', 'settings', 'videoquality',
],
},
userInfo: {
displayName: userName,
}
};
appointment-{id}-{timestamp} (generado al iniciar videollamada)roomName NO se genera al aprobar la cita, solo cuando un usuario intenta unirse en el horario válido (15 min antes hasta 1 hora después). Esto evita salas creadas prematuramente.