Actualización de la gestión de datasets y plots en el Dashboard, integrando nuevos componentes que respetan los layouts definidos en los schemas UI. Se mejoró la presentación de formularios y se implementaron logs de debugging para facilitar la verificación de esquemas. Además, se ajustaron los esquemas de configuración y se eliminaron campos estáticos innecesarios, optimizando la interfaz para el usuario.
This commit is contained in:
parent
500b68c4d5
commit
bacc9933b3
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -86,7 +86,7 @@
|
|||
},
|
||||
{
|
||||
"name": "version",
|
||||
"width": 3
|
||||
"width": 4
|
||||
}
|
||||
]
|
||||
]
|
||||
|
|
|
@ -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 <Text>Loading dataset configuration...</Text>
|
||||
}
|
||||
|
||||
return (
|
||||
<VStack align="stretch" spacing={4}>
|
||||
{message && (
|
||||
<Alert status={message.includes('Error') ? 'error' : 'success'}>
|
||||
<AlertIcon />
|
||||
{message}
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
{/* Tabla de Datasets */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<Heading size="sm">📊 Dataset Management</Heading>
|
||||
<Text fontSize="sm" color={muted}>
|
||||
Manage your datasets: create, edit and configure
|
||||
</Text>
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
{datasetSchema?.properties?.datasets ? (
|
||||
<FormTable
|
||||
schema={datasetSchema.properties.datasets}
|
||||
uiSchema={datasetUiSchema.datasets}
|
||||
data={fullData.datasets || {}}
|
||||
onChange={saveDatasets}
|
||||
title="Datasets"
|
||||
keyField="id"
|
||||
/>
|
||||
) : (
|
||||
<Alert status="warning">
|
||||
<AlertIcon />
|
||||
Dataset schema for individual datasets not available
|
||||
</Alert>
|
||||
)}
|
||||
</CardBody>
|
||||
</Card>
|
||||
|
||||
<Divider />
|
||||
|
||||
{/* Variables del Dataset */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<HStack justify="space-between">
|
||||
<Heading size="sm">🔧 Dataset Variables</Heading>
|
||||
<HStack>
|
||||
<Text fontSize="sm" color={muted}>Dataset:</Text>
|
||||
<Select
|
||||
size="sm"
|
||||
value={selectedDatasetId}
|
||||
onChange={(e) => setSelectedDatasetId(e.target.value)}
|
||||
placeholder="Select dataset"
|
||||
width="200px"
|
||||
>
|
||||
{datasetOptions.map(option => (
|
||||
<option key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</option>
|
||||
))}
|
||||
</Select>
|
||||
</HStack>
|
||||
</HStack>
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
{!selectedDatasetId ? (
|
||||
<Alert status="info">
|
||||
<AlertIcon />
|
||||
Select a dataset to manage its variables
|
||||
</Alert>
|
||||
) : variableSchema ? (
|
||||
<FormTable
|
||||
schema={variableSchema}
|
||||
uiSchema={variableUiSchema}
|
||||
data={currentDatasetVariables}
|
||||
onChange={saveDatasetVariables}
|
||||
title={`Variables for: ${fullData.datasets?.[selectedDatasetId]?.name || selectedDatasetId}`}
|
||||
keyField="name"
|
||||
/>
|
||||
) : (
|
||||
<Alert status="warning">
|
||||
<AlertIcon />
|
||||
Variable schema not available
|
||||
</Alert>
|
||||
)}
|
||||
</CardBody>
|
||||
</Card>
|
||||
</VStack>
|
||||
)
|
||||
}
|
|
@ -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 || {})
|
||||
|
||||
|
|
|
@ -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}
|
||||
>
|
||||
<HStack mt={3}>
|
||||
<Button type="submit" size="sm" colorScheme="blue">
|
||||
|
@ -214,6 +220,7 @@ export default function FormTable({
|
|||
templates={{ ObjectFieldTemplate: LayoutObjectFieldTemplate }}
|
||||
widgets={widgets}
|
||||
readonly={editingKey !== key}
|
||||
showErrorList={false}
|
||||
>
|
||||
{editingKey === key && (
|
||||
<HStack mt={3}>
|
||||
|
|
|
@ -0,0 +1,417 @@
|
|||
import React, { useState, useEffect, useMemo } from 'react'
|
||||
import {
|
||||
Box,
|
||||
VStack,
|
||||
HStack,
|
||||
Text,
|
||||
Select,
|
||||
Card,
|
||||
CardBody,
|
||||
CardHeader,
|
||||
Heading,
|
||||
Alert,
|
||||
AlertIcon,
|
||||
useColorModeValue,
|
||||
Divider,
|
||||
Button,
|
||||
Table,
|
||||
Thead,
|
||||
Tbody,
|
||||
Tr,
|
||||
Th,
|
||||
Td,
|
||||
IconButton,
|
||||
Input,
|
||||
Modal,
|
||||
ModalOverlay,
|
||||
ModalContent,
|
||||
ModalHeader,
|
||||
ModalBody,
|
||||
ModalFooter,
|
||||
ModalCloseButton,
|
||||
useDisclosure
|
||||
} from '@chakra-ui/react'
|
||||
import { AddIcon, DeleteIcon, EditIcon } from '@chakra-ui/icons'
|
||||
// Solo necesitamos FormTable para plots individuales
|
||||
import FormTable from './FormTable.jsx'
|
||||
import { getSchema, readConfig, writeConfig } from '../services/api.js'
|
||||
|
||||
/**
|
||||
* PlotVariablesManager - Componente para gestionar array de strings (variables de plot)
|
||||
*/
|
||||
function PlotVariablesManager({ variables = [], onChange, title = "Variables" }) {
|
||||
const [newVariable, setNewVariable] = useState('')
|
||||
const [editingIndex, setEditingIndex] = useState(null)
|
||||
const [editingValue, setEditingValue] = useState('')
|
||||
|
||||
const { isOpen: isAddOpen, onOpen: onAddOpen, onClose: onAddClose } = useDisclosure()
|
||||
const { isOpen: isEditOpen, onOpen: onEditOpen, onClose: onEditClose } = useDisclosure()
|
||||
|
||||
const muted = useColorModeValue('gray.600', 'gray.300')
|
||||
const borderColor = useColorModeValue('gray.200', 'gray.600')
|
||||
|
||||
const handleAdd = () => {
|
||||
if (!newVariable.trim()) return
|
||||
|
||||
const newVariables = [...variables, newVariable.trim()]
|
||||
onChange(newVariables)
|
||||
setNewVariable('')
|
||||
onAddClose()
|
||||
}
|
||||
|
||||
const handleEdit = () => {
|
||||
if (editingIndex === null || !editingValue.trim()) return
|
||||
|
||||
const newVariables = [...variables]
|
||||
newVariables[editingIndex] = editingValue.trim()
|
||||
onChange(newVariables)
|
||||
setEditingIndex(null)
|
||||
setEditingValue('')
|
||||
onEditClose()
|
||||
}
|
||||
|
||||
const handleDelete = (index) => {
|
||||
if (confirm('¿Eliminar esta variable?')) {
|
||||
const newVariables = variables.filter((_, i) => i !== index)
|
||||
onChange(newVariables)
|
||||
}
|
||||
}
|
||||
|
||||
const openEdit = (index) => {
|
||||
setEditingIndex(index)
|
||||
setEditingValue(variables[index])
|
||||
onEditOpen()
|
||||
}
|
||||
|
||||
return (
|
||||
<VStack align="stretch" spacing={3}>
|
||||
<HStack justify="space-between">
|
||||
<Text fontWeight="semibold">{title}</Text>
|
||||
<Button size="sm" leftIcon={<AddIcon />} onClick={onAddOpen}>
|
||||
Add Variable
|
||||
</Button>
|
||||
</HStack>
|
||||
|
||||
<Box overflowX="auto" borderWidth="1px" borderRadius="md" borderColor={borderColor}>
|
||||
<Table size="sm">
|
||||
<Thead>
|
||||
<Tr>
|
||||
<Th>Variable Name</Th>
|
||||
<Th width="100px">Actions</Th>
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
{variables.length === 0 ? (
|
||||
<Tr>
|
||||
<Td colSpan={2}>
|
||||
<Text color={muted} textAlign="center">No variables</Text>
|
||||
</Td>
|
||||
</Tr>
|
||||
) : (
|
||||
variables.map((variable, index) => (
|
||||
<Tr key={index}>
|
||||
<Td>{variable}</Td>
|
||||
<Td>
|
||||
<HStack spacing={1}>
|
||||
<IconButton
|
||||
icon={<EditIcon />}
|
||||
size="xs"
|
||||
variant="outline"
|
||||
onClick={() => openEdit(index)}
|
||||
/>
|
||||
<IconButton
|
||||
icon={<DeleteIcon />}
|
||||
size="xs"
|
||||
variant="outline"
|
||||
colorScheme="red"
|
||||
onClick={() => handleDelete(index)}
|
||||
/>
|
||||
</HStack>
|
||||
</Td>
|
||||
</Tr>
|
||||
))
|
||||
)}
|
||||
</Tbody>
|
||||
</Table>
|
||||
</Box>
|
||||
|
||||
{/* Add Modal */}
|
||||
<Modal isOpen={isAddOpen} onClose={onAddClose}>
|
||||
<ModalOverlay />
|
||||
<ModalContent>
|
||||
<ModalHeader>Add Variable</ModalHeader>
|
||||
<ModalCloseButton />
|
||||
<ModalBody>
|
||||
<VStack spacing={3}>
|
||||
<Box width="100%">
|
||||
<Text fontSize="sm" fontWeight="medium" mb={1}>
|
||||
Variable Name *
|
||||
</Text>
|
||||
<Input
|
||||
size="sm"
|
||||
value={newVariable}
|
||||
onChange={(e) => setNewVariable(e.target.value)}
|
||||
placeholder="e.g., UR29_Brix"
|
||||
/>
|
||||
<Text fontSize="xs" color={muted} mt={1}>
|
||||
Enter the name of the variable to plot
|
||||
</Text>
|
||||
</Box>
|
||||
</VStack>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button variant="ghost" mr={3} onClick={onAddClose}>Cancel</Button>
|
||||
<Button colorScheme="blue" onClick={handleAdd}>Add</Button>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
|
||||
{/* Edit Modal */}
|
||||
<Modal isOpen={isEditOpen} onClose={onEditClose}>
|
||||
<ModalOverlay />
|
||||
<ModalContent>
|
||||
<ModalHeader>Edit Variable</ModalHeader>
|
||||
<ModalCloseButton />
|
||||
<ModalBody>
|
||||
<VStack spacing={3}>
|
||||
<Box width="100%">
|
||||
<Text fontSize="sm" fontWeight="medium" mb={1}>
|
||||
Variable Name *
|
||||
</Text>
|
||||
<Input
|
||||
size="sm"
|
||||
value={editingValue}
|
||||
onChange={(e) => setEditingValue(e.target.value)}
|
||||
placeholder="e.g., UR29_Brix"
|
||||
/>
|
||||
<Text fontSize="xs" color={muted} mt={1}>
|
||||
Enter the name of the variable to plot
|
||||
</Text>
|
||||
</Box>
|
||||
</VStack>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button variant="ghost" mr={3} onClick={onEditClose}>Cancel</Button>
|
||||
<Button colorScheme="blue" onClick={handleEdit}>Save</Button>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
</VStack>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 <Text>Loading plot configuration...</Text>
|
||||
}
|
||||
|
||||
return (
|
||||
<VStack align="stretch" spacing={4}>
|
||||
{message && (
|
||||
<Alert status={message.includes('Error') ? 'error' : 'success'}>
|
||||
<AlertIcon />
|
||||
{message}
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
{/* Tabla de Plots */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<Heading size="sm">📈 Plot Management</Heading>
|
||||
<Text fontSize="sm" color={muted}>
|
||||
Manage your plots: create, edit and configure
|
||||
</Text>
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
{plotSchema?.properties?.plots ? (
|
||||
<FormTable
|
||||
schema={plotSchema.properties.plots}
|
||||
uiSchema={plotUiSchema.plots}
|
||||
data={fullData.plots || {}}
|
||||
onChange={savePlots}
|
||||
title="Plots"
|
||||
keyField="session_id"
|
||||
/>
|
||||
) : (
|
||||
<Alert status="warning">
|
||||
<AlertIcon />
|
||||
Plot schema for individual plots not available
|
||||
</Alert>
|
||||
)}
|
||||
</CardBody>
|
||||
</Card>
|
||||
|
||||
<Divider />
|
||||
|
||||
{/* Variables del Plot */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<HStack justify="space-between">
|
||||
<Heading size="sm">🔧 Plot Variables</Heading>
|
||||
<HStack>
|
||||
<Text fontSize="sm" color={muted}>Plot:</Text>
|
||||
<Select
|
||||
size="sm"
|
||||
value={selectedPlotId}
|
||||
onChange={(e) => setSelectedPlotId(e.target.value)}
|
||||
placeholder="Select plot"
|
||||
width="200px"
|
||||
>
|
||||
{plotOptions.map(option => (
|
||||
<option key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</option>
|
||||
))}
|
||||
</Select>
|
||||
</HStack>
|
||||
</HStack>
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
{!selectedPlotId ? (
|
||||
<Alert status="info">
|
||||
<AlertIcon />
|
||||
Select a plot to manage its variables
|
||||
</Alert>
|
||||
) : (
|
||||
<PlotVariablesManager
|
||||
variables={currentPlotVariables}
|
||||
onChange={savePlotVariables}
|
||||
title={`Variables for: ${fullData.plots?.[selectedPlotId]?.name || selectedPlotId}`}
|
||||
/>
|
||||
)}
|
||||
</CardBody>
|
||||
</Card>
|
||||
</VStack>
|
||||
)
|
||||
}
|
|
@ -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 || {})
|
||||
|
||||
|
|
|
@ -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() {
|
|||
<Flex wrap="wrap" gap={2} align="center" mb={3}>
|
||||
<Text fontWeight="semibold" textTransform="uppercase">📊 Dataset Management</Text>
|
||||
</Flex>
|
||||
<DatasetFormManager />
|
||||
<DatasetCompleteManager />
|
||||
</CardBody>
|
||||
</Card>
|
||||
)}
|
||||
|
@ -261,7 +261,7 @@ export default function DashboardPage() {
|
|||
<Flex wrap="wrap" gap={2} align="center" mb={3}>
|
||||
<Text fontWeight="semibold" textTransform="uppercase">📈 Plot Management</Text>
|
||||
</Flex>
|
||||
<PlotFormManager />
|
||||
<PlotCompleteManager />
|
||||
</CardBody>
|
||||
</Card>
|
||||
)}
|
||||
|
|
Loading…
Reference in New Issue