diff --git a/nuxt4/app/app.vue b/nuxt4/app/app.vue
index 416f827..ec6e420 100644
--- a/nuxt4/app/app.vue
+++ b/nuxt4/app/app.vue
@@ -80,7 +80,7 @@
-
+
@@ -105,10 +105,51 @@
@create="showCreateOperacionModal = true"
@view="handleViewOperacion"
/>
-
-
-
-
+
+
+
+
+
+
+
+
+
+
Grafo de trazabilidad
+
Visualiza el grafo desde el lote seleccionado (por defecto el más reciente).
+
+
+
+
+ Recargar
+
+
+
+
+
+ {{ graphError }}
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -181,11 +222,44 @@
-
+
+
+
+
+
Detalle de Operación
+
+ {{ getTipoLabel(selectedOperacion.tipo) }} · {{ formatDate(selectedOperacion.fecha) }}
+
+
+
+
+
+
+
+
+
ID
+
{{ selectedOperacion.id }}
+
+
+
Fecha
+
{{ formatDate(selectedOperacion.fecha) }}
+
+
+
Tipo
+
{{ getTipoLabel(selectedOperacion.tipo) }}
+
+
+
+
Meta
+
+
+{{ JSON.stringify(selectedOperacion.meta || {}, null, 2) }}
+
+
+
+
+ No hay operación seleccionada.
+
@@ -195,12 +269,14 @@
import type { Lote, Operacion } from '~/composables/useLotes'
const { isAuthenticated } = useAuthentik()
+const { fetchLotes: fetchLotesComposable } = useLotes()
// Navegación
const selectedTab = ref('lotes')
const tabs = [
{ label: 'Lotes', icon: 'i-heroicons-cube', slot: 'lotes', value: 'lotes' },
{ label: 'Operaciones', icon: 'i-heroicons-beaker', slot: 'operaciones', value: 'operaciones' },
+ { label: 'Grafos', icon: 'i-heroicons-share', slot: 'grafos', value: 'grafos' },
]
// Estados de modales
@@ -215,6 +291,10 @@ const showOperacionDetailModal = ref(false)
const selectedLote = ref(null)
const selectedOperacion = ref(null)
const trazabilidadLoteId = ref(null)
+const graphLotes = ref([])
+const graphLoading = ref(false)
+const graphError = ref(null)
+const selectedGraphLoteId = ref(null)
// Handlers para Lotes
const handleViewLote = (lote: Lote) => {
@@ -271,6 +351,36 @@ const closeOperacionDetailModal = () => {
selectedOperacion.value = null
}
+// Datos para grafos
+const graphSelectItems = computed(() =>
+ graphLotes.value.map((lote) => ({
+ label: `${lote.codigo || lote.id} · ${getTipoLabel(lote.tipo)}`,
+ value: lote.id,
+ }))
+)
+
+const loadGraphLotes = async () => {
+ graphLoading.value = true
+ graphError.value = null
+ try {
+ const lotes = await fetchLotesComposable()
+ graphLotes.value = lotes.sort((a, b) =>
+ new Date(b.fecha_creado).getTime() - new Date(a.fecha_creado).getTime()
+ )
+ if (!selectedGraphLoteId.value && graphLotes.value.length > 0) {
+ selectedGraphLoteId.value = graphLotes.value[0].id
+ }
+ } catch (err: any) {
+ graphError.value = err?.message || 'Error cargando lotes'
+ } finally {
+ graphLoading.value = false
+ }
+}
+
+onMounted(() => {
+ loadGraphLotes()
+})
+
// ⚠️⚠️⚠️ FUNCIONES DE DEBUG - TEMPORALES ⚠️⚠️⚠️
// NO ELIMINAR SIN CONSULTAR A DARIO/DRAGANEL/NUCLEO000
const resettingDB = ref(false)
diff --git a/nuxt4/app/components/lotes/TrazabilidadGraph.vue b/nuxt4/app/components/lotes/TrazabilidadGraph.vue
index 178e372..aa8182d 100644
--- a/nuxt4/app/components/lotes/TrazabilidadGraph.vue
+++ b/nuxt4/app/components/lotes/TrazabilidadGraph.vue
@@ -51,7 +51,7 @@
/>
-
+
@@ -68,6 +68,15 @@
:x1="edge.fromPos.x"
:y1="edge.midPos.y"
:x2="edge.toPos.x"
+ :y2="edge.midPos.y"
+ class="transition-opacity duration-200"
+ :opacity="0.9"
+ />
+
+