diff --git a/frontend/src/components/FormularioConfiguracion.tsx b/frontend/src/components/FormularioConfiguracion.tsx
index 92022fa..fc8bcd3 100644
--- a/frontend/src/components/FormularioConfiguracion.tsx
+++ b/frontend/src/components/FormularioConfiguracion.tsx
@@ -1,7 +1,8 @@
-import { Box, TextField, Button, Paper, CircularProgress, Typography } from '@mui/material';
+import { Box, TextField, Paper, CircularProgress, Chip } from '@mui/material';
import type { Configuracion } from '../types';
import * as api from '../services/apiService';
-import { useState } from 'react';
+import { useState, useEffect, useRef } from 'react';
+import { useDebounce } from '../hooks/useDebounce';
interface Props {
config: Configuracion | null;
@@ -9,61 +10,79 @@ interface Props {
}
const FormularioConfiguracion = ({ config, setConfig }: Props) => {
- const [saving, setSaving] = useState(false);
- const [success, setSuccess] = useState(false);
+ const [saveStatus, setSaveStatus] = useState<'idle' | 'saving' | 'saved'>('idle');
+ const debouncedConfig = useDebounce(config, 750);
+
+ const isInitialLoad = useRef(true);
+
+ useEffect(() => {
+ // Solo procedemos si tenemos un objeto de configuración "debounced"
+ if (debouncedConfig) {
+ // Si la 'ref' es true, significa que esta es la primera vez que recibimos
+ // un objeto de configuración válido. Lo ignoramos y marcamos la carga inicial como completada.
+ if (isInitialLoad.current) {
+ isInitialLoad.current = false;
+ return;
+ }
+
+ // Si la 'ref' ya es false, significa que cualquier cambio posterior
+ // es una modificación real del usuario, por lo que procedemos a guardar.
+ const saveConfig = async () => {
+ setSaveStatus('saving');
+ try {
+ await api.guardarConfiguracion(debouncedConfig);
+ setSaveStatus('saved');
+ setTimeout(() => {
+ setSaveStatus('idle');
+ }, 2000);
+ } catch (err) {
+ console.error("Error en el auto-guardado:", err);
+ setSaveStatus('idle');
+ }
+ };
+
+ saveConfig();
+ }
+ }, [debouncedConfig]); // La dependencia sigue siendo la misma
+
if (!config) {
return ;
}
const handleChange = (event: React.ChangeEvent) => {
+ setSaveStatus('idle');
const { name, value } = event.target;
const numericFields = ['intervaloMinutos', 'cantidadTitularesAScrapear'];
-
setConfig(prevConfig => prevConfig ? {
...prevConfig,
[name]: numericFields.includes(name) ? Number(value) : value
} : null);
};
-
- const handleSubmit = async (event: React.FormEvent) => {
- event.preventDefault();
- if (!config) return;
- setSaving(true);
- setSuccess(false);
- try {
- await api.guardarConfiguracion(config);
- setSuccess(true);
- setTimeout(() => setSuccess(false), 2000);
- } catch (err) {
- console.error("Error al guardar configuración", err);
- } finally {
- setSaving(false);
+
+ const getSaveStatusIndicator = () => {
+ if (saveStatus === 'saving') {
+ return } />;
}
+ if (saveStatus === 'saved') {
+ return ;
+ }
+ return null;
};
return (
-
-
-
-
+
+
+
+
-
- {success && ¡Guardado!}
-
+
+ {getSaveStatusIndicator()}
diff --git a/frontend/src/hooks/useDebounce.ts b/frontend/src/hooks/useDebounce.ts
new file mode 100644
index 0000000..5f7f831
--- /dev/null
+++ b/frontend/src/hooks/useDebounce.ts
@@ -0,0 +1,27 @@
+// frontend/src/hooks/useDebounce.ts
+
+import { useState, useEffect } from 'react';
+
+// Este hook toma un valor y un retardo (delay) en milisegundos.
+// Devuelve una nueva versión del valor que solo se actualiza
+// después de que el valor original no haya cambiado durante el 'delay' especificado.
+export function useDebounce(value: T, delay: number): T {
+ const [debouncedValue, setDebouncedValue] = useState(value);
+
+ useEffect(() => {
+ // Configura un temporizador para actualizar el valor "debounced"
+ // después de que pase el tiempo de 'delay'.
+ const handler = setTimeout(() => {
+ setDebouncedValue(value);
+ }, delay);
+
+ // Función de limpieza: Si el 'value' cambia (porque el usuario sigue escribiendo),
+ // este return se ejecuta primero, limpiando el temporizador anterior.
+ // Esto previene que el valor se actualice mientras el usuario sigue interactuando.
+ return () => {
+ clearTimeout(handler);
+ };
+ }, [value, delay]); // El efecto se vuelve a ejecutar solo si el valor o el retardo cambian.
+
+ return debouncedValue;
+}
\ No newline at end of file