All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 1m4s
176 lines
4.9 KiB
Vue
176 lines
4.9 KiB
Vue
<template>
|
|
<UCard class="bg-gradient-to-br from-white to-slate-50 dark:from-slate-900 dark:to-slate-950 shadow-lg">
|
|
<template #header>
|
|
<div class="flex items-center justify-between">
|
|
<div>
|
|
<p class="text-xs uppercase tracking-wide text-gray-500 dark:text-gray-400">
|
|
{{ lote ? 'Edición' : 'Creación' }} · Lote
|
|
</p>
|
|
<h3 class="text-xl font-semibold">
|
|
{{ lote ? 'Editar Lote' : 'Nuevo Lote' }}
|
|
</h3>
|
|
<p class="text-sm text-gray-500 dark:text-gray-400">
|
|
Completa los campos para registrar el lote en la trazabilidad.
|
|
</p>
|
|
</div>
|
|
<UBadge color="blue" variant="subtle">{{ lote ? 'Edición' : 'Nuevo' }}</UBadge>
|
|
</div>
|
|
</template>
|
|
|
|
<UForm :state="formState" @submit="handleSubmit" class="space-y-6">
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
<UFormGroup label="Código" name="codigo" help="Opcional · Se generará si lo dejas vacío">
|
|
<UInput
|
|
v-model="formState.codigo"
|
|
placeholder="Ej: UVA-001, SEC-042"
|
|
icon="i-heroicons-hashtag"
|
|
/>
|
|
</UFormGroup>
|
|
|
|
<UFormGroup label="Tipo" name="tipo" required>
|
|
<USelect
|
|
v-model="formState.tipo"
|
|
:options="TIPOS_LOTE"
|
|
option-attribute="label"
|
|
value-attribute="value"
|
|
placeholder="Selecciona un tipo"
|
|
searchable
|
|
/>
|
|
</UFormGroup>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
<UFormGroup label="Cantidad (kg)" name="cantidad_kg">
|
|
<UInput
|
|
v-model.number="formState.cantidad_kg"
|
|
type="number"
|
|
step="0.01"
|
|
placeholder="0.00"
|
|
icon="i-heroicons-scale"
|
|
/>
|
|
</UFormGroup>
|
|
|
|
<UFormGroup label="Lugar ID" name="lugar_id" help="Opcional · ID de ubicación">
|
|
<UInput
|
|
v-model.number="formState.lugar_id"
|
|
type="number"
|
|
placeholder="1"
|
|
icon="i-heroicons-map-pin"
|
|
/>
|
|
</UFormGroup>
|
|
</div>
|
|
|
|
<UFormGroup label="Información adicional (JSON)" name="meta" help="Ej: { "humedad": 12.5, "notas": "café especial" }">
|
|
<UTextarea
|
|
v-model="metaText"
|
|
placeholder='{"humedad": 12.5, "notas": "café especial"}'
|
|
rows="4"
|
|
class="font-mono"
|
|
/>
|
|
</UFormGroup>
|
|
|
|
<div class="flex flex-col md:flex-row md:items-center justify-between gap-3 pt-2">
|
|
<p class="text-xs text-gray-500 dark:text-gray-400">
|
|
Los campos marcados como opcionales se pueden dejar vacíos.
|
|
</p>
|
|
<div class="flex gap-2 justify-end">
|
|
<UButton
|
|
type="button"
|
|
variant="outline"
|
|
label="Cancelar"
|
|
@click="$emit('cancel')"
|
|
/>
|
|
<UButton
|
|
type="submit"
|
|
color="primary"
|
|
:loading="loading"
|
|
:label="lote ? 'Actualizar' : 'Crear'"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</UForm>
|
|
</UCard>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import type { Lote } from '~/composables/useLotes'
|
|
|
|
const props = defineProps<{
|
|
lote?: Lote | null
|
|
}>()
|
|
|
|
const emit = defineEmits<{
|
|
cancel: []
|
|
success: [lote: Lote]
|
|
}>()
|
|
|
|
const { createLote, updateLote, TIPOS_LOTE } = useLotes()
|
|
|
|
const loading = ref(false)
|
|
const metaText = ref('')
|
|
|
|
const formState = ref({
|
|
codigo: '',
|
|
tipo: '',
|
|
cantidad_kg: undefined as number | undefined,
|
|
lugar_id: undefined as number | undefined,
|
|
})
|
|
|
|
// Si hay un lote para editar, cargar sus datos
|
|
watchEffect(() => {
|
|
if (props.lote) {
|
|
formState.value = {
|
|
codigo: props.lote.codigo || '',
|
|
tipo: props.lote.tipo,
|
|
cantidad_kg: props.lote.cantidad_kg || undefined,
|
|
lugar_id: props.lote.lugar_id || undefined,
|
|
}
|
|
metaText.value = props.lote.meta ? JSON.stringify(props.lote.meta, null, 2) : ''
|
|
}
|
|
})
|
|
|
|
const handleSubmit = async () => {
|
|
loading.value = true
|
|
try {
|
|
// Parsear meta si existe
|
|
let meta = null
|
|
if (metaText.value.trim()) {
|
|
try {
|
|
meta = JSON.parse(metaText.value)
|
|
} catch (err) {
|
|
useToast().add({
|
|
title: 'Error',
|
|
description: 'El formato JSON de información adicional no es válido',
|
|
color: 'red',
|
|
})
|
|
return
|
|
}
|
|
}
|
|
|
|
const data = {
|
|
codigo: formState.value.codigo || undefined,
|
|
tipo: formState.value.tipo,
|
|
cantidad_kg: formState.value.cantidad_kg,
|
|
lugar_id: formState.value.lugar_id,
|
|
meta,
|
|
}
|
|
|
|
let result: Lote | null = null
|
|
|
|
if (props.lote) {
|
|
// Actualizar lote existente
|
|
result = await updateLote(props.lote.id, data)
|
|
} else {
|
|
// Crear nuevo lote
|
|
result = await createLote(data)
|
|
}
|
|
|
|
if (result) {
|
|
emit('success', result)
|
|
}
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
</script>
|