From 6a2a4c0f05e2acf1e3156532a446b5bf5fd0ec58 Mon Sep 17 00:00:00 2001 From: Guillermo Arrieta Medina Date: Wed, 31 Dec 2025 14:32:55 -0600 Subject: [PATCH] Responsividad correcta para el wizard --- src/routes/planes/_lista/nuevo.tsx | 328 +++++++++++++++++++---------- 1 file changed, 216 insertions(+), 112 deletions(-) diff --git a/src/routes/planes/_lista/nuevo.tsx b/src/routes/planes/_lista/nuevo.tsx index f164999..34e386c 100644 --- a/src/routes/planes/_lista/nuevo.tsx +++ b/src/routes/planes/_lista/nuevo.tsx @@ -2,6 +2,7 @@ import { createFileRoute, useNavigate } from '@tanstack/react-router' import * as Icons from 'lucide-react' import { useMemo, useState } from 'react' +import { CircularProgress } from '@/components/CircularProgress' import { defineStepper } from '@/components/stepper' import { Button } from '@/components/ui/button' import { @@ -19,6 +20,12 @@ import { } from '@/components/ui/dialog' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from '@/components/ui/tooltip' export const Route = createFileRoute('/planes/_lista/nuevo')({ component: NuevoPlanModal, @@ -251,7 +258,12 @@ function NuevoPlanModal() { 1. h-[90vh]: Altura fija del 90% de la ventana. 2. flex-col gap-0 p-0: Quitamos espacios automáticos para control manual. */} - + { + e.preventDefault() + }} + > {role !== 'JEFE_CARRERA' ? ( // --- VISTA SIN PERMISOS --- <> @@ -283,126 +295,191 @@ function NuevoPlanModal() { initialStep={Wizard.utils.getFirst().id} className="flex h-full flex-col" > - {({ methods }) => ( - <> - {/* 1. HEADER (FIJO - Top Bun) - flex-none: Asegura que no se aplaste ni haga scroll. - */} -
- - Nuevo plan de estudios - + {({ methods }) => { + // --- LÓGICA PARA MÓVIL (Cálculos) --- + const currentIndex = Wizard.utils.getIndex(methods.current.id) + 1 + const totalSteps = Wizard.steps.length + const nextStep = Wizard.steps[currentIndex] + return ( + <> + {/* ========================================================= + HEADER RESPONSIVO + ========================================================= */} +
+
+ + {' '} + {/* Quitamos padding interno porque ya lo tiene el div padre */} + + Nuevo plan de estudios + + - {/* Navegación dentro del bloque fijo */} -
- - {/* Nota: En móvil esto se verá apretado. Considera overflow-x-auto si tienes muchos pasos */} + {/* BOTÓN DE CERRAR (X) */} + +
- {Wizard.steps.map((step) => ( - - - {step.title} - - - ))} - +
+ {/* OPCIÓN A: MÓVIL (< 640px) + - Usa block sm:hidden + - Muestra el CircularProgress y Texto Grande + */} +
+
+ +
+

+ {/* Usamos el Helper del Tooltip aquí también */} + +

+ {nextStep ? ( +

+ Siguiente: {nextStep.title} +

+ ) : ( +

+ ¡Último paso! +

+ )} +
+
+
+ + {/* OPCIÓN B: DESKTOP (>= 640px) + - Usa hidden sm:block + - Muestra la navegación original horizontal + */} +
+ + {Wizard.steps.map((step) => ( + + + {/* Envolvemos el título en nuestro componente Tooltip */} + + + + ))} + +
+
-
- {/* 2. CONTENIDO (SCROLLEABLE - Meat) + {/* 2. CONTENIDO (SCROLLEABLE - Meat) flex-1: Ocupa todo el espacio restante. overflow-y-auto: Scrollea solo esta parte. - */} -
- {/* Aquí renderizamos el panel. Quitamos el 'grid' del padre para evitar conflictos de altura */} -
- {Wizard.utils.getIndex(methods.current.id) === 0 && ( - - - - )} - {Wizard.utils.getIndex(methods.current.id) === 1 && ( - - - - )} - {Wizard.utils.getIndex(methods.current.id) === 2 && ( - - - - )} - {Wizard.utils.getIndex(methods.current.id) === 3 && ( - - - - )} + */} +
+ {/* Aquí renderizamos el panel. Quitamos el 'grid' del padre para evitar conflictos de altura */} +
+ {Wizard.utils.getIndex(methods.current.id) === 0 && ( + + + + )} + {Wizard.utils.getIndex(methods.current.id) === 1 && ( + + + + )} + {Wizard.utils.getIndex(methods.current.id) === 2 && ( + + + + )} + {Wizard.utils.getIndex(methods.current.id) === 3 && ( + + + + )} +
-
- {/* 3. CONTROLES (FIJO - Bottom Bun) + {/* 3. CONTROLES (FIJO - Bottom Bun) flex-none: Siempre visible abajo. */} -
- -
- {wizard.errorMessage && ( - - {wizard.errorMessage} - - )} -
+
+ +
+ {wizard.errorMessage && ( + + {wizard.errorMessage} + + )} +
-
- - - {Wizard.utils.getIndex(methods.current.id) < - Wizard.steps.length - 1 ? ( +
- ) : ( - - )} -
- -
- - )} + + {Wizard.utils.getIndex(methods.current.id) < + Wizard.steps.length - 1 ? ( + + ) : ( + + )} +
+
+
+ + ) + }} )} @@ -410,6 +487,33 @@ function NuevoPlanModal() { ) } +function StepWithTooltip({ title, desc }: { title: string; desc: string }) { + const [isOpen, setIsOpen] = useState(false) + + return ( + + + + { + e.stopPropagation() // Evita que el clic dispare la navegación del stepper si está dentro de un botón + setIsOpen((prev) => !prev) + }} + onMouseEnter={() => setIsOpen(true)} + onMouseLeave={() => setIsOpen(false)} + > + {title} + + + +

{desc}

+
+
+
+ ) +} + // Paso 1: selección de modo function PasoModo({ wizard, @@ -477,7 +581,7 @@ function PasoModo({ Desde un plan existente o archivos. {wizard.modoCreacion === 'CLONADO' && ( - +