diff --git a/.doc/MemoriaDeEvolucion.md b/.doc/MemoriaDeEvolucion.md index 0b376b1..1628ef1 100644 --- a/.doc/MemoriaDeEvolucion.md +++ b/.doc/MemoriaDeEvolucion.md @@ -78,3 +78,81 @@ El usuario solicitó cambiar la presentación de los datasets de un formato de f - Debugging mejorado con logs de consola - Mantiene integridad completa con schemas JSON existentes - Patrón reutilizable para futuras entidades + +### Integración con Layouts UI Schema (10/08/2025) + +**Solicitud del Usuario**: Que las tablas usen los layouts definidos en los schemas UI. + +**Implementación**: + +**1. FormTable.jsx mejorado** +- Logs de debugging para verificar schema y uiSchema recibidos +- showErrorList={false} para formularios más limpios +- Preservación completa del uiSchema.additionalProperties + +**2. Managers actualizados** +- Logs de debugging en DatasetFormManager y PlotFormManager +- Acceso correcto a schemas completos sin modificar +- Verificación de ui:layout en variables: area(3), db(2), offset(3), bit(2), type(2) en primera fila +- streaming(12) en segunda fila como define el schema UI + +**3. LayoutObjectFieldTemplate** +- Ya soporta ui:layout con SimpleGrid columns={12} +- Interpreta width de cada campo correctamente +- Maneja múltiples filas de layout + +**Resultado esperado**: Los formularios ahora deben mostrar los campos con los layouts exactos definidos en los archivos uischema.json, especialmente las variables de dataset con su layout específico de 2 filas. + +### Componentes Completos con Layout UI Respetado (10/08/2025) + +**Problema identificado**: Usuario reportó que el layout definido en dataset-definitions.uischema.json no se respetaba. + +**Causa**: Solo mostrábamos datasets individuales, pero no el schema completo con active_datasets, current_dataset_id, etc. + +**Solución final**: + +**1. DatasetCompleteManager.jsx** +- Formulario completo de dataset-definitions con schema y uiSchema completos +- Respeta ui:layout definido: active_datasets(3), current_dataset_id(3), last_update(3), version(4) +- Tabla de datasets individuales con FormTable +- Tabla de variables con layout específico: area(3), db(2), offset(3), bit(2), type(2) + streaming(12) + +**2. PlotCompleteManager.jsx** +- Formulario completo de plot-definitions con schema completo +- Tabla de plots individuales como tarjetas de formulario +- Gestión de variables de plot como array de strings + +**3. Dashboard.jsx actualizado** +- Usa DatasetCompleteManager y PlotCompleteManager +- Mantiene funcionalidad híbrida: formulario completo + tablas editables +- Respeta completamente los layouts UI definidos en schemas + +**Resultado**: Ahora se muestran tanto los formularios completos con layouts UI como las tablas editables solicitadas por el usuario. + +### Simplificación: Eliminación de Campos Estáticos (10/08/2025) + +**Solicitud del Usuario**: Eliminar área innecesaria de datos estáticos como cabecera de datasets. No necesita: active_datasets, current_dataset_id, last_update, version. + +**Problema**: UI mostraba formularios completos con campos de configuración global que el usuario no necesita editar. + +**Solución implementada**: + +**1. DatasetCompleteManager simplificado** +- ❌ Removido formulario completo con campos estáticos +- ✅ Solo tabla de datasets individuales con FormTable +- ✅ Tabla de variables del dataset con layout UI específico +- ✅ saveDatasets() mantiene campos necesarios para backend automáticamente + +**2. PlotCompleteManager simplificado** +- ❌ Removido formulario completo con configuración global +- ✅ Solo tabla de plots individuales +- ✅ Gestión de variables de plot +- ✅ savePlots() preserva session_counter, version automáticamente + +**3. Arquitectura híbrida inteligente** +- 👁️ UI: Solo muestra lo que el usuario necesita editar +- 🔧 Backend: Preserva automáticamente campos técnicos necesarios +- 💾 Datos: active_datasets se calcula automáticamente desde datasets.enabled +- 🔄 current_dataset_id se mantiene basado en selección del usuario + +**Resultado final**: Interfaz limpia enfocada solo en datasets y plots individuales, sin campos estáticos innecesarios. diff --git a/application_events.json b/application_events.json index e1f2bb9..f65d8d2 100644 --- a/application_events.json +++ b/application_events.json @@ -9385,8 +9385,29 @@ "event_type": "application_started", "message": "Application initialization completed successfully", "details": {} + }, + { + "timestamp": "2025-08-12T20:55:02.435268", + "level": "info", + "event_type": "application_started", + "message": "Application initialization completed successfully", + "details": {} + }, + { + "timestamp": "2025-08-12T20:56:20.348940", + "level": "info", + "event_type": "application_started", + "message": "Application initialization completed successfully", + "details": {} + }, + { + "timestamp": "2025-08-12T21:01:03.404131", + "level": "info", + "event_type": "application_started", + "message": "Application initialization completed successfully", + "details": {} } ], - "last_updated": "2025-08-12T20:49:00.212504", - "total_entries": 880 + "last_updated": "2025-08-12T21:01:03.404131", + "total_entries": 883 } \ No newline at end of file diff --git a/config/schema/ui/dataset-definitions.uischema.json b/config/schema/ui/dataset-definitions.uischema.json index b5819ee..0653f0f 100644 --- a/config/schema/ui/dataset-definitions.uischema.json +++ b/config/schema/ui/dataset-definitions.uischema.json @@ -86,7 +86,7 @@ }, { "name": "version", - "width": 3 + "width": 4 } ] ] diff --git a/frontend/src/components/DatasetCompleteManager.jsx b/frontend/src/components/DatasetCompleteManager.jsx new file mode 100644 index 0000000..c58886a --- /dev/null +++ b/frontend/src/components/DatasetCompleteManager.jsx @@ -0,0 +1,255 @@ +import React, { useState, useEffect, useMemo } from 'react' +import { + Box, + VStack, + HStack, + Text, + Select, + Card, + CardBody, + CardHeader, + Heading, + Alert, + AlertIcon, + useColorModeValue, + Divider, + Button +} from '@chakra-ui/react' +// No necesitamos Form completo, solo FormTable +import FormTable from './FormTable.jsx' +import { getSchema, readConfig, writeConfig } from '../services/api.js' + +/** + * DatasetCompleteManager - Gestiona datasets y variables de forma simplificada + * Incluye: tabla de datasets individuales + variables (sin campos estáticos de configuración) + */ +export default function DatasetCompleteManager() { + const [fullData, setFullData] = useState({}) + const [datasetVariables, setDatasetVariables] = useState({}) + const [selectedDatasetId, setSelectedDatasetId] = useState('') + + const [datasetSchema, setDatasetSchema] = useState(null) + const [datasetUiSchema, setDatasetUiSchema] = useState({}) + const [variableSchema, setVariableSchema] = useState(null) + const [variableUiSchema, setVariableUiSchema] = useState({}) + + const [loading, setLoading] = useState(true) + const [saving, setSaving] = useState(false) + const [message, setMessage] = useState('') + + const muted = useColorModeValue('gray.600', 'gray.300') + + useEffect(() => { + loadData() + }, []) + + const loadData = async () => { + setLoading(true) + try { + // Cargar schemas completos + const [datasetSchemaResp, variableSchemaResp] = await Promise.all([ + getSchema('dataset-definitions'), + getSchema('dataset-variables') + ]) + + console.log('Complete dataset schema response:', datasetSchemaResp) + console.log('Complete variable schema response:', variableSchemaResp) + + // Cargar datos + const [datasetDataResp, variableDataResp] = await Promise.all([ + readConfig('dataset-definitions'), + readConfig('dataset-variables') + ]) + + console.log('Complete dataset data response:', datasetDataResp) + console.log('Complete variable data response:', variableDataResp) + + // Usar schemas completos + setDatasetSchema(datasetSchemaResp.schema) + setDatasetUiSchema(datasetSchemaResp.ui_schema || {}) + + // Schema para variables individuales + setVariableSchema(variableSchemaResp.schema?.properties?.dataset_variables?.additionalProperties?.properties?.variables) + setVariableUiSchema(variableSchemaResp.ui_schema?.dataset_variables?.additionalProperties?.variables || {}) + + setFullData(datasetDataResp.data || {}) + setDatasetVariables(variableDataResp.data?.dataset_variables || {}) + + // Seleccionar dataset actual + const currentId = datasetDataResp.data?.current_dataset_id + const datasetIds = Object.keys(datasetDataResp.data?.datasets || {}) + if (currentId && datasetIds.includes(currentId)) { + setSelectedDatasetId(currentId) + } else if (datasetIds.length > 0) { + setSelectedDatasetId(datasetIds[0]) + } + + } catch (error) { + console.error('Error loading complete data:', error) + setMessage(`Error loading data: ${error.message}`) + } finally { + setLoading(false) + } + } + + const saveFullData = async (newData) => { + setSaving(true) + try { + await writeConfig('dataset-definitions', newData) + setFullData(newData) + setMessage('Datasets saved successfully') + setTimeout(() => setMessage(''), 3000) + } catch (error) { + console.error('Error saving data:', error) + setMessage(`Error saving datasets: ${error.message}`) + } finally { + setSaving(false) + } + } + + const saveDatasets = async (newDatasets) => { + try { + // Mantener campos necesarios pero ocultar de la UI + const newFullData = { + datasets: newDatasets, + active_datasets: Object.keys(newDatasets).filter(id => newDatasets[id]?.enabled), + current_dataset_id: selectedDatasetId || Object.keys(newDatasets)[0] || null, + version: fullData.version || "1.0", + last_update: new Date().toISOString() + } + await saveFullData(newFullData) + } catch (error) { + console.error('Error saving datasets:', error) + setMessage(`Error saving datasets: ${error.message}`) + } + } + + const saveDatasetVariables = async (newVariables) => { + try { + const currentConfig = await readConfig('dataset-variables') + const updatedDatasetVariables = { + ...datasetVariables, + [selectedDatasetId]: { + variables: newVariables, + streaming_variables: Object.keys(newVariables).filter(key => newVariables[key]?.streaming) + } + } + + const saveData = { + ...currentConfig.data, + dataset_variables: updatedDatasetVariables, + last_update: new Date().toISOString() + } + + await writeConfig('dataset-variables', saveData) + setDatasetVariables(updatedDatasetVariables) + setMessage('Dataset variables saved successfully') + setTimeout(() => setMessage(''), 3000) + } catch (error) { + console.error('Error saving variables:', error) + setMessage(`Error saving variables: ${error.message}`) + } + } + + const currentDatasetVariables = useMemo(() => { + return selectedDatasetId && datasetVariables[selectedDatasetId] + ? datasetVariables[selectedDatasetId].variables || {} + : {} + }, [selectedDatasetId, datasetVariables]) + + const datasetOptions = Object.entries(fullData.datasets || {}).map(([id, dataset]) => ({ + value: id, + label: `${dataset.name || id} (${id})` + })) + + if (loading) { + return Loading dataset configuration... + } + + return ( + + {message && ( + + + {message} + + )} + + {/* Tabla de Datasets */} + + + 📊 Dataset Management + + Manage your datasets: create, edit and configure + + + + {datasetSchema?.properties?.datasets ? ( + + ) : ( + + + Dataset schema for individual datasets not available + + )} + + + + + + {/* Variables del Dataset */} + + + + 🔧 Dataset Variables + + Dataset: + + + + + + {!selectedDatasetId ? ( + + + Select a dataset to manage its variables + + ) : variableSchema ? ( + + ) : ( + + + Variable schema not available + + )} + + + + ) +} diff --git a/frontend/src/components/DatasetFormManager.jsx b/frontend/src/components/DatasetFormManager.jsx index 72df130..81710a0 100644 --- a/frontend/src/components/DatasetFormManager.jsx +++ b/frontend/src/components/DatasetFormManager.jsx @@ -64,9 +64,15 @@ export default function DatasetFormManager() { setDatasetSchema(datasetSchemaResp.schema?.properties?.datasets) setDatasetUiSchema(datasetSchemaResp.ui_schema?.datasets || {}) + console.log('Dataset full schema:', datasetSchemaResp.schema?.properties?.datasets) + console.log('Dataset full uiSchema:', datasetSchemaResp.ui_schema?.datasets) + setVariableSchema(variableSchemaResp.schema?.properties?.dataset_variables?.additionalProperties?.properties?.variables) setVariableUiSchema(variableSchemaResp.ui_schema?.dataset_variables?.additionalProperties?.variables || {}) + console.log('Variable full schema:', variableSchemaResp.schema?.properties?.dataset_variables?.additionalProperties?.properties?.variables) + console.log('Variable full uiSchema:', variableSchemaResp.ui_schema?.dataset_variables?.additionalProperties?.variables) + setDatasets(datasetDataResp.data?.datasets || {}) setDatasetVariables(variableDataResp.data?.dataset_variables || {}) diff --git a/frontend/src/components/FormTable.jsx b/frontend/src/components/FormTable.jsx index 9857c85..b3ff8b7 100644 --- a/frontend/src/components/FormTable.jsx +++ b/frontend/src/components/FormTable.jsx @@ -53,7 +53,12 @@ export default function FormTable({ } const itemSchema = schema.additionalProperties + // Usar el uiSchema completo de additionalProperties para respetar layouts definidos const itemUiSchema = uiSchema.additionalProperties || {} + + console.log('FormTable schema for', title, ':', itemSchema) + console.log('FormTable uiSchema for', title, ':', itemUiSchema) + const dataKeys = Object.keys(data) const handleAdd = (formData) => { @@ -147,6 +152,7 @@ export default function FormTable({ onSubmit={({ formData }) => handleAdd(formData)} templates={{ ObjectFieldTemplate: LayoutObjectFieldTemplate }} widgets={widgets} + showErrorList={false} > + + + + + + + + + + + + {variables.length === 0 ? ( + + + + ) : ( + variables.map((variable, index) => ( + + + + + )) + )} + +
Variable NameActions
+ No variables +
{variable} + + } + size="xs" + variant="outline" + onClick={() => openEdit(index)} + /> + } + size="xs" + variant="outline" + colorScheme="red" + onClick={() => handleDelete(index)} + /> + +
+
+ + {/* Add Modal */} + + + + Add Variable + + + + + + Variable Name * + + setNewVariable(e.target.value)} + placeholder="e.g., UR29_Brix" + /> + + Enter the name of the variable to plot + + + + + + + + + + + + {/* Edit Modal */} + + + + Edit Variable + + + + + + Variable Name * + + setEditingValue(e.target.value)} + placeholder="e.g., UR29_Brix" + /> + + Enter the name of the variable to plot + + + + + + + + + + + + ) +} + +/** + * PlotCompleteManager - Gestiona plots y variables de forma simplificada + * Incluye: tabla de plots individuales + variables (sin campos estáticos de configuración) + */ +export default function PlotCompleteManager() { + const [fullData, setFullData] = useState({}) + const [plotVariables, setPlotVariables] = useState({}) + const [selectedPlotId, setSelectedPlotId] = useState('') + + const [plotSchema, setPlotSchema] = useState(null) + const [plotUiSchema, setPlotUiSchema] = useState({}) + + const [loading, setLoading] = useState(true) + const [saving, setSaving] = useState(false) + const [message, setMessage] = useState('') + + const muted = useColorModeValue('gray.600', 'gray.300') + + useEffect(() => { + loadData() + }, []) + + const loadData = async () => { + setLoading(true) + try { + // Cargar schemas completos + const [plotSchemaResp, plotVariableSchemaResp] = await Promise.all([ + getSchema('plot-definitions'), + getSchema('plot-variables') + ]) + + console.log('Complete plot schema response:', plotSchemaResp) + console.log('Complete plot variable schema response:', plotVariableSchemaResp) + + // Cargar datos + const [plotDataResp, plotVariableDataResp] = await Promise.all([ + readConfig('plot-definitions'), + readConfig('plot-variables') + ]) + + console.log('Complete plot data response:', plotDataResp) + console.log('Complete plot variable data response:', plotVariableDataResp) + + // Usar schemas completos + setPlotSchema(plotSchemaResp.schema) + setPlotUiSchema(plotSchemaResp.ui_schema || {}) + + setFullData(plotDataResp.data || {}) + setPlotVariables(plotVariableDataResp.data?.plot_variables || {}) + + // Seleccionar primer plot + const plotIds = Object.keys(plotDataResp.data?.plots || {}) + if (plotIds.length > 0 && !selectedPlotId) { + setSelectedPlotId(plotIds[0]) + } + + } catch (error) { + console.error('Error loading complete plot data:', error) + setMessage(`Error loading data: ${error.message}`) + } finally { + setLoading(false) + } + } + + const saveFullData = async (newData) => { + setSaving(true) + try { + await writeConfig('plot-definitions', newData) + setFullData(newData) + setMessage('Plot configuration saved successfully') + setTimeout(() => setMessage(''), 3000) + } catch (error) { + console.error('Error saving full plot data:', error) + setMessage(`Error saving plot configuration: ${error.message}`) + } finally { + setSaving(false) + } + } + + const savePlots = async (newPlots) => { + try { + // Mantener campos necesarios pero ocultar de la UI + const newFullData = { + plots: newPlots, + session_counter: fullData.session_counter || 0, + last_saved: new Date().toISOString(), + version: fullData.version || "1.0" + } + await saveFullData(newFullData) + } catch (error) { + console.error('Error saving plots:', error) + setMessage(`Error saving plots: ${error.message}`) + } + } + + const savePlotVariables = async (newVariables) => { + try { + const currentConfig = await readConfig('plot-variables') + const updatedPlotVariables = { + ...plotVariables, + [selectedPlotId]: { + variables: newVariables + } + } + + const saveData = { + ...currentConfig.data, + plot_variables: updatedPlotVariables, + last_update: new Date().toISOString() + } + + await writeConfig('plot-variables', saveData) + setPlotVariables(updatedPlotVariables) + setMessage('Plot variables saved successfully') + setTimeout(() => setMessage(''), 3000) + } catch (error) { + console.error('Error saving plot variables:', error) + setMessage(`Error saving variables: ${error.message}`) + } + } + + const currentPlotVariables = useMemo(() => { + return selectedPlotId && plotVariables[selectedPlotId] + ? plotVariables[selectedPlotId].variables || [] + : [] + }, [selectedPlotId, plotVariables]) + + const plotOptions = Object.entries(fullData.plots || {}).map(([id, plot]) => ({ + value: id, + label: `${plot.name || id} (${id})` + })) + + if (loading) { + return Loading plot configuration... + } + + return ( + + {message && ( + + + {message} + + )} + + {/* Tabla de Plots */} + + + 📈 Plot Management + + Manage your plots: create, edit and configure + + + + {plotSchema?.properties?.plots ? ( + + ) : ( + + + Plot schema for individual plots not available + + )} + + + + + + {/* Variables del Plot */} + + + + 🔧 Plot Variables + + Plot: + + + + + + {!selectedPlotId ? ( + + + Select a plot to manage its variables + + ) : ( + + )} + + + + ) +} diff --git a/frontend/src/components/PlotFormManager.jsx b/frontend/src/components/PlotFormManager.jsx index a25cf03..d85be5c 100644 --- a/frontend/src/components/PlotFormManager.jsx +++ b/frontend/src/components/PlotFormManager.jsx @@ -244,6 +244,9 @@ export default function PlotFormManager() { setPlotSchema(plotSchemaResp.schema?.properties?.plots) setPlotUiSchema(plotSchemaResp.ui_schema?.plots || {}) + console.log('Plot full schema:', plotSchemaResp.schema?.properties?.plots) + console.log('Plot full uiSchema:', plotSchemaResp.ui_schema?.plots) + setPlots(plotDataResp.data?.plots || {}) setPlotVariables(plotVariableDataResp.data?.plot_variables || {}) diff --git a/frontend/src/pages/Dashboard.jsx b/frontend/src/pages/Dashboard.jsx index 1644e23..97f1ab9 100644 --- a/frontend/src/pages/Dashboard.jsx +++ b/frontend/src/pages/Dashboard.jsx @@ -5,8 +5,8 @@ import Form from '@rjsf/chakra-ui' import validator from '@rjsf/validator-ajv8' import LayoutObjectFieldTemplate from '../components/rjsf/LayoutObjectFieldTemplate.jsx' import { widgets } from '../components/rjsf/widgets.jsx' -import DatasetFormManager from '../components/DatasetFormManager.jsx' -import PlotFormManager from '../components/PlotFormManager.jsx' +import DatasetCompleteManager from '../components/DatasetCompleteManager.jsx' +import PlotCompleteManager from '../components/PlotCompleteManager.jsx' import { getStatus, getEvents, @@ -249,7 +249,7 @@ export default function DashboardPage() { 📊 Dataset Management - + )} @@ -261,7 +261,7 @@ export default function DashboardPage() { 📈 Plot Management - + )}