From a6a94fa42b310204920a70d87edfd8df591d1d7f Mon Sep 17 00:00:00 2001 From: "Roberto.silva" Date: Thu, 5 Feb 2026 14:09:55 -0600 Subject: [PATCH] =?UTF-8?q?WIP:=20Guardado=20autom=C3=A1tico=20fix=20#53?= =?UTF-8?q?=20fix=20#68?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/routes/planes/$planId/_detalle.tsx | 138 ++++++++++++------------- 1 file changed, 68 insertions(+), 70 deletions(-) diff --git a/src/routes/planes/$planId/_detalle.tsx b/src/routes/planes/$planId/_detalle.tsx index 94f1c29..2f6b37c 100644 --- a/src/routes/planes/$planId/_detalle.tsx +++ b/src/routes/planes/$planId/_detalle.tsx @@ -5,12 +5,10 @@ import { Clock, Hash, CalendarDays, - Save, } from 'lucide-react' import { useState, useEffect, forwardRef } from 'react' import { Badge } from '@/components/ui/badge' -import { Button } from '@/components/ui/button' import { DropdownMenu, DropdownMenuContent, @@ -20,7 +18,7 @@ import { import { NotFoundPage } from '@/components/ui/NotFoundPage' import { Skeleton } from '@/components/ui/skeleton' import { plans_get } from '@/data/api/plans.api' -import { usePlan } from '@/data/hooks/usePlans' +import { usePlan, useUpdatePlanFields } from '@/data/hooks/usePlans' import { qk } from '@/data/query/keys' export const Route = createFileRoute('/planes/$planId/_detalle')({ @@ -56,6 +54,7 @@ export const Route = createFileRoute('/planes/$planId/_detalle')({ function RouteComponent() { const { planId } = Route.useParams() const { data, isLoading } = usePlan(planId) + const { mutate } = useUpdatePlanFields() // Estados locales para manejar la edición "en vivo" antes de persistir const [nombrePlan, setNombrePlan] = useState('') @@ -77,32 +76,37 @@ function RouteComponent() { 'Especialidad', ] - const handleKeyDown = (e: React.KeyboardEvent) => { + const persistChange = (patch: any) => { + mutate({ planId, patch }) + } + + const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key === 'Enter') { - e.preventDefault() // Evita el salto de línea - e.currentTarget.blur() // Quita el foco, lo que dispara el onBlur y "guarda" en el estado + e.preventDefault() + e.currentTarget.blur() // Esto disparará el onBlur automáticamente } } - const handleSave = () => { - console.log('Guardando en DB...', { nombrePlan, nivelPlan }) - // Aquí iría tu mutation - setIsDirty(false) + const handleBlurNombre = (e: React.FocusEvent) => { + const nuevoNombre = e.currentTarget.textContent || '' + setNombrePlan(nuevoNombre) + + // Solo guardamos si el valor es realmente distinto al de la base de datos + if (nuevoNombre !== data?.nombre) { + persistChange({ nombre: nuevoNombre }) + } + } + + const handleSelectNivel = (n: string) => { + setNivelPlan(n) + // Guardamos inmediatamente al seleccionar + if (n !== data?.nivel) { + persistChange({ nivel: n }) + } } return (
- {/* Botón Flotante de Guardar */} - {isDirty && ( -
- -
- )} {/* 1. Header Superior */}
@@ -116,62 +120,54 @@ function RouteComponent() {
- {/* Header del Plan */} + {/* 2. Header del Plan */} {isLoading ? ( /* ===== SKELETON ===== */ -
-
- {Array.from({ length: 6 }).map((_, i) => ( - - ))} -
+
+ {Array.from({ length: 6 }).map((_, i) => ( + + ))}
) : ( - <> -
-
-

- {nivelPlan} en - - setNombrePlan(e.currentTarget.textContent || '') +
+
+

+ {nivelPlan} en + { + const nuevoNombre = e.currentTarget.textContent || '' + setNombrePlan(nuevoNombre) + if (nuevoNombre !== data?.nombre) { + mutate({ planId, patch: { nombre: nuevoNombre } }) } - className="cursor-text border-b border-transparent decoration-transparent transition-colors outline-none select-text hover:border-slate-300 focus:border-teal-500" - style={{ - WebkitTextDecoration: 'none', - textDecoration: 'none', - }} // Doble seguridad contra subrayados - > - {nombrePlan} - -

-

- {data?.carreras?.facultades?.nombre}{' '} - {data?.carreras?.nombre_corto} -

-
- -
- {/* - {data?.estados_plan?.etiqueta} - */} - - {data?.estados_plan?.etiqueta} - -
+ {nombrePlan} + +

+

+ {data?.carreras?.facultades?.nombre}{' '} + {data?.carreras?.nombre_corto} +

- + +
+ + {data?.estados_plan?.etiqueta} + +
+
)} - {/* 3. Cards de Información con Context Menu */} + {/* 3. Cards de Información */}
@@ -189,7 +185,9 @@ function RouteComponent() { key={n} onClick={() => { setNivelPlan(n) - setIsDirty(true) + if (n !== data?.nivel) { + mutate({ planId, patch: { nivel: n } }) + } }} > {n} @@ -211,7 +209,7 @@ function RouteComponent() { } label="Creación" - value={data?.creado_en?.split('T')[0]} // Cortamos la fecha para que no sea tan larga + value={data?.creado_en?.split('T')[0]} />