Files
seguidorDeLotes/nuxt4/app/components/operaciones/Table.vue
josedario87 f551fa67ed
All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 1m5s
mejorando la trazabilidad
2025-11-22 01:18:37 -06:00

186 lines
5.3 KiB
Vue

<template>
<div class="space-y-4">
<div class="flex justify-between items-center">
<div>
<h2 class="text-2xl font-bold">Operaciones</h2>
<p class="text-gray-500">Historial de operaciones de proceso</p>
</div>
<UButton
icon="i-heroicons-plus"
label="Nueva Operación"
@click="$emit('create')"
/>
</div>
<div class="flex gap-2">
<USelect
v-model="filtroTipo"
:options="[{ value: '', label: 'Todos los tipos' }, ...TIPOS_OPERACION]"
placeholder="Filtrar por tipo"
class="w-64"
/>
<UButton
icon="i-heroicons-arrow-path"
label="Refrescar"
variant="outline"
@click="loadOperaciones"
/>
</div>
<!-- Mensaje de error -->
<UCard v-if="error" class="bg-red-50 dark:bg-red-950 border-red-500">
<div class="flex items-start gap-3">
<UIcon name="i-heroicons-exclamation-triangle" class="w-6 h-6 text-red-600 flex-shrink-0 mt-0.5" />
<div class="flex-1">
<h3 class="font-semibold text-red-600">Error cargando operaciones</h3>
<p class="text-sm text-red-700 dark:text-red-400 mt-1">{{ error }}</p>
<p class="text-xs text-red-600 dark:text-red-500 mt-2">Ver consola del navegador (F12) para más detalles</p>
</div>
</div>
</UCard>
<UCard>
<UTable
:data="operaciones"
:columns="columns"
:loading="loading"
:empty-state="{
icon: 'i-heroicons-inbox',
label: 'No hay operaciones registradas'
}"
>
<template #tipo-cell="{ row, getValue }">
<div class="flex items-center gap-2">
<UIcon :name="getTipoIcon(getValue())" class="w-4 h-4" />
<UBadge :color="getTipoColor(getValue())" variant="subtle">
{{ getTipoLabel(getValue()) }}
</UBadge>
</div>
</template>
<template #fecha-cell="{ getValue }">
{{ formatDate(getValue()) }}
</template>
<template #actions-cell="{ row }">
<div class="flex gap-1">
<UButton
icon="i-heroicons-eye"
size="xs"
variant="ghost"
@click="$emit('view', row.original as Operacion)"
/>
</div>
</template>
</UTable>
</UCard>
</div>
</template>
<script setup lang="ts">
import type { ColumnDef } from '@tanstack/vue-table'
import type { Operacion } from '~/composables/useLotes'
console.log('🟡 OperacionesTable: <script setup> ejecutándose')
const emit = defineEmits<{
create: []
view: [operacion: Operacion]
}>()
console.log('🟡 OperacionesTable: Llamando a useLotes()...')
const { fetchOperaciones, TIPOS_OPERACION } = useLotes()
console.log('🟡 OperacionesTable: useLotes() completado, TIPOS_OPERACION:', TIPOS_OPERACION?.length)
const operaciones = ref<Operacion[]>([])
const loading = ref(false)
const filtroTipo = ref('')
const error = ref<string | null>(null)
const columns: ColumnDef<Operacion>[] = [
{ accessorKey: 'tipo', id: 'tipo', header: 'Tipo de Operación' },
{ accessorKey: 'fecha', id: 'fecha', header: 'Fecha' },
{ accessorKey: 'lugar_id', id: 'lugar_id', header: 'Lugar' },
{ id: 'actions', header: 'Acciones' },
]
const loadOperaciones = async () => {
loading.value = true
error.value = null
try {
console.log('🟡 OperacionesTable: Iniciando carga de operaciones...')
operaciones.value = await fetchOperaciones({
tipo: filtroTipo.value || undefined,
})
console.log('🟡 OperacionesTable: Operaciones cargadas exitosamente:', operaciones.value.length)
} catch (err: any) {
const errorMsg = err?.message || String(err)
error.value = errorMsg
console.error('❌ OperacionesTable: Error cargando operaciones:', {
message: errorMsg,
stack: err?.stack,
error: err
})
} finally {
loading.value = false
}
}
const getTipoLabel = (tipo: string) => {
const found = TIPOS_OPERACION.find((t) => t.value === tipo)
return found?.label || tipo
}
const getTipoIcon = (tipo: string): string => {
const found = TIPOS_OPERACION.find((t) => t.value === tipo)
return found?.icon || 'i-heroicons-cube'
}
const getTipoColor = (tipo: string): string => {
const colorMap: Record<string, string> = {
ingreso: 'blue',
despulpado: 'green',
oreado: 'orange',
presecado: 'amber',
reposo: 'purple',
secado: 'emerald',
traslado: 'gray',
mezcla: 'yellow',
ajuste_merma: 'red',
ajuste_cantidad: 'orange',
ajuste_tipo: 'yellow',
}
return colorMap[tipo] || 'gray'
}
const formatDate = (dateString: string) => {
return new Date(dateString).toLocaleDateString('es-AR', {
day: '2-digit',
month: '2-digit',
year: 'numeric',
hour: '2-digit',
minute: '2-digit',
})
}
// Cargar operaciones al montar
onMounted(() => {
try {
console.log('🟡 OperacionesTable: Componente montado, cargando operaciones...')
loadOperaciones()
} catch (err: any) {
console.error('❌ OperacionesTable: Error en onMounted:', err)
}
})
// Recargar cuando cambia el filtro
watch(filtroTipo, () => {
try {
console.log('🟡 OperacionesTable: Filtro cambiado, recargando operaciones...')
loadOperaciones()
} catch (err: any) {
console.error('❌ OperacionesTable: Error en watch:', err)
}
})
</script>