Files
seguidorDeLotes/nuxt4/app/app.vue
josedario87 ee3dffa38e
Some checks failed
build-and-deploy / build-and-deploy (push) Failing after 1m47s
Implementar sistema completo de trazabilidad de lotes
- Agregar PostgreSQL 16 con esquema completo
- Crear API endpoints para lotes y operaciones
- Implementar UI con Nuxt UI (tablas, formularios, trazabilidad)
- Agregar datos de ejemplo del flujo completo
- Documentar sistema en PLAN_TRAZABILIDAD.md
2025-11-21 18:39:04 -06:00

199 lines
5.7 KiB
Vue

<template>
<UApp>
<NuxtRouteAnnouncer />
<UNotifications />
<UContainer class="py-8">
<div class="space-y-6">
<!-- Header -->
<div class="flex justify-between items-center mb-8">
<div>
<h1 class="text-3xl font-bold">Seguidor de Lotes</h1>
<p class="text-gray-600 dark:text-gray-400">
Sistema de trazabilidad de café
</p>
</div>
<div class="flex items-center gap-3">
<AuthUserAvatar v-if="isAuthenticated" />
<AuthLogoutButton v-if="isAuthenticated" />
</div>
</div>
<!-- Contenido principal -->
<div v-if="isAuthenticated">
<!-- Navegación por Tabs -->
<UTabs v-model="selectedTab" :items="tabs" class="mb-6">
<!-- Tab: Lotes -->
<template #lotes>
<div class="py-4">
<LotesLotesTable
@create="showCreateLoteModal = true"
@view="handleViewLote"
@edit="handleEditLote"
@trazabilidad="handleViewTrazabilidad"
/>
</div>
</template>
<!-- Tab: Operaciones -->
<template #operaciones>
<div class="py-4">
<OperacionesOperacionesTable
@create="showCreateOperacionModal = true"
@view="handleViewOperacion"
/>
</div>
</template>
</UTabs>
</div>
<!-- Mensaje si no está autenticado -->
<UCard v-else class="text-center">
<div class="py-8">
<UIcon name="i-heroicons-shield-exclamation" class="w-16 h-16 mx-auto mb-4 text-gray-400" />
<h2 class="text-2xl font-semibold mb-2">No autenticado</h2>
<p class="text-gray-600 dark:text-gray-400">
Authentik Proxy Outpost debería redirigirte automáticamente.
</p>
</div>
</UCard>
</div>
</UContainer>
<!-- Modal: Crear/Editar Lote -->
<UModal v-model="showLoteFormModal">
<LotesLoteForm
:lote="selectedLote"
@cancel="closeLoteFormModal"
@success="handleLoteFormSuccess"
/>
</UModal>
<!-- Modal: Ver Detalle de Lote -->
<UModal v-model="showLoteDetailModal">
<LotesLoteCard
v-if="selectedLote"
:lote="selectedLote"
@edit="handleEditLoteFromDetail"
@trazabilidad="handleViewTrazabilidadFromDetail"
/>
</UModal>
<!-- Modal: Ver Trazabilidad -->
<UModal v-model="showTrazabilidadModal" :ui="{ width: 'max-w-4xl' }">
<LotesTrazabilidadTree
v-if="trazabilidadLoteId"
:lote-id="trazabilidadLoteId"
@close="showTrazabilidadModal = false"
/>
</UModal>
<!-- Modal: Crear Operación -->
<UModal v-model="showCreateOperacionModal" :ui="{ width: 'max-w-3xl' }">
<OperacionesOperacionForm
@cancel="showCreateOperacionModal = false"
@success="handleOperacionFormSuccess"
/>
</UModal>
</UApp>
</template>
<script setup lang="ts">
import type { Lote, Operacion } from '~/composables/useLotes'
const { isAuthenticated } = useAuthentik()
// Navegación
const selectedTab = ref(0)
const tabs = [
{ label: 'Lotes', icon: 'i-heroicons-cube', slot: 'lotes' },
{ label: 'Operaciones', icon: 'i-heroicons-beaker', slot: 'operaciones' },
]
// Estados de modales
const showLoteFormModal = ref(false)
const showLoteDetailModal = ref(false)
const showTrazabilidadModal = ref(false)
const showCreateLoteModal = ref(false)
const showCreateOperacionModal = ref(false)
// Estados de datos
const selectedLote = ref<Lote | null>(null)
const trazabilidadLoteId = ref<string | null>(null)
// Handlers para Lotes
const handleViewLote = (lote: Lote) => {
selectedLote.value = lote
showLoteDetailModal.value = true
}
const handleEditLote = (lote: Lote) => {
selectedLote.value = lote
showLoteFormModal.value = true
}
const handleEditLoteFromDetail = () => {
showLoteDetailModal.value = false
showLoteFormModal.value = true
}
const handleViewTrazabilidad = (lote: Lote) => {
trazabilidadLoteId.value = lote.id
showTrazabilidadModal.value = true
}
const handleViewTrazabilidadFromDetail = () => {
if (selectedLote.value) {
showLoteDetailModal.value = false
trazabilidadLoteId.value = selectedLote.value.id
showTrazabilidadModal.value = true
}
}
const closeLoteFormModal = () => {
showLoteFormModal.value = false
selectedLote.value = null
}
const handleLoteFormSuccess = () => {
closeLoteFormModal()
// La tabla se recargará automáticamente
}
// Handlers para Operaciones
const handleViewOperacion = (operacion: Operacion) => {
// TODO: Implementar vista de detalle de operación si es necesario
console.log('Ver operación:', operacion)
}
const handleOperacionFormSuccess = () => {
showCreateOperacionModal.value = false
// Las tablas se recargarán automáticamente
}
// Watch para crear lote
watch(showCreateLoteModal, (value) => {
if (value) {
selectedLote.value = null
showLoteFormModal.value = true
showCreateLoteModal.value = false
}
})
// Configurar meta tags para PWA
useHead({
title: 'Seguidor de Lotes - Trazabilidad de Café',
link: [
{ rel: 'manifest', href: '/manifest.webmanifest' },
{ rel: 'icon', type: 'image/svg+xml', href: '/icon.svg' },
{ rel: 'apple-touch-icon', href: '/apple-touch-icon.png' }
],
meta: [
{ name: 'theme-color', content: '#00DC82' },
{ name: 'mobile-web-app-capable', content: 'yes' },
{ name: 'apple-mobile-web-app-capable', content: 'yes' },
{ name: 'apple-mobile-web-app-status-bar-style', content: 'default' }
]
})
</script>