Browse Source

ux improvements

Matthew Trejo 1 month ago
parent
commit
5270112b34

+ 0 - 4
src/app/chat/select/page.tsx

@@ -73,10 +73,6 @@ export default function ChatSelectPage() {
                   Obtén información sobre síntomas, condiciones médicas y recomendaciones de salud
                 </CardDescription>
                 <ul className="mt-4 space-y-2 text-sm text-gray-600 dark:text-gray-400">
-                  <li className="flex items-center gap-2">
-                    <span className="text-blue-500">•</span>
-                    <span>Límite de 10 mensajes</span>
-                  </li>
                   <li className="flex items-center gap-2">
                     <span className="text-blue-500">•</span>
                     <span>Reporte médico al finalizar</span>

+ 1 - 0
src/components/RecordsList.tsx

@@ -133,6 +133,7 @@ export default function RecordsList() {
         isOpen={showModal}
         record={selectedRecord}
         generatingPDF={generatingPDF}
+        userRole={session?.user?.role}
         onClose={handleCloseModal}
         onCopyContent={copyToClipboard}
         onDownloadReport={downloadReport}

+ 6 - 18
src/components/Sidebar.tsx

@@ -1,7 +1,7 @@
 "use client"
 
 import { useState, useEffect, Fragment } from "react"
-import { Menu, ChevronLeft, ChevronRight } from "lucide-react"
+import { Menu } from "lucide-react"
 import { Dialog, Transition } from "@headlessui/react"
 import SidebarHeader from "@/components/sidebar/SidebarHeader"
 import SidebarUserInfo from "@/components/sidebar/SidebarUserInfo"
@@ -48,22 +48,10 @@ export default function Sidebar({ isCollapsed: externalIsCollapsed, onToggleColl
           <div 
             className="flex grow flex-col overflow-y-auto border-r border-gray-200 bg-white shadow-sm"
           >
-            {/* Botón de toggle */}
-            <div className={`flex ${isCollapsed ? 'justify-center' : 'justify-end'} p-4 border-b border-gray-200`}>
-              <button
-                onClick={toggleCollapse}
-                className="p-1.5 rounded-md hover:bg-gray-100 transition-colors"
-                title={isCollapsed ? 'Expandir sidebar' : 'Colapsar sidebar'}
-              >
-                {isCollapsed ? (
-                  <ChevronRight className="h-4 w-4 text-gray-600" />
-                ) : (
-                  <ChevronLeft className="h-4 w-4 text-gray-600" />
-                )}
-              </button>
-            </div>
-            
-            <SidebarHeader isCollapsed={isCollapsed} />
+            <SidebarHeader 
+              isCollapsed={isCollapsed} 
+              onToggleCollapse={toggleCollapse} 
+            />
             <SidebarNavigation isCollapsed={isCollapsed} />
             
             {/* User info y footer al final */}
@@ -137,4 +125,4 @@ export default function Sidebar({ isCollapsed: externalIsCollapsed, onToggleColl
       </Transition.Root>
     </>
   )
-}
+}

+ 8 - 8
src/components/dashboard/DashboardContent.tsx

@@ -39,20 +39,20 @@ export default function DashboardContent({ role }: DashboardContentProps) {
   return (
     <div className="space-y-6 p-8 pl-12 pt-6">
       <DashboardHeader role={role} roleConfig={config} />
-      
-      <DashboardStats 
-        isPatient={isPatient}
-        isDoctor={isDoctor}
-        isAdmin={isAdmin}
-        stats={stats}
-      />
 
-      <QuickActions 
+      <QuickActions
         isAdmin={isAdmin}
         isDoctor={isDoctor}
         isPatient={isPatient}
       />
 
+      <DashboardStats
+        isPatient={isPatient}
+        isDoctor={isDoctor}
+        isAdmin={isAdmin}
+        stats={stats}
+      />
+
       {(isDoctor || isAdmin) && <ActivitySummary stats={stats} />}
     </div>
   )

+ 11 - 18
src/components/dashboard/DashboardStats.tsx

@@ -26,7 +26,7 @@ interface DashboardStatsProps {
 export default function DashboardStats({ isPatient, isDoctor, isAdmin, stats }: DashboardStatsProps) {
   if (stats.loading) {
     return (
-      <div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
+      <div className="grid gap-4 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 min-w-0">
         {[1, 2, 3].map((i) => (
           <Card key={i}>
             <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
@@ -44,23 +44,15 @@ export default function DashboardStats({ isPatient, isDoctor, isAdmin, stats }:
   }
 
   return (
-    <div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
-      <Card>
-        <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
-          <CardTitle className="text-sm font-medium">
-            Consultas
-          </CardTitle>
-          <MessageSquare className="h-4 w-4 text-muted-foreground" />
-        </CardHeader>
-        <CardContent>
-          <div className="text-2xl font-bold">
-            {isPatient ? "Disponible" : stats.totalConsults}
-          </div>
-          <p className="text-xs text-muted-foreground">
-            {isPatient ? "5 consultas por sesión" : stats.consultsTrend + " desde el mes pasado"}
-          </p>
-        </CardContent>
-      </Card>
+    <div>
+      <div className="mb-4">
+        <h3 className="text-lg font-semibold">Estadísticas</h3>
+        <p className="text-sm text-muted-foreground">
+          Muestra como has usado el sistema
+        </p>
+      </div>
+
+      <div className="grid gap-4 grid-cols-1 sm:grid-cols-2 lg:grid-cols-2 min-w-0">
 
       <Card>
         <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
@@ -104,5 +96,6 @@ export default function DashboardStats({ isPatient, isDoctor, isAdmin, stats }:
         </CardContent>
       </Card>
     </div>
+    </div>
   )
 }

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

@@ -18,7 +18,7 @@ export default function QuickActions({ isAdmin, isDoctor, isPatient }: QuickActi
           Accede a las funciones principales del sistema
         </p>
       </div>
-      <div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
+      <div className="grid gap-4 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 min-w-0">
         {isAdmin ? (
           <>
             <QuickActionButton

+ 8 - 6
src/components/records/RecordsCard.tsx

@@ -59,12 +59,14 @@ export default function RecordsCard({
             </div>
             <div>
               <CardTitle className="text-xl font-bold text-foreground">
-                Reporte #{record.id.slice(-8)}
+                {userRole === "PATIENT" ? "Mi Reporte" : `Reporte #${record.id.slice(-8)}`}
               </CardTitle>
-              <div className="flex items-center text-xs text-muted-foreground mt-1">
-                <Tag className="w-3 h-3 mr-1" />
-                {record.id.slice(0, 8)}...
-              </div>
+              {userRole !== "PATIENT" && (
+                <div className="flex items-center text-xs text-muted-foreground mt-1">
+                  <Tag className="w-3 h-3 mr-1" />
+                  {record.id.slice(0, 8)}...
+                </div>
+              )}
             </div>
           </div>
           <div className="flex space-x-1">
@@ -150,4 +152,4 @@ export default function RecordsCard({
         </CardContent>
       </Card>
     )
-  }
+  }

+ 92 - 81
src/components/records/RecordsFilters.tsx

@@ -4,7 +4,7 @@ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
 import { Button } from "@/components/ui/button"
 import { Input } from "@/components/ui/input"
 import { Label } from "@/components/ui/label"
-import { Filter, ChevronDown } from "lucide-react"
+import { Filter, Search, Calendar, SlidersHorizontal } from "lucide-react"
 import PatientSearchInput from "./PatientSearchInput"
 
 interface RecordsFiltersProps {
@@ -62,105 +62,116 @@ export default function RecordsFilters({
   onShowFiltersToggle,
   onClearFilters
 }: RecordsFiltersProps) {
+  // Siempre mostrar los filtros más importantes, solo ocultar los avanzados
+  const showBasicFilters = true
+
   return (
     <Card className="mb-6 border shadow-sm rounded-xl">
       <CardHeader className="pb-4 px-6 pt-6">
-        <div className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-3">
-          <div className="flex items-center space-x-3 flex-1 min-w-0">
+        <div className="flex items-center justify-between">
+          <div className="flex items-center space-x-3">
             <div className="w-10 h-10 flex-shrink-0 bg-primary rounded-lg flex items-center justify-center shadow-sm">
               <Filter className="w-5 h-5 text-primary-foreground" />
             </div>
-            <div className="min-w-0">
+            <div>
               <CardTitle className="text-xl font-bold text-foreground">Filtros y Búsqueda</CardTitle>
-              <p className="text-sm text-muted-foreground">Personaliza tu búsqueda de reportes</p>
+              <p className="text-sm text-muted-foreground">Encuentra rápidamente lo que buscas</p>
             </div>
           </div>
-          <Button
-            variant="outline"
-            size="sm"
-            onClick={onShowFiltersToggle}
-            className="flex items-center space-x-2 flex-shrink-0"
-          >
-            <span className="font-medium">{showFilters ? "Ocultar" : "Mostrar"} filtros</span>
-            <ChevronDown className={`w-4 h-4 transition-transform duration-200 ${showFilters ? 'rotate-180' : ''}`} />
-          </Button>
+          {(searchTerm || selectedPatientId || dateFilter) && (
+            <Button
+              variant="outline"
+              size="sm"
+              onClick={onClearFilters}
+              className="text-destructive border-destructive hover:bg-destructive hover:text-destructive-foreground"
+            >
+              Limpiar filtros
+            </Button>
+          )}
         </div>
       </CardHeader>
       
-      {showFilters && (
-        <CardContent className="pt-0 px-6 pb-6">
-          <div className="grid grid-cols-1 md:grid-cols-3 gap-6">
-            {/* Búsqueda por paciente - Solo para doctores */}
-            {userRole === "DOCTOR" && (
-              <div className="bg-muted rounded-xl p-4 border">
-                <PatientSearchInput
-                  value={searchTerm}
-                  onChange={onSearchChange}
-                  selectedPatient={selectedPatient}
-                  onPatientSelect={onPatientSelect}
-                  placeholder="Nombre, email o username del paciente..."
-                />
-              </div>
-            )}
+      {/* Filtros básicos - Siempre visibles */}
+      <CardContent className="pt-0 px-6 pb-6">
+        <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
+          {/* Búsqueda principal - Siempre visible */}
+          <div className="bg-muted/50 rounded-xl p-4 border border-border/50">
+            <Label htmlFor="mainSearch" className="text-foreground font-medium flex items-center">
+              <Search className="w-4 h-4 mr-2" />
+              Búsqueda rápida
+            </Label>
+            <Input
+              id="mainSearch"
+              type="text"
+              placeholder="Buscar en contenidos, paciente..."
+              value={searchTerm}
+              onChange={(e) => onSearchChange(e.target.value)}
+              className="mt-2"
+            />
+          </div>
 
-            {/* Búsqueda por contenido del reporte */}
-            <div className="bg-muted rounded-xl p-4 border">
-              <Label htmlFor="contentSearch" className="text-foreground font-medium">Buscar en reportes</Label>
-              <Input
-                id="contentSearch"
-                type="text"
-                placeholder="Buscar por contenido, paciente..."
-                value={searchTerm}
-                onChange={(e) => onSearchChange(e.target.value)}
-                className="mt-2"
-              />
-            </div>
+          {/* Filtro por fecha - Siempre visible */}
+          <div className="bg-muted/50 rounded-xl p-4 border border-border/50">
+            <Label htmlFor="dateFilter" className="text-foreground font-medium flex items-center">
+              <Calendar className="w-4 h-4 mr-2" />
+              Fecha
+            </Label>
+            <Input
+              id="dateFilter"
+              type="date"
+              value={dateFilter}
+              onChange={(e) => onDateFilterChange(e.target.value)}
+              className="mt-2"
+            />
+          </div>
 
-            {/* Filtro por fecha */}
-            <div className="bg-muted rounded-xl p-4 border">
-              <Label htmlFor="dateFilter" className="text-foreground font-medium">Filtrar por fecha</Label>
-              <Input
-                id="dateFilter"
-                type="date"
-                value={dateFilter}
-                onChange={(e) => onDateFilterChange(e.target.value)}
-                className="mt-2"
+          {/* Búsqueda por paciente - Solo para doctores */}
+          {userRole === "DOCTOR" && (
+            <div className="bg-muted/50 rounded-xl p-4 border border-border/50">
+              <PatientSearchInput
+                value={searchTerm}
+                onChange={onSearchChange}
+                selectedPatient={selectedPatient}
+                onPatientSelect={onPatientSelect}
+                placeholder="Buscar paciente..."
               />
             </div>
+          )}
 
-            {/* Items por página */}
-            <div className="bg-muted rounded-xl p-4 border">
-              <Label htmlFor="itemsPerPage" className="text-foreground font-medium">Items por página</Label>
-              <select
-                id="itemsPerPage"
-                value={itemsPerPage}
-                onChange={(e) => onItemsPerPageChange(Number(e.target.value))}
-                className="w-full mt-2 px-3 py-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-ring focus:border-transparent"
-              >
-                {ITEMS_PER_PAGE_OPTIONS.map(option => (
-                  <option key={option} value={option}>
-                    {option} reportes
-                  </option>
-                ))}
-              </select>
-            </div>
+          {/* Items por página - Siempre visible */}
+          <div className="bg-muted/50 rounded-xl p-4 border border-border/50">
+            <Label htmlFor="itemsPerPage" className="text-foreground font-medium flex items-center">
+              <SlidersHorizontal className="w-4 h-4 mr-2" />
+              Resultados
+            </Label>
+            <select
+              id="itemsPerPage"
+              value={itemsPerPage}
+              onChange={(e) => onItemsPerPageChange(Number(e.target.value))}
+              className="w-full mt-2 px-3 py-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-ring focus:border-transparent bg-background"
+            >
+              {ITEMS_PER_PAGE_OPTIONS.map(option => (
+                <option key={option} value={option}>
+                  {option} por página
+                </option>
+              ))}
+            </select>
           </div>
+        </div>
 
-          {/* Botón limpiar filtros */}
-          {(searchTerm || selectedPatientId || dateFilter) && (
-            <div className="mt-6 flex justify-center">
-              <Button
-                variant="outline"
-                size="sm"
-                onClick={onClearFilters}
-                className="text-destructive border-destructive hover:bg-destructive hover:text-destructive-foreground px-6"
-              >
-                Limpiar todos los filtros
-              </Button>
+        {/* Indicador de filtros activos */}
+        {(searchTerm || selectedPatientId || dateFilter) && (
+          <div className="mt-4 flex items-center justify-center">
+            <div className="text-sm text-muted-foreground bg-muted/80 px-4 py-2 rounded-full border">
+              <span className="font-medium">
+                {searchTerm && `🔍 "${searchTerm}" `}
+                {selectedPatientId && `👤 Paciente seleccionado `}
+                {dateFilter && `📅 ${new Date(dateFilter).toLocaleDateString('es-ES')}`}
+              </span>
             </div>
-          )}
-        </CardContent>
-      )}
+          </div>
+        )}
+      </CardContent>
     </Card>
   )
-} 
+}

+ 15 - 11
src/components/records/RecordsModal.tsx

@@ -26,6 +26,7 @@ interface RecordsModalProps {
   isOpen: boolean;
   record: Record | null;
   generatingPDF: boolean;
+  userRole?: string;
   onClose: () => void;
   onCopyContent: (content: string) => void;
   onDownloadReport: (record: Record) => void;
@@ -38,6 +39,7 @@ function RecordsModal({
   isOpen,
   record,
   generatingPDF,
+  userRole,
   onClose,
   onCopyContent,
   onDownloadReport,
@@ -65,7 +67,7 @@ function RecordsModal({
           <div className="px-4 py-3 border-b border-border bg-card sticky top-0 z-10 flex items-center justify-between">
             <DialogTitle className="flex items-center text-lg font-semibold">
               <FileText className="w-5 h-5 mr-2 text-primary flex-shrink-0" />
-              Reporte Médico - {record.id.slice(-8)}
+              {userRole === "PATIENT" ? "Mi Reporte Médico" : `Reporte Médico - ${record.id.slice(-8)}`}
             </DialogTitle>
             <Button
               variant="ghost"
@@ -134,18 +136,20 @@ function RecordsModal({
 
               {/* Información adicional */}
               <div className="grid grid-cols-1 md:grid-cols-3 gap-4 pt-4 border-t border-border">
-                {/* ID del Reporte */}
-                <div className="flex items-center">
-                  <Tag className="w-4 h-4 mr-2 text-primary flex-shrink-0" />
-                  <div className="min-w-0">
-                    <span className="font-medium text-xs text-muted-foreground">
-                      ID del Reporte
-                    </span>
-                    <div className="font-mono text-xs break-all text-foreground">
-                      {record.id}
+                {/* ID del Reporte - Oculto para pacientes */}
+                {userRole !== "PATIENT" && (
+                  <div className="flex items-center">
+                    <Tag className="w-4 h-4 mr-2 text-primary flex-shrink-0" />
+                    <div className="min-w-0">
+                      <span className="font-medium text-xs text-muted-foreground">
+                        ID del Reporte
+                      </span>
+                      <div className="font-mono text-xs break-all text-foreground">
+                        {record.id}
+                      </div>
                     </div>
                   </div>
-                </div>
+                )}
 
                 {/* Fecha de creación */}
                 <div className="flex items-center">

+ 35 - 5
src/components/sidebar/SidebarHeader.tsx

@@ -1,13 +1,39 @@
 "use client"
 
 import Link from "next/link"
-import { Stethoscope, ChevronLeft } from "lucide-react"
+import { Stethoscope, ChevronLeft, ChevronRight } from "lucide-react"
 
-export default function SidebarHeader({ isCollapsed, onClose }: { isCollapsed: boolean, onClose?: () => void }) {
+export default function SidebarHeader({ 
+  isCollapsed, 
+  onClose, 
+  onToggleCollapse 
+}: { 
+  isCollapsed: boolean, 
+  onClose?: () => void,
+  onToggleCollapse?: () => void 
+}) {
   return (
     <div 
       className={`${isCollapsed ? 'p-4 flex justify-center' : 'p-6'} border-b border-gray-200 bg-white relative`}
     >
+      {/* Botón de colapsar (solo en desktop) */}
+      {onToggleCollapse && (
+        <button
+          onClick={onToggleCollapse}
+          className={`absolute top-4 p-1.5 rounded-md hover:bg-gray-100 transition-colors ${
+            isCollapsed ? 'left-1/2 -translate-x-1/2' : 'right-4'
+          }`}
+          title={isCollapsed ? 'Expandir sidebar' : 'Colapsar sidebar'}
+        >
+          {isCollapsed ? (
+            <ChevronRight className="h-4 w-4 text-gray-600" />
+          ) : (
+            <ChevronLeft className="h-4 w-4 text-gray-600" />
+          )}
+        </button>
+      )}
+      
+      {/* Botón de cerrar (solo en móvil) */}
       {onClose && (
         <button
           onClick={onClose}
@@ -17,9 +43,13 @@ export default function SidebarHeader({ isCollapsed, onClose }: { isCollapsed: b
           <ChevronLeft className="h-5 w-5 text-gray-600" />
         </button>
       )}
-      <Link href="/dashboard" className={`flex items-center space-x-3 ${onClose ? 'pr-12' : ''}`}>
+      
+      <Link 
+        href="/dashboard" 
+        className={`flex items-center ${isCollapsed ? 'justify-center' : 'space-x-3'} ${onClose ? 'pr-12' : ''} ${onToggleCollapse && !isCollapsed ? 'pr-12' : ''}`}
+      >
         <div 
-          className="w-10 h-10 rounded-xl flex items-center justify-center bg-primary"
+          className="w-10 h-10 rounded-xl flex items-center justify-center bg-primary flex-shrink-0"
         >
           <Stethoscope className="h-6 w-6 text-primary-foreground" />
         </div>
@@ -32,4 +62,4 @@ export default function SidebarHeader({ isCollapsed, onClose }: { isCollapsed: b
       </Link>
     </div>
   )
-}
+}

+ 1 - 1
src/hooks/useChat.ts

@@ -3,7 +3,7 @@ import { notifications } from "@/lib/notifications";
 import { generateReportFromMessages } from "@/utils/reports";
 import { Message, ChatState, ChatResponse, SuggestedPrompt, MedicalAlert } from "@/components/chatbot/types";
 
-export const MAX_MESSAGES = 5;
+export const MAX_MESSAGES = 25;
 
 interface UseChatProps {
   chatType: "medical" | "psychological";

+ 2 - 2
src/hooks/useRecords.ts

@@ -32,7 +32,7 @@ export function useRecords() {
   } | null>(null)
   const [dateFilter, setDateFilter] = useState("")
   const [itemsPerPage, setItemsPerPage] = useState(3)
-  const [showFilters, setShowFilters] = useState(false)
+  const [showFilters, setShowFilters] = useState(true)
   const [showLoadingSkeleton, setShowLoadingSkeleton] = useState(false)
 
   const fetchRecords = useCallback(async () => {
@@ -274,4 +274,4 @@ export function useRecords() {
     fetchRecords,
     handlePatientSelect
   }
-}
+}