Przeglądaj źródła

[hack] revert revert streaming changes

Matthew Trejo 2 miesięcy temu
rodzic
commit
06d81279d4

+ 1 - 1
src/app/api/appointments/[id]/start-meeting/route.ts

@@ -83,7 +83,7 @@ export async function POST(
 
     if (now > oneHourAfter) {
       return NextResponse.json(
-        { error: "La cita ya finalizó. El tiempo límite para unirse ha expirado." },
+        { error: "La cita ya finalizó" },
         { status: 400 }
       );
     }

+ 7 - 4
src/app/api/chat/route.ts

@@ -116,9 +116,9 @@ export async function POST(request: NextRequest) {
           },
           ...conversationHistory,
         ],
-        max_tokens: 3000,
+        max_tokens: 8192, // fixme
         temperature: 0.7,
-        stream: true, // 🔥 STREAMING HABILITADO
+        stream: true,
       });
 
       console.log("📡 [API] Stream iniciado, comenzando a procesar chunks...");
@@ -137,8 +137,11 @@ export async function POST(request: NextRequest) {
                 fullResponse += content;
                 
                 // Intentar extraer solo el contenido del campo "response" del JSON en tiempo real
-                // Buscar el patrón: "response": "TEXTO AQUI"
-                const responseMatch = fullResponse.match(/"response"\s*:\s*"([\s\S]*?)(?:"|$)/);
+                // Regex mejorada que:
+                // 1. Funciona durante el streaming (sin requerir comilla de cierre)
+                // 2. Maneja correctamente comillas escapadas \"
+                // 3. Captura: caracteres normales [^"\\] O secuencias escapadas \\.
+                const responseMatch = fullResponse.match(/"response"\s*:\s*"((?:[^"\\]|\\.)*)/);
                 
                 if (responseMatch) {
                   // Decodificar secuencias de escape JSON

+ 66 - 2
src/app/appointments/[id]/meet/page.tsx

@@ -13,11 +13,12 @@ import {
   DialogHeader,
   DialogTitle,
 } from "@/components/ui/dialog";
-import { Loader2, Video, AlertTriangle, FileText } from "lucide-react";
+import { Loader2, Video, AlertTriangle, FileText, Clock, XCircle } from "lucide-react";
 import { ConsultationNotes } from "@/components/appointments/ConsultationNotes";
 import RecordsModal from "@/components/records/RecordsModal";
 import type { Record as MedicalRecord } from "@/components/records/types";
 import type { Appointment } from "@/types/appointments";
+import { canJoinMeeting, getAppointmentTimeStatus } from "@/utils/appointments";
 
 interface JitsiMeetExternalAPI {
   dispose: () => void;
@@ -41,6 +42,9 @@ export default function MeetPage() {
   const [showExitDialog, setShowExitDialog] = useState(false);
   const [showRecordsModal, setShowRecordsModal] = useState(false);
   const [appointment, setAppointment] = useState<Appointment | null>(null);
+  const [loading, setLoading] = useState(true);
+  const [accessDenied, setAccessDenied] = useState(false);
+  const [denialReason, setDenialReason] = useState("");
 
   // Cargar información del appointment
   useEffect(() => {
@@ -50,9 +54,29 @@ export default function MeetPage() {
         if (response.ok) {
           const data = await response.json();
           setAppointment(data);
+          
+          // Validar acceso por tiempo
+          const timeCheck = canJoinMeeting(data.fechaSolicitada);
+          if (!timeCheck.canJoin) {
+            setAccessDenied(true);
+            setDenialReason(timeCheck.reason || "No puedes acceder a esta videollamada");
+          }
+          
+          // Validar que la cita esté aprobada
+          if (data.estado !== "APROBADA" && data.estado !== "COMPLETADA") {
+            setAccessDenied(true);
+            setDenialReason("Esta cita no está aprobada");
+          }
+        } else {
+          setAccessDenied(true);
+          setDenialReason("No se pudo cargar la información de la cita");
         }
       } catch (error) {
         console.error("Error loading appointment:", error);
+        setAccessDenied(true);
+        setDenialReason("Error al cargar la cita");
+      } finally {
+        setLoading(false);
       }
     };
 
@@ -211,7 +235,7 @@ export default function MeetPage() {
     };
   }, [status, session, initJitsi]);
 
-  if (status === "loading") {
+  if (status === "loading" || loading) {
     return (
       <div className="flex items-center justify-center min-h-screen">
         <Loader2 className="h-8 w-8 animate-spin" />
@@ -223,6 +247,46 @@ export default function MeetPage() {
     redirect("/auth/login");
   }
 
+  // Mostrar pantalla de acceso denegado si no cumple las condiciones
+  if (accessDenied) {
+    return (
+      <div className="container mx-auto px-4 py-8 max-w-2xl">
+        <Card>
+          <CardHeader>
+            <div className="flex items-center gap-2 text-destructive">
+              <XCircle className="h-6 w-6" />
+              <CardTitle>Acceso no permitido</CardTitle>
+            </div>
+          </CardHeader>
+          <CardContent className="space-y-4">
+            <p className="text-muted-foreground">{denialReason}</p>
+            
+            {appointment?.fechaSolicitada && (
+              <div className="bg-muted p-4 rounded-lg">
+                <div className="flex items-center gap-2 mb-2">
+                  <Clock className="h-5 w-5" />
+                  <p className="font-medium">Estado de la cita</p>
+                </div>
+                <p className="text-sm text-muted-foreground">
+                  {getAppointmentTimeStatus(appointment.fechaSolicitada)}
+                </p>
+              </div>
+            )}
+            
+            <div className="flex gap-2">
+              <Button onClick={() => router.push(`/appointments/${params.id}`)}>
+                Ver detalles de la cita
+              </Button>
+              <Button variant="outline" onClick={() => router.push("/appointments")}>
+                Volver a mis citas
+              </Button>
+            </div>
+          </CardContent>
+        </Card>
+      </div>
+    );
+  }
+
   const isDoctor = session.user.role === "DOCTOR";
   const appointmentId = params.id as string;
 

+ 4 - 2
src/app/appointments/[id]/page.tsx

@@ -389,8 +389,10 @@ export default function AppointmentDetailPage({ params }: PageProps) {
               </>
             )}
 
-            {/* Solo mostrar sala si NO está completada */}
-            {appointment.roomName && appointment.estado !== "COMPLETADA" && (
+            {/* Solo mostrar sala si NO está completada Y el tiempo aún es válido */}
+            {appointment.roomName && 
+             appointment.estado !== "COMPLETADA" && 
+             canJoinMeeting(appointment.fechaSolicitada).canJoin && (
               <>
                 <Separator />
                 <div className="bg-primary/10 p-4 rounded-lg">

+ 3 - 3
src/components/dashboard/QuickActions.tsx

@@ -53,7 +53,7 @@ export default function QuickActions({ isAdmin, isDoctor, isPatient }: QuickActi
               title="Mis Pacientes"
               description="Revisa y gestiona tus pacientes asignados"
               href="/patients"
-              iconBg="bg-blue-100 dark:bg-blue-900/20"
+              iconBg="bg-blue-100 dark:bg-blue-500/20"
               iconColor="text-blue-600 dark:text-blue-400"
             />
             <QuickActionButton
@@ -61,7 +61,7 @@ export default function QuickActions({ isAdmin, isDoctor, isPatient }: QuickActi
               title="Reportes Médicos"
               description="Historial de consultas y diagnósticos"
               href="/records"
-              iconBg="bg-emerald-100 dark:bg-emerald-900/20"
+              iconBg="bg-emerald-100 dark:bg-emerald-500/20"
               iconColor="text-emerald-600 dark:text-emerald-400"
             />
             <QuickActionButton
@@ -69,7 +69,7 @@ export default function QuickActions({ isAdmin, isDoctor, isPatient }: QuickActi
               title="Agenda de Citas"
               description="Visualiza tu calendario de consultas"
               href="/appointments/doctor"
-              iconBg="bg-amber-100 dark:bg-amber-900/20"
+              iconBg="bg-amber-100 dark:bg-amber-500/20"
               iconColor="text-amber-600 dark:text-amber-400"
             />
           </>

+ 1 - 1
src/utils/appointments.ts

@@ -35,7 +35,7 @@ export function canJoinMeeting(appointmentDate: Date | string | null): {
   if (now > oneHourAfter) {
     return {
       canJoin: false,
-      reason: "La cita ya finalizó. El tiempo límite para unirse ha expirado.",
+      reason: "La cita ya finalizó",
     };
   }