mejorar de UI
All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 1m4s
All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 1m4s
This commit is contained in:
@@ -1,16 +1,29 @@
|
|||||||
<template>
|
<template>
|
||||||
<UCard>
|
<UCard class="bg-gradient-to-br from-white to-slate-50 dark:from-slate-900 dark:to-slate-950 shadow-lg">
|
||||||
<template #header>
|
<template #header>
|
||||||
<h3 class="text-lg font-semibold">
|
<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' }}
|
{{ lote ? 'Editar Lote' : 'Nuevo Lote' }}
|
||||||
</h3>
|
</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>
|
</template>
|
||||||
|
|
||||||
<UForm :state="formState" @submit="handleSubmit" class="space-y-4">
|
<UForm :state="formState" @submit="handleSubmit" class="space-y-6">
|
||||||
<UFormGroup label="Código" name="codigo" help="Opcional - Si no se especifica, se generará automáticamente">
|
<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
|
<UInput
|
||||||
v-model="formState.codigo"
|
v-model="formState.codigo"
|
||||||
placeholder="Ej: UVA-001, SEC-042"
|
placeholder="Ej: UVA-001, SEC-042"
|
||||||
|
icon="i-heroicons-hashtag"
|
||||||
/>
|
/>
|
||||||
</UFormGroup>
|
</UFormGroup>
|
||||||
|
|
||||||
@@ -18,35 +31,48 @@
|
|||||||
<USelect
|
<USelect
|
||||||
v-model="formState.tipo"
|
v-model="formState.tipo"
|
||||||
:options="TIPOS_LOTE"
|
:options="TIPOS_LOTE"
|
||||||
|
option-attribute="label"
|
||||||
|
value-attribute="value"
|
||||||
placeholder="Selecciona un tipo"
|
placeholder="Selecciona un tipo"
|
||||||
|
searchable
|
||||||
/>
|
/>
|
||||||
</UFormGroup>
|
</UFormGroup>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
<UFormGroup label="Cantidad (kg)" name="cantidad_kg">
|
<UFormGroup label="Cantidad (kg)" name="cantidad_kg">
|
||||||
<UInput
|
<UInput
|
||||||
v-model.number="formState.cantidad_kg"
|
v-model.number="formState.cantidad_kg"
|
||||||
type="number"
|
type="number"
|
||||||
step="0.01"
|
step="0.01"
|
||||||
placeholder="0.00"
|
placeholder="0.00"
|
||||||
|
icon="i-heroicons-scale"
|
||||||
/>
|
/>
|
||||||
</UFormGroup>
|
</UFormGroup>
|
||||||
|
|
||||||
<UFormGroup label="Lugar ID" name="lugar_id" help="Opcional - ID del lugar donde se encuentra">
|
<UFormGroup label="Lugar ID" name="lugar_id" help="Opcional · ID de ubicación">
|
||||||
<UInput
|
<UInput
|
||||||
v-model.number="formState.lugar_id"
|
v-model.number="formState.lugar_id"
|
||||||
type="number"
|
type="number"
|
||||||
placeholder="1"
|
placeholder="1"
|
||||||
|
icon="i-heroicons-map-pin"
|
||||||
/>
|
/>
|
||||||
</UFormGroup>
|
</UFormGroup>
|
||||||
|
</div>
|
||||||
|
|
||||||
<UFormGroup label="Información Adicional (JSON)" name="meta" help="Opcional - Datos adicionales en formato JSON">
|
<UFormGroup label="Información adicional (JSON)" name="meta" help="Ej: { "humedad": 12.5, "notas": "café especial" }">
|
||||||
<UTextarea
|
<UTextarea
|
||||||
v-model="metaText"
|
v-model="metaText"
|
||||||
placeholder='{"humedad": 12.5, "notas": "café especial"}'
|
placeholder='{"humedad": 12.5, "notas": "café especial"}'
|
||||||
rows="3"
|
rows="4"
|
||||||
|
class="font-mono"
|
||||||
/>
|
/>
|
||||||
</UFormGroup>
|
</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">
|
<div class="flex gap-2 justify-end">
|
||||||
<UButton
|
<UButton
|
||||||
type="button"
|
type="button"
|
||||||
@@ -56,10 +82,12 @@
|
|||||||
/>
|
/>
|
||||||
<UButton
|
<UButton
|
||||||
type="submit"
|
type="submit"
|
||||||
|
color="primary"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
:label="lote ? 'Actualizar' : 'Crear'"
|
:label="lote ? 'Actualizar' : 'Crear'"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</UForm>
|
</UForm>
|
||||||
</UCard>
|
</UCard>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -15,7 +15,10 @@
|
|||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
<USelect
|
<USelect
|
||||||
v-model="filtroTipo"
|
v-model="filtroTipo"
|
||||||
:options="[{ value: '', label: 'Todos los tipos' }, ...TIPOS_LOTE]"
|
:options="selectOptions"
|
||||||
|
option-attribute="label"
|
||||||
|
value-attribute="value"
|
||||||
|
searchable
|
||||||
placeholder="Filtrar por tipo"
|
placeholder="Filtrar por tipo"
|
||||||
class="w-64"
|
class="w-64"
|
||||||
/>
|
/>
|
||||||
@@ -118,6 +121,10 @@ console.log('🟢 LotesTable: useLotes() completado')
|
|||||||
const lotes = ref<Lote[]>([])
|
const lotes = ref<Lote[]>([])
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const filtroTipo = ref('')
|
const filtroTipo = ref('')
|
||||||
|
const selectOptions = computed(() => [
|
||||||
|
{ value: '', label: 'Todos los tipos' },
|
||||||
|
...TIPOS_LOTE,
|
||||||
|
])
|
||||||
const error = ref<string | null>(null)
|
const error = ref<string | null>(null)
|
||||||
|
|
||||||
const columns: ColumnDef<Lote>[] = [
|
const columns: ColumnDef<Lote>[] = [
|
||||||
|
|||||||
@@ -1,28 +1,44 @@
|
|||||||
<template>
|
<template>
|
||||||
<UCard>
|
<UCard class="bg-gradient-to-br from-white to-slate-50 dark:from-slate-900 dark:to-slate-950 shadow-lg">
|
||||||
<template #header>
|
<template #header>
|
||||||
<h3 class="text-lg font-semibold">Nueva Operación</h3>
|
<div class="flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<p class="text-xs uppercase tracking-wide text-gray-500 dark:text-gray-400">Flujo guiado</p>
|
||||||
|
<h3 class="text-xl font-semibold">Nueva Operación</h3>
|
||||||
|
<p class="text-sm text-gray-500 dark:text-gray-400">Define el tipo, selecciona inputs y crea los outputs.</p>
|
||||||
|
</div>
|
||||||
|
<UBadge color="indigo" variant="subtle">3 pasos</UBadge>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<div class="space-y-6">
|
<div class="space-y-6">
|
||||||
<!-- Paso 1: Tipo de Operación -->
|
<!-- Paso 1: Tipo de Operación -->
|
||||||
<div v-if="step === 1" class="space-y-4">
|
<div v-if="step === 1" class="space-y-4">
|
||||||
<h4 class="font-medium">Paso 1: Selecciona el tipo de operación</h4>
|
<div class="flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<h4 class="font-medium text-lg">1. Tipo de operación</h4>
|
||||||
|
<p class="text-sm text-gray-500 dark:text-gray-400">Selecciona el proceso que ejecutarás.</p>
|
||||||
|
</div>
|
||||||
|
<UBadge color="gray" variant="soft">Selecciona uno</UBadge>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="grid grid-cols-2 gap-3">
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-3">
|
||||||
<button
|
<button
|
||||||
v-for="tipo in TIPOS_OPERACION"
|
v-for="tipo in TIPOS_OPERACION"
|
||||||
:key="tipo.value"
|
:key="tipo.value"
|
||||||
@click="formState.tipo = tipo.value"
|
@click="formState.tipo = tipo.value"
|
||||||
class="p-4 border-2 rounded-lg transition-all hover:border-primary"
|
class="p-4 border-2 rounded-lg transition-all text-left hover:-translate-y-0.5 hover:shadow-md"
|
||||||
:class="{
|
:class="{
|
||||||
'border-primary bg-primary/10': formState.tipo === tipo.value,
|
'border-primary bg-primary/10 ring-2 ring-primary/30': formState.tipo === tipo.value,
|
||||||
'border-gray-200': formState.tipo !== tipo.value,
|
'border-gray-200 dark:border-slate-700': formState.tipo !== tipo.value,
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<div class="flex items-center gap-3">
|
<div class="flex items-center gap-3">
|
||||||
<UIcon :name="tipo.icon" class="w-6 h-6" />
|
<UIcon :name="tipo.icon" class="w-6 h-6" />
|
||||||
<span class="font-medium">{{ tipo.label }}</span>
|
<div>
|
||||||
|
<p class="font-semibold">{{ tipo.label }}</p>
|
||||||
|
<p class="text-xs text-gray-500 dark:text-gray-400">Seleccionar</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -35,6 +51,7 @@
|
|||||||
/>
|
/>
|
||||||
<UButton
|
<UButton
|
||||||
label="Siguiente"
|
label="Siguiente"
|
||||||
|
color="primary"
|
||||||
:disabled="!formState.tipo"
|
:disabled="!formState.tipo"
|
||||||
@click="step = 2"
|
@click="step = 2"
|
||||||
/>
|
/>
|
||||||
@@ -43,10 +60,15 @@
|
|||||||
|
|
||||||
<!-- Paso 2: Seleccionar Lotes de Entrada (Inputs) -->
|
<!-- Paso 2: Seleccionar Lotes de Entrada (Inputs) -->
|
||||||
<div v-else-if="step === 2" class="space-y-4">
|
<div v-else-if="step === 2" class="space-y-4">
|
||||||
<h4 class="font-medium">Paso 2: Selecciona los lotes de entrada (inputs)</h4>
|
<div class="flex items-center justify-between">
|
||||||
<p class="text-sm text-gray-500">
|
<div>
|
||||||
Estos son los lotes que se usarán en la operación de <strong>{{ getTipoLabel(formState.tipo) }}</strong>
|
<h4 class="font-medium text-lg">2. Lotes de entrada</h4>
|
||||||
|
<p class="text-sm text-gray-500 dark:text-gray-400">
|
||||||
|
Usa lotes para la operación de <strong>{{ getTipoLabel(formState.tipo) }}</strong>.
|
||||||
</p>
|
</p>
|
||||||
|
</div>
|
||||||
|
<UBadge color="blue" variant="soft">{{ formState.inputs.length }} seleccionados</UBadge>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div v-if="loadingLotes" class="text-center py-4">
|
<div v-if="loadingLotes" class="text-center py-4">
|
||||||
<UIcon name="i-heroicons-arrow-path" class="animate-spin w-6 h-6" />
|
<UIcon name="i-heroicons-arrow-path" class="animate-spin w-6 h-6" />
|
||||||
@@ -56,10 +78,10 @@
|
|||||||
<div
|
<div
|
||||||
v-for="lote in lotesDisponibles"
|
v-for="lote in lotesDisponibles"
|
||||||
:key="lote.id"
|
:key="lote.id"
|
||||||
class="p-3 border rounded-lg cursor-pointer transition-all hover:border-primary"
|
class="p-3 border rounded-lg cursor-pointer transition-all hover:-translate-y-0.5 hover:shadow-sm"
|
||||||
:class="{
|
:class="{
|
||||||
'border-primary bg-primary/10': isLoteSeleccionado(lote.id),
|
'border-primary bg-primary/10 ring-1 ring-primary/30': isLoteSeleccionado(lote.id),
|
||||||
'border-gray-200': !isLoteSeleccionado(lote.id),
|
'border-gray-200 dark:border-slate-700': !isLoteSeleccionado(lote.id),
|
||||||
}"
|
}"
|
||||||
@click="toggleLoteInput(lote)"
|
@click="toggleLoteInput(lote)"
|
||||||
>
|
>
|
||||||
@@ -69,7 +91,7 @@
|
|||||||
<UBadge :color="getTipoColor(lote.tipo)" variant="subtle" class="ml-2">
|
<UBadge :color="getTipoColor(lote.tipo)" variant="subtle" class="ml-2">
|
||||||
{{ getTipoLabel(lote.tipo) }}
|
{{ getTipoLabel(lote.tipo) }}
|
||||||
</UBadge>
|
</UBadge>
|
||||||
<span v-if="lote.cantidad_kg" class="ml-2 text-sm text-gray-600">
|
<span v-if="lote.cantidad_kg" class="ml-2 text-sm text-gray-600 dark:text-gray-400">
|
||||||
{{ lote.cantidad_kg.toLocaleString('es-AR') }} kg
|
{{ lote.cantidad_kg.toLocaleString('es-AR') }} kg
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -90,6 +112,7 @@
|
|||||||
/>
|
/>
|
||||||
<UButton
|
<UButton
|
||||||
label="Siguiente"
|
label="Siguiente"
|
||||||
|
color="primary"
|
||||||
:disabled="formState.inputs.length === 0"
|
:disabled="formState.inputs.length === 0"
|
||||||
@click="step = 3"
|
@click="step = 3"
|
||||||
/>
|
/>
|
||||||
@@ -98,16 +121,19 @@
|
|||||||
|
|
||||||
<!-- Paso 3: Definir Lotes de Salida (Outputs) -->
|
<!-- Paso 3: Definir Lotes de Salida (Outputs) -->
|
||||||
<div v-else-if="step === 3" class="space-y-4">
|
<div v-else-if="step === 3" class="space-y-4">
|
||||||
<h4 class="font-medium">Paso 3: Define los lotes de salida (outputs)</h4>
|
<div class="flex items-center justify-between">
|
||||||
<p class="text-sm text-gray-500">
|
<div>
|
||||||
Estos son los lotes nuevos que se crearán como resultado de la operación
|
<h4 class="font-medium text-lg">3. Lotes de salida</h4>
|
||||||
</p>
|
<p class="text-sm text-gray-500 dark:text-gray-400">Define los lotes resultantes de la operación.</p>
|
||||||
|
</div>
|
||||||
|
<UBadge color="emerald" variant="soft">{{ formState.outputs.length }} outputs</UBadge>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="space-y-3">
|
<div class="space-y-3">
|
||||||
<div
|
<div
|
||||||
v-for="(output, index) in formState.outputs"
|
v-for="(output, index) in formState.outputs"
|
||||||
:key="index"
|
:key="index"
|
||||||
class="p-4 border rounded-lg space-y-3"
|
class="p-4 border rounded-lg space-y-3 bg-white/80 dark:bg-slate-900/60"
|
||||||
>
|
>
|
||||||
<div class="flex justify-between items-center">
|
<div class="flex justify-between items-center">
|
||||||
<h5 class="font-medium">Lote de salida {{ index + 1 }}</h5>
|
<h5 class="font-medium">Lote de salida {{ index + 1 }}</h5>
|
||||||
@@ -120,17 +146,24 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="grid grid-cols-2 gap-3">
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-3">
|
||||||
<UFormGroup label="Código (opcional)">
|
<UFormGroup label="Código (opcional)">
|
||||||
<UInput v-model="output.codigo" placeholder="Ej: PRIM-001" />
|
<UInput v-model="output.codigo" placeholder="Ej: PRIM-001" icon="i-heroicons-hashtag" />
|
||||||
</UFormGroup>
|
</UFormGroup>
|
||||||
|
|
||||||
<UFormGroup label="Tipo" required>
|
<UFormGroup label="Tipo" required>
|
||||||
<USelect v-model="output.tipo" :options="TIPOS_LOTE" placeholder="Selecciona tipo" />
|
<USelect
|
||||||
|
v-model="output.tipo"
|
||||||
|
:options="TIPOS_LOTE"
|
||||||
|
option-attribute="label"
|
||||||
|
value-attribute="value"
|
||||||
|
placeholder="Selecciona tipo"
|
||||||
|
searchable
|
||||||
|
/>
|
||||||
</UFormGroup>
|
</UFormGroup>
|
||||||
|
|
||||||
<UFormGroup label="Cantidad (kg)">
|
<UFormGroup label="Cantidad (kg)">
|
||||||
<UInput v-model.number="output.cantidad_kg" type="number" step="0.01" placeholder="0.00" />
|
<UInput v-model.number="output.cantidad_kg" type="number" step="0.01" placeholder="0.00" icon="i-heroicons-scale" />
|
||||||
</UFormGroup>
|
</UFormGroup>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -152,6 +185,7 @@
|
|||||||
/>
|
/>
|
||||||
<UButton
|
<UButton
|
||||||
label="Crear Operación"
|
label="Crear Operación"
|
||||||
|
color="primary"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
:disabled="formState.outputs.length === 0 || !allOutputsValid"
|
:disabled="formState.outputs.length === 0 || !allOutputsValid"
|
||||||
@click="handleSubmit"
|
@click="handleSubmit"
|
||||||
|
|||||||
Reference in New Issue
Block a user