Implementar sistema completo de trazabilidad de lotes
Some checks failed
build-and-deploy / build-and-deploy (push) Failing after 1m47s
Some checks failed
build-and-deploy / build-and-deploy (push) Failing after 1m47s
- 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
This commit is contained in:
@@ -6,85 +6,45 @@
|
||||
<UContainer class="py-8">
|
||||
<div class="space-y-6">
|
||||
<!-- Header -->
|
||||
<div class="text-center mb-8">
|
||||
<h1 class="text-4xl font-bold mb-2">Plantilla Nuxt + Authentik</h1>
|
||||
<p class="text-gray-600 dark:text-gray-400">
|
||||
Ejemplo de integración con Authentik Proxy Outpost
|
||||
</p>
|
||||
<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>
|
||||
|
||||
<!-- Componentes de autenticación -->
|
||||
<div v-if="isAuthenticated" class="grid gap-6 lg:grid-cols-2">
|
||||
<!-- Columna izquierda -->
|
||||
<div class="space-y-6">
|
||||
<!-- Avatar y datos básicos -->
|
||||
<AuthUserAvatar />
|
||||
|
||||
<!-- Botones de acción individuales -->
|
||||
<UCard class="w-full">
|
||||
<template #header>
|
||||
<h3 class="text-lg font-semibold">Acciones de Sesión</h3>
|
||||
</template>
|
||||
<div class="flex flex-wrap gap-3">
|
||||
<AuthSessionStatusButton />
|
||||
<AuthProfileButton />
|
||||
<AuthLogoutButton />
|
||||
<AuthLoginButton />
|
||||
<!-- 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>
|
||||
</UCard>
|
||||
</template>
|
||||
|
||||
<!-- Verificaciones Frontend/Backend -->
|
||||
<UCard class="w-full">
|
||||
<template #header>
|
||||
<div class="flex items-center gap-2">
|
||||
<UIcon name="i-heroicons-cpu-chip" class="w-5 h-5" />
|
||||
<h3 class="text-lg font-semibold">Verificación de Sistema</h3>
|
||||
</div>
|
||||
</template>
|
||||
<div class="flex flex-wrap gap-3">
|
||||
<AuthFrontendVerificationButton />
|
||||
<AuthBackendVerificationButton />
|
||||
<!-- Tab: Operaciones -->
|
||||
<template #operaciones>
|
||||
<div class="py-4">
|
||||
<OperacionesOperacionesTable
|
||||
@create="showCreateOperacionModal = true"
|
||||
@view="handleViewOperacion"
|
||||
/>
|
||||
</div>
|
||||
</UCard>
|
||||
</div>
|
||||
|
||||
<!-- Columna derecha -->
|
||||
<div class="space-y-6">
|
||||
<!-- Metadatos completos -->
|
||||
<AuthUserMetadata />
|
||||
|
||||
<!-- Verificaciones de Grupos Frontend -->
|
||||
<UCard class="w-full">
|
||||
<template #header>
|
||||
<div class="flex items-center gap-2">
|
||||
<UIcon name="i-heroicons-user-group" class="w-5 h-5 text-purple-500" />
|
||||
<h3 class="text-lg font-semibold">Grupos (Frontend)</h3>
|
||||
</div>
|
||||
</template>
|
||||
<div class="grid grid-cols-2 gap-3">
|
||||
<AuthCheckAuthentikAdminsButton />
|
||||
<AuthCheckGrupoPruebaButton />
|
||||
<AuthCheckLvl0Button />
|
||||
<AuthCheckPublicAccessButton />
|
||||
</div>
|
||||
</UCard>
|
||||
|
||||
<!-- Verificaciones de Grupos Backend -->
|
||||
<UCard class="w-full">
|
||||
<template #header>
|
||||
<div class="flex items-center gap-2">
|
||||
<UIcon name="i-heroicons-server-stack" class="w-5 h-5 text-orange-500" />
|
||||
<h3 class="text-lg font-semibold">Grupos (Backend)</h3>
|
||||
</div>
|
||||
</template>
|
||||
<div class="grid grid-cols-2 gap-3">
|
||||
<AuthCheckAuthentikAdminsButton :verify-backend="true" />
|
||||
<AuthCheckGrupoPruebaButton :verify-backend="true" />
|
||||
<AuthCheckLvl0Button :verify-backend="true" />
|
||||
<AuthCheckPublicAccessButton :verify-backend="true" />
|
||||
</div>
|
||||
</UCard>
|
||||
</div>
|
||||
</template>
|
||||
</UTabs>
|
||||
</div>
|
||||
|
||||
<!-- Mensaje si no está autenticado -->
|
||||
@@ -99,14 +59,130 @@
|
||||
</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' },
|
||||
|
||||
Reference in New Issue
Block a user