Some checks failed
build-and-deploy / build-and-deploy (push) Failing after 16s
- Crear componente CataResumenMuestra para mostrar resumen de cada muestra en el header del accordion - Crear componente CataFormularioMuestra para mostrar formulario de edición de intensidades por tab - Corregir uso de slots en UAccordion para mostrar correctamente el contenido - Usar slot #default para customizar el header con CataResumenMuestra - Usar slots personalizados por muestra para el body con CataFormularioMuestra - Agregar estilos mejorados para los items del accordion
448 lines
17 KiB
Vue
448 lines
17 KiB
Vue
<template>
|
|
<div class="formulario-muestra cata-text p-4">
|
|
<!-- Tab: Fragancia/Aroma -->
|
|
<div v-if="tabActiva === 'fragancia-aroma'" class="tab-content">
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
<!-- Fragancia -->
|
|
<div class="campo-grupo">
|
|
<label class="text-sm font-semibold mb-3 block cata-text">Fragancia</label>
|
|
<div class="flex gap-3 items-center">
|
|
<div class="flex-1">
|
|
<label class="text-xs opacity-75 mb-1 block">Descriptiva (1-10)</label>
|
|
<input
|
|
v-model.number="fraganciaDescriptiva"
|
|
type="number"
|
|
min="1"
|
|
max="10"
|
|
class="cata-input w-full"
|
|
placeholder="1-10"
|
|
@change="actualizarFragancia"
|
|
>
|
|
</div>
|
|
<div class="flex-1">
|
|
<label class="text-xs opacity-75 mb-1 block">Afectiva (1-15)</label>
|
|
<input
|
|
v-model.number="fraganciaAfectiva"
|
|
type="number"
|
|
min="1"
|
|
max="15"
|
|
class="cata-input w-full"
|
|
placeholder="1-15"
|
|
@change="actualizarFragancia"
|
|
>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Aroma -->
|
|
<div class="campo-grupo">
|
|
<label class="text-sm font-semibold mb-3 block cata-text">Aroma</label>
|
|
<div class="flex gap-3 items-center">
|
|
<div class="flex-1">
|
|
<label class="text-xs opacity-75 mb-1 block">Descriptiva (1-10)</label>
|
|
<input
|
|
v-model.number="aromaDescriptiva"
|
|
type="number"
|
|
min="1"
|
|
max="10"
|
|
class="cata-input w-full"
|
|
placeholder="1-10"
|
|
@change="actualizarAroma"
|
|
>
|
|
</div>
|
|
<div class="flex-1">
|
|
<label class="text-xs opacity-75 mb-1 block">Afectiva (1-15)</label>
|
|
<input
|
|
v-model.number="aromaAfectiva"
|
|
type="number"
|
|
min="1"
|
|
max="15"
|
|
class="cata-input w-full"
|
|
placeholder="1-15"
|
|
@change="actualizarAroma"
|
|
>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Notas de Fragancia/Aroma -->
|
|
<div class="mt-6">
|
|
<label class="text-sm font-semibold mb-3 block cata-text">Notas de Fragancia/Aroma</label>
|
|
<div class="text-xs opacity-60 mb-2">Puedes escribir tus notas libremente</div>
|
|
<textarea
|
|
v-model="notasFraganciaAroma"
|
|
rows="3"
|
|
class="cata-input w-full"
|
|
placeholder="Describe las notas de fragancia y aroma..."
|
|
@blur="actualizarNotasFragancia"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Tab: Sabor -->
|
|
<div v-else-if="tabActiva === 'sabor'" class="tab-content">
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
<!-- Sabor -->
|
|
<div class="campo-grupo">
|
|
<label class="text-sm font-semibold mb-3 block cata-text">Sabor</label>
|
|
<div class="flex gap-3 items-center">
|
|
<div class="flex-1">
|
|
<label class="text-xs opacity-75 mb-1 block">Descriptiva (1-10)</label>
|
|
<input
|
|
v-model.number="saborDescriptiva"
|
|
type="number"
|
|
min="1"
|
|
max="10"
|
|
class="cata-input w-full"
|
|
placeholder="1-10"
|
|
@change="actualizarSabor"
|
|
>
|
|
</div>
|
|
<div class="flex-1">
|
|
<label class="text-xs opacity-75 mb-1 block">Afectiva (1-15)</label>
|
|
<input
|
|
v-model.number="saborAfectiva"
|
|
type="number"
|
|
min="1"
|
|
max="15"
|
|
class="cata-input w-full"
|
|
placeholder="1-15"
|
|
@change="actualizarSabor"
|
|
>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Sabor Residual -->
|
|
<div class="campo-grupo">
|
|
<label class="text-sm font-semibold mb-3 block cata-text">Sabor Residual</label>
|
|
<div class="flex gap-3 items-center">
|
|
<div class="flex-1">
|
|
<label class="text-xs opacity-75 mb-1 block">Descriptiva (1-10)</label>
|
|
<input
|
|
v-model.number="saborResidualDescriptiva"
|
|
type="number"
|
|
min="1"
|
|
max="10"
|
|
class="cata-input w-full"
|
|
placeholder="1-10"
|
|
@change="actualizarSaborResidual"
|
|
>
|
|
</div>
|
|
<div class="flex-1">
|
|
<label class="text-xs opacity-75 mb-1 block">Afectiva (1-15)</label>
|
|
<input
|
|
v-model.number="saborResidualAfectiva"
|
|
type="number"
|
|
min="1"
|
|
max="15"
|
|
class="cata-input w-full"
|
|
placeholder="1-15"
|
|
@change="actualizarSaborResidual"
|
|
>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Acidez -->
|
|
<div class="campo-grupo">
|
|
<label class="text-sm font-semibold mb-3 block cata-text">Acidez</label>
|
|
<div class="flex gap-3 items-center">
|
|
<div class="flex-1">
|
|
<label class="text-xs opacity-75 mb-1 block">Descriptiva (1-10)</label>
|
|
<input
|
|
v-model.number="acidezDescriptiva"
|
|
type="number"
|
|
min="1"
|
|
max="10"
|
|
class="cata-input w-full"
|
|
placeholder="1-10"
|
|
@change="actualizarAcidez"
|
|
>
|
|
</div>
|
|
<div class="flex-1">
|
|
<label class="text-xs opacity-75 mb-1 block">Afectiva (1-15)</label>
|
|
<input
|
|
v-model.number="acidezAfectiva"
|
|
type="number"
|
|
min="1"
|
|
max="15"
|
|
class="cata-input w-full"
|
|
placeholder="1-15"
|
|
@change="actualizarAcidez"
|
|
>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Dulzor -->
|
|
<div class="campo-grupo">
|
|
<label class="text-sm font-semibold mb-3 block cata-text">Dulzor</label>
|
|
<div class="flex gap-3 items-center">
|
|
<div class="flex-1">
|
|
<label class="text-xs opacity-75 mb-1 block">Descriptiva (1-10)</label>
|
|
<input
|
|
v-model.number="dulzorDescriptiva"
|
|
type="number"
|
|
min="1"
|
|
max="10"
|
|
class="cata-input w-full"
|
|
placeholder="1-10"
|
|
@change="actualizarDulzor"
|
|
>
|
|
</div>
|
|
<div class="flex-1">
|
|
<label class="text-xs opacity-75 mb-1 block">Afectiva (1-15)</label>
|
|
<input
|
|
v-model.number="dulzorAfectiva"
|
|
type="number"
|
|
min="1"
|
|
max="15"
|
|
class="cata-input w-full"
|
|
placeholder="1-15"
|
|
@change="actualizarDulzor"
|
|
>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Notas de Sabor -->
|
|
<div class="mt-6">
|
|
<label class="text-sm font-semibold mb-3 block cata-text">Notas de Sabor</label>
|
|
<div class="text-xs opacity-60 mb-2">Puedes escribir tus notas libremente</div>
|
|
<textarea
|
|
v-model="notasSabor"
|
|
rows="3"
|
|
class="cata-input w-full"
|
|
placeholder="Describe las notas de sabor..."
|
|
@blur="actualizarNotasSabor"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Tab: Impresión Global -->
|
|
<div v-else-if="tabActiva === 'impresion-global'" class="tab-content">
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
<!-- Sensación en Boca -->
|
|
<div class="campo-grupo">
|
|
<label class="text-sm font-semibold mb-3 block cata-text">Sensación en Boca</label>
|
|
<div class="flex gap-3 items-center">
|
|
<div class="flex-1">
|
|
<label class="text-xs opacity-75 mb-1 block">Descriptiva (1-10)</label>
|
|
<input
|
|
v-model.number="sensacionBocaDescriptiva"
|
|
type="number"
|
|
min="1"
|
|
max="10"
|
|
class="cata-input w-full"
|
|
placeholder="1-10"
|
|
@change="actualizarSensacionBoca"
|
|
>
|
|
</div>
|
|
<div class="flex-1">
|
|
<label class="text-xs opacity-75 mb-1 block">Afectiva (1-15)</label>
|
|
<input
|
|
v-model.number="sensacionBocaAfectiva"
|
|
type="number"
|
|
min="1"
|
|
max="15"
|
|
class="cata-input w-full"
|
|
placeholder="1-15"
|
|
@change="actualizarSensacionBoca"
|
|
>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Impresión Global -->
|
|
<div class="campo-grupo">
|
|
<label class="text-sm font-semibold mb-3 block cata-text">Impresión Global</label>
|
|
<div class="flex gap-3 items-center">
|
|
<div class="flex-1">
|
|
<label class="text-xs opacity-75 mb-1 block">Descriptiva (1-10)</label>
|
|
<input
|
|
v-model.number="impresionGlobalDescriptiva"
|
|
type="number"
|
|
min="1"
|
|
max="10"
|
|
class="cata-input w-full"
|
|
placeholder="1-10"
|
|
@change="actualizarImpresionGlobal"
|
|
>
|
|
</div>
|
|
<div class="flex-1">
|
|
<label class="text-xs opacity-75 mb-1 block">Afectiva (1-15)</label>
|
|
<input
|
|
v-model.number="impresionGlobalAfectiva"
|
|
type="number"
|
|
min="1"
|
|
max="15"
|
|
class="cata-input w-full"
|
|
placeholder="1-15"
|
|
@change="actualizarImpresionGlobal"
|
|
>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Notas Adicionales -->
|
|
<div class="mt-6">
|
|
<label class="text-sm font-semibold mb-3 block cata-text">Notas Adicionales</label>
|
|
<div class="text-xs opacity-60 mb-2">Observaciones generales sobre la muestra</div>
|
|
<textarea
|
|
v-model="otrasNotas"
|
|
rows="4"
|
|
class="cata-input w-full"
|
|
placeholder="Escribe tus observaciones generales..."
|
|
@blur="actualizarOtrasNotas"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import type { Muestra } from '~/types/catacion'
|
|
import type { TabCatacion } from '~/composables/useCatacion'
|
|
|
|
const props = defineProps<{
|
|
muestra: Muestra
|
|
tabActiva: TabCatacion
|
|
}>()
|
|
|
|
const { actualizarIntensidad, actualizarFraganciaAroma, actualizarSabor, actualizarOtrasNotas: actualizarOtrasNotasComposable } = useCatacion()
|
|
|
|
// Valores reactivos para Fragancia
|
|
const fraganciaDescriptiva = ref(props.muestra.intensidades.fragancia.descriptiva)
|
|
const fraganciaAfectiva = ref(props.muestra.intensidades.fragancia.afectiva)
|
|
const aromaDescriptiva = ref(props.muestra.intensidades.aroma.descriptiva)
|
|
const aromaAfectiva = ref(props.muestra.intensidades.aroma.afectiva)
|
|
|
|
// Valores reactivos para Sabor
|
|
const saborDescriptiva = ref(props.muestra.intensidades.sabor.descriptiva)
|
|
const saborAfectiva = ref(props.muestra.intensidades.sabor.afectiva)
|
|
const saborResidualDescriptiva = ref(props.muestra.intensidades.saborResidual.descriptiva)
|
|
const saborResidualAfectiva = ref(props.muestra.intensidades.saborResidual.afectiva)
|
|
const acidezDescriptiva = ref(props.muestra.intensidades.acidez.descriptiva)
|
|
const acidezAfectiva = ref(props.muestra.intensidades.acidez.afectiva)
|
|
const dulzorDescriptiva = ref(props.muestra.intensidades.dulzor.descriptiva)
|
|
const dulzorAfectiva = ref(props.muestra.intensidades.dulzor.afectiva)
|
|
|
|
// Valores reactivos para Impresión Global
|
|
const sensacionBocaDescriptiva = ref(props.muestra.intensidades.sensacionBoca.descriptiva)
|
|
const sensacionBocaAfectiva = ref(props.muestra.intensidades.sensacionBoca.afectiva)
|
|
const impresionGlobalDescriptiva = ref(props.muestra.intensidades.impresionGlobal.descriptiva)
|
|
const impresionGlobalAfectiva = ref(props.muestra.intensidades.impresionGlobal.afectiva)
|
|
|
|
// Notas de texto
|
|
const notasFraganciaAroma = ref(props.muestra.fraganciaAromaNotas.notaEspecifica || '')
|
|
const notasSabor = ref(props.muestra.saborNotas.notaEspecifica || '')
|
|
const otrasNotas = ref(props.muestra.otrasNotas)
|
|
|
|
// Actualizar cuando cambie la prop
|
|
watch(() => props.muestra, (nuevaMuestra) => {
|
|
fraganciaDescriptiva.value = nuevaMuestra.intensidades.fragancia.descriptiva
|
|
fraganciaAfectiva.value = nuevaMuestra.intensidades.fragancia.afectiva
|
|
aromaDescriptiva.value = nuevaMuestra.intensidades.aroma.descriptiva
|
|
aromaAfectiva.value = nuevaMuestra.intensidades.aroma.afectiva
|
|
|
|
saborDescriptiva.value = nuevaMuestra.intensidades.sabor.descriptiva
|
|
saborAfectiva.value = nuevaMuestra.intensidades.sabor.afectiva
|
|
saborResidualDescriptiva.value = nuevaMuestra.intensidades.saborResidual.descriptiva
|
|
saborResidualAfectiva.value = nuevaMuestra.intensidades.saborResidual.afectiva
|
|
acidezDescriptiva.value = nuevaMuestra.intensidades.acidez.descriptiva
|
|
acidezAfectiva.value = nuevaMuestra.intensidades.acidez.afectiva
|
|
dulzorDescriptiva.value = nuevaMuestra.intensidades.dulzor.descriptiva
|
|
dulzorAfectiva.value = nuevaMuestra.intensidades.dulzor.afectiva
|
|
|
|
sensacionBocaDescriptiva.value = nuevaMuestra.intensidades.sensacionBoca.descriptiva
|
|
sensacionBocaAfectiva.value = nuevaMuestra.intensidades.sensacionBoca.afectiva
|
|
impresionGlobalDescriptiva.value = nuevaMuestra.intensidades.impresionGlobal.descriptiva
|
|
impresionGlobalAfectiva.value = nuevaMuestra.intensidades.impresionGlobal.afectiva
|
|
|
|
notasFraganciaAroma.value = nuevaMuestra.fraganciaAromaNotas.notaEspecifica || ''
|
|
notasSabor.value = nuevaMuestra.saborNotas.notaEspecifica || ''
|
|
otrasNotas.value = nuevaMuestra.otrasNotas
|
|
}, { deep: true })
|
|
|
|
// Métodos de actualización
|
|
const actualizarFragancia = async () => {
|
|
await actualizarIntensidad(props.muestra.muestraId, 'fragancia', 'descriptiva', fraganciaDescriptiva.value)
|
|
await actualizarIntensidad(props.muestra.muestraId, 'fragancia', 'afectiva', fraganciaAfectiva.value)
|
|
}
|
|
|
|
const actualizarAroma = async () => {
|
|
await actualizarIntensidad(props.muestra.muestraId, 'aroma', 'descriptiva', aromaDescriptiva.value)
|
|
await actualizarIntensidad(props.muestra.muestraId, 'aroma', 'afectiva', aromaAfectiva.value)
|
|
}
|
|
|
|
const actualizarSabor = async () => {
|
|
await actualizarIntensidad(props.muestra.muestraId, 'sabor', 'descriptiva', saborDescriptiva.value)
|
|
await actualizarIntensidad(props.muestra.muestraId, 'sabor', 'afectiva', saborAfectiva.value)
|
|
}
|
|
|
|
const actualizarSaborResidual = async () => {
|
|
await actualizarIntensidad(props.muestra.muestraId, 'saborResidual', 'descriptiva', saborResidualDescriptiva.value)
|
|
await actualizarIntensidad(props.muestra.muestraId, 'saborResidual', 'afectiva', saborResidualAfectiva.value)
|
|
}
|
|
|
|
const actualizarAcidez = async () => {
|
|
await actualizarIntensidad(props.muestra.muestraId, 'acidez', 'descriptiva', acidezDescriptiva.value)
|
|
await actualizarIntensidad(props.muestra.muestraId, 'acidez', 'afectiva', acidezAfectiva.value)
|
|
}
|
|
|
|
const actualizarDulzor = async () => {
|
|
await actualizarIntensidad(props.muestra.muestraId, 'dulzor', 'descriptiva', dulzorDescriptiva.value)
|
|
await actualizarIntensidad(props.muestra.muestraId, 'dulzor', 'afectiva', dulzorAfectiva.value)
|
|
}
|
|
|
|
const actualizarSensacionBoca = async () => {
|
|
await actualizarIntensidad(props.muestra.muestraId, 'sensacionBoca', 'descriptiva', sensacionBocaDescriptiva.value)
|
|
await actualizarIntensidad(props.muestra.muestraId, 'sensacionBoca', 'afectiva', sensacionBocaAfectiva.value)
|
|
}
|
|
|
|
const actualizarImpresionGlobal = async () => {
|
|
await actualizarIntensidad(props.muestra.muestraId, 'impresionGlobal', 'descriptiva', impresionGlobalDescriptiva.value)
|
|
await actualizarIntensidad(props.muestra.muestraId, 'impresionGlobal', 'afectiva', impresionGlobalAfectiva.value)
|
|
}
|
|
|
|
const actualizarNotasFragancia = async () => {
|
|
await actualizarFraganciaAroma(props.muestra.muestraId, null, null, notasFraganciaAroma.value)
|
|
}
|
|
|
|
const actualizarNotasSabor = async () => {
|
|
await actualizarSabor(props.muestra.muestraId, null, null, notasSabor.value)
|
|
}
|
|
|
|
const actualizarOtrasNotas = async () => {
|
|
await actualizarOtrasNotasComposable(props.muestra.muestraId, otrasNotas.value)
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
.campo-grupo {
|
|
padding: 1rem;
|
|
border-radius: 0.5rem;
|
|
border: 1px solid color-mix(in srgb, var(--cata-primary) 20%, transparent);
|
|
background: color-mix(in srgb, var(--cata-primary) 5%, transparent);
|
|
}
|
|
|
|
.tab-content {
|
|
animation: fadeIn 0.3s ease-in-out;
|
|
}
|
|
|
|
@keyframes fadeIn {
|
|
from {
|
|
opacity: 0;
|
|
transform: translateY(-10px);
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
transform: translateY(0);
|
|
}
|
|
}
|
|
</style>
|