mejoras UI 5
All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 1m5s

This commit is contained in:
2025-11-22 02:19:32 -06:00
parent e698cc795a
commit c4be9649b8
2 changed files with 130 additions and 11 deletions

View File

@@ -80,7 +80,7 @@
<!-- Contenido principal -->
<div v-if="isAuthenticated">
<!-- Navegación por Tabs -->
<UTabs v-model="selectedTab" :items="tabs" class="mb-6">
<UTabs v-model="selectedTab" :items="tabs" class="mb-6">
<!-- Tab: Lotes -->
<template #lotes>
<div class="py-4">
@@ -108,6 +108,47 @@
</ClientOnly>
</div>
</template>
<!-- Tab: Grafos -->
<template #grafos>
<div class="py-4 space-y-4">
<div class="flex flex-col md:flex-row md:items-center md:justify-between gap-3">
<div>
<h3 class="text-xl font-semibold">Grafo de trazabilidad</h3>
<p class="text-sm text-gray-500 dark:text-gray-400">Visualiza el grafo desde el lote seleccionado (por defecto el más reciente).</p>
</div>
<div class="flex gap-2 items-center">
<USelect
v-model="selectedGraphLoteId"
:items="graphSelectItems"
label-key="label"
value-key="value"
searchable
placeholder="Selecciona lote"
class="w-72"
:loading="graphLoading"
:disabled="graphLoading || graphSelectItems.length === 0"
/>
<UButton icon="i-heroicons-arrow-path" variant="outline" @click="loadGraphLotes">
Recargar
</UButton>
</div>
</div>
<UAlert v-if="graphError" color="red" variant="soft" title="Error cargando lotes">
{{ graphError }}
</UAlert>
<ClientOnly>
<div v-if="selectedGraphLoteId" class="mt-2">
<LotesTrazabilidad :lote-id="selectedGraphLoteId" @close="selectedGraphLoteId = null" />
</div>
<template #fallback>
<USkeleton class="h-64" />
</template>
</ClientOnly>
</div>
</template>
</UTabs>
</div>
@@ -181,11 +222,44 @@
<!-- Modal: Ver Detalle de Operación -->
<UModal v-model:open="showOperacionDetailModal">
<template #content>
<OperacionesCard
v-if="selectedOperacion"
:operacion="selectedOperacion"
@close="closeOperacionDetailModal"
/>
<UCard>
<template #header>
<div class="flex justify-between items-center">
<div>
<h3 class="text-lg font-semibold">Detalle de Operación</h3>
<p class="text-sm text-gray-500" v-if="selectedOperacion">
{{ getTipoLabel(selectedOperacion.tipo) }} · {{ formatDate(selectedOperacion.fecha) }}
</p>
</div>
<UButton icon="i-heroicons-x-mark" variant="ghost" @click="closeOperacionDetailModal" />
</div>
</template>
<div v-if="selectedOperacion" class="space-y-3">
<div class="grid grid-cols-2 gap-3">
<div>
<p class="text-xs uppercase text-gray-500">ID</p>
<p class="font-mono text-sm">{{ selectedOperacion.id }}</p>
</div>
<div>
<p class="text-xs uppercase text-gray-500">Fecha</p>
<p class="text-sm">{{ formatDate(selectedOperacion.fecha) }}</p>
</div>
<div class="col-span-2">
<p class="text-xs uppercase text-gray-500">Tipo</p>
<UBadge color="blue" variant="subtle">{{ getTipoLabel(selectedOperacion.tipo) }}</UBadge>
</div>
</div>
<div>
<p class="text-xs uppercase text-gray-500 mb-1">Meta</p>
<div class="rounded-lg border border-gray-200 dark:border-slate-800 bg-gray-50 dark:bg-slate-900/60 p-2">
<pre class="text-xs text-gray-800 dark:text-slate-200 whitespace-pre-wrap overflow-x-auto">
{{ JSON.stringify(selectedOperacion.meta || {}, null, 2) }}
</pre>
</div>
</div>
</div>
<div v-else class="text-sm text-gray-500">No hay operación seleccionada.</div>
</UCard>
</template>
</UModal>
</UApp>
@@ -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<Lote | null>(null)
const selectedOperacion = ref<Operacion | null>(null)
const trazabilidadLoteId = ref<string | null>(null)
const graphLotes = ref<Lote[]>([])
const graphLoading = ref(false)
const graphError = ref<string | null>(null)
const selectedGraphLoteId = ref<string | null>(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)

View File

@@ -51,7 +51,7 @@
/>
</g>
<!-- Aristas -->
<!-- Aristas con codo (vertical + horizontal) -->
<g stroke="url(#edgeGradient)" stroke-width="2.5" stroke-linecap="round">
<template v-for="edge in edges" :key="`${edge.from}-${edge.to}`">
<!-- tramo vertical -->
@@ -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"
/>
<!-- tramo final vertical -->
<line
:x1="edge.toPos.x"
:y1="edge.midPos.y"
:x2="edge.toPos.x"
:y2="edge.toPos.y"
class="transition-opacity duration-200"
:opacity="0.9"