{/* Variable Name Field */}
diff --git a/frontend/src/components/widgets/TestWidget.jsx b/frontend/src/components/widgets/TestWidget.jsx
index 3298dcf..d07a9f9 100644
--- a/frontend/src/components/widgets/TestWidget.jsx
+++ b/frontend/src/components/widgets/TestWidget.jsx
@@ -4,8 +4,6 @@ import React from 'react'
* Simple Test Widget para verificar que el sistema de widgets funciona
*/
const TestWidget = (props) => {
- console.log('🧪 TestWidget loaded!', props)
-
return (
{}
+ }
}
return context
}
diff --git a/frontend/src/hooks/useCoordinatedConnection.js b/frontend/src/hooks/useCoordinatedConnection.js
index fcbfaf8..f0bd9f2 100644
--- a/frontend/src/hooks/useCoordinatedConnection.js
+++ b/frontend/src/hooks/useCoordinatedConnection.js
@@ -13,6 +13,9 @@ export function useCoordinatedConnection(source, connectionFactory, dependencies
const coordinatorRef = useRef(null)
const subscriptionRef = useRef(null)
+ // Ensure dependencies is always an array to prevent undefined issues
+ const safeDependencies = Array.isArray(dependencies) ? dependencies : []
+
// Obtener el coordinador
useEffect(() => {
coordinatorRef.current = getTabCoordinator()
@@ -30,7 +33,7 @@ export function useCoordinatedConnection(source, connectionFactory, dependencies
// Crear/recrear conexión cuando cambia el liderazgo o dependencias
useEffect(() => {
- if (!coordinatorRef.current) return
+ if (!coordinatorRef.current || source === 'null_source') return
// Limpiar conexión anterior
if (connectionRef.current && typeof connectionRef.current.close === 'function') {
@@ -83,7 +86,7 @@ export function useCoordinatedConnection(source, connectionFactory, dependencies
subscriptionRef.current()
}
}
- }, [source, isLeader, ...dependencies])
+ }, [source, isLeader, ...safeDependencies])
// Cleanup final
useEffect(() => {
@@ -106,6 +109,9 @@ export function useCoordinatedConnection(source, connectionFactory, dependencies
export function useCoordinatedPolling(source, fetchFunction, interval = 5000, dependencies = []) {
const [connectionError, setConnectionError] = useState(null)
+ // Ensure dependencies is always an array
+ const safeDependencies = Array.isArray(dependencies) ? dependencies : []
+
const result = useCoordinatedConnection(
source,
useCallback((onData) => {
@@ -169,45 +175,114 @@ export function useCoordinatedPolling(source, fetchFunction, interval = 5000, de
}
}
}, [fetchFunction, interval]),
- dependencies
+ safeDependencies
)
return { ...result, connectionError }
}
/**
- * Hook para SSE coordinado
+ * Hook para SSE coordinado con reconexión automática
*/
export function useCoordinatedSSE(source, url, dependencies = []) {
- return useCoordinatedConnection(
- source,
+ // Ensure dependencies is always an array
+ const safeDependencies = Array.isArray(dependencies) ? dependencies : []
+
+ // Always call useCoordinatedConnection to maintain hook order
+ const result = useCoordinatedConnection(
+ source || 'null_source', // Use a placeholder when source is null
useCallback((onData) => {
// Don't create EventSource if URL is null or undefined
- if (!url) {
- // console.log(`Skipping SSE connection - URL is ${url}`)
+ if (!url || !source) {
+ // console.log(`Skipping SSE connection - URL is ${url}, source is ${source}`)
return {
close: () => {} // Return mock connection with close method
}
}
- // console.log(`Creating SSE connection to ${url}`)
- const eventSource = new EventSource(url)
+ let eventSource = null
+ let isActive = true
+ let reconnectAttempts = 0
+ const maxReconnectAttempts = 10
+ const baseReconnectDelay = 1000 // 1 second
- eventSource.onmessage = (event) => {
+ const createConnection = () => {
+ if (!isActive) return
+
try {
- const data = JSON.parse(event.data)
- onData(data)
+ // console.log(`Creating SSE connection to ${url} (attempt ${reconnectAttempts + 1})`)
+ eventSource = new EventSource(url)
+
+ eventSource.onopen = () => {
+ // console.log(`SSE connection opened for ${source}`)
+ reconnectAttempts = 0 // Reset attempts on successful connection
+ }
+
+ eventSource.onmessage = (event) => {
+ try {
+ const data = JSON.parse(event.data)
+ onData(data)
+ } catch (error) {
+ console.error('SSE data parse error:', error)
+ }
+ }
+
+ eventSource.onerror = (error) => {
+ console.warn(`SSE connection error for ${source}:`, error)
+
+ if (eventSource.readyState === EventSource.CLOSED) {
+ // Connection is closed, attempt to reconnect
+ if (isActive && reconnectAttempts < maxReconnectAttempts) {
+ reconnectAttempts++
+ const delay = Math.min(baseReconnectDelay * Math.pow(2, reconnectAttempts - 1), 30000)
+ console.log(`Reconnecting SSE in ${delay}ms (attempt ${reconnectAttempts}/${maxReconnectAttempts})`)
+
+ setTimeout(() => {
+ if (isActive) {
+ createConnection()
+ }
+ }, delay)
+ } else {
+ console.error(`Max reconnection attempts reached for ${source}`)
+ }
+ }
+ }
} catch (error) {
- console.error('SSE data parse error:', error)
+ console.error(`Failed to create SSE connection for ${source}:`, error)
+
+ // Retry with exponential backoff
+ if (isActive && reconnectAttempts < maxReconnectAttempts) {
+ reconnectAttempts++
+ const delay = Math.min(baseReconnectDelay * Math.pow(2, reconnectAttempts - 1), 30000)
+ setTimeout(() => {
+ if (isActive) {
+ createConnection()
+ }
+ }, delay)
+ }
}
}
+
+ // Create initial connection
+ createConnection()
- eventSource.onerror = (error) => {
- console.error('SSE error:', error)
+ return {
+ close: () => {
+ isActive = false
+ if (eventSource) {
+ eventSource.close()
+ eventSource = null
+ }
+ }
}
-
- return eventSource
- }, [url]),
- dependencies
+ }, [url, source]),
+ safeDependencies
)
+
+ // If source is null or undefined, return null data but maintain hook call consistency
+ if (!source) {
+ return { data: null, isLeader: false, isConnected: false }
+ }
+
+ return result
}
diff --git a/frontend/src/pages/Dashboard.jsx b/frontend/src/pages/Dashboard.jsx
index e63245f..d82648c 100644
--- a/frontend/src/pages/Dashboard.jsx
+++ b/frontend/src/pages/Dashboard.jsx
@@ -1320,9 +1320,6 @@ function DatasetManager() {
api.getSchema('dataset-variables')
])
- console.log('🔧 Dashboard loaded datasets data:', JSON.stringify(datasetsData, null, 2))
- console.log('🔧 Dashboard loaded variables data:', JSON.stringify(variablesData, null, 2))
-
setDatasetsConfig(datasetsData)
setVariablesConfig(variablesData)
setDatasetsSchemaData(datasetsSchemaResponse)
@@ -1414,7 +1411,6 @@ function DatasetManager() {
// Get filtered variables for selected dataset (memoized)
const selectedDatasetVariables = useMemo(() => {
- console.log('🔧 Recalculating selected dataset variables...')
if (!variablesConfig?.variables || !selectedDatasetId) {
return { dataset_id: selectedDatasetId, variables: [] }
}
@@ -1494,7 +1490,6 @@ function DatasetManager() {
onChange={(e) => {
const newDatasetId = e.target.value
setSelectedDatasetId(newDatasetId)
- // console.log(`🎯 Dataset selection changed to: ${newDatasetId}`)
}}
placeholder="Choose a dataset to configure..."
size="lg"
@@ -1555,14 +1550,6 @@ function DatasetManager() {
{(() => {
const selectedDatasetVars = selectedDatasetVariables
- // Debug log to understand what data we're working with
- console.log('🔧 Form rendering with data:', {
- selectedDatasetId,
- selectedDatasetVars,
- variablesArray: selectedDatasetVars?.variables,
- variablesCount: selectedDatasetVars?.variables?.length || 0
- })
-
// Create simplified schema from external schema for single dataset variables
let singleDatasetSchema = null
let singleDatasetUiSchema = null
@@ -1573,13 +1560,6 @@ function DatasetManager() {
const datasetItemSchema = variablesSchemaData.schema.properties?.variables?.items
const variablesArraySchema = datasetItemSchema?.properties?.variables
- console.log('🔧 Schema extraction:', {
- datasetItemSchema: !!datasetItemSchema,
- variablesArraySchema: !!variablesArraySchema,
- variablesArraySchemaKeys: variablesArraySchema ? Object.keys(variablesArraySchema) : null,
- itemsSchema: variablesArraySchema?.items
- })
-
if (variablesArraySchema) {
singleDatasetSchema = {
type: "object",
@@ -1600,13 +1580,6 @@ function DatasetManager() {
const datasetItemUiSchema = variablesSchemaData.uiSchema.variables?.items
const variablesUiSchema = datasetItemUiSchema?.variables
- console.log('🔧 UI Schema extraction:', {
- datasetItemUiSchema: !!datasetItemUiSchema,
- variablesUiSchema: !!variablesUiSchema,
- variablesUiSchemaKeys: variablesUiSchema ? Object.keys(variablesUiSchema) : null,
- itemsUiField: variablesUiSchema?.items?.['ui:field']
- })
-
if (variablesUiSchema) {
singleDatasetUiSchema = {
variables: variablesUiSchema
@@ -1635,22 +1608,6 @@ function DatasetManager() {
variables: selectedDatasetVars.variables || []
}
- console.log('🔧 Passing formData to Form:', {
- formDataToPass,
- variablesLength: formDataToPass.variables.length,
- firstVariable: formDataToPass.variables[0] || 'no variables',
- allVariables: formDataToPass.variables,
- formDataJSON: JSON.stringify(formDataToPass, null, 2)
- })
-
- console.log('🔧 Final schemas for Form:', {
- singleDatasetSchema,
- singleDatasetUiSchema,
- schemaVariablesType: singleDatasetSchema?.properties?.variables?.type,
- schemaVariablesItemsProps: singleDatasetSchema?.properties?.variables?.items ? Object.keys(singleDatasetSchema.properties.variables.items.properties || {}) : null,
- uiSchemaVariablesItemsField: singleDatasetUiSchema?.variables?.items?.['ui:field']
- })
-
return (