Feat: Agregar barra de subcategorías sticky debajo de tabs principales
All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 1m7s
All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 1m7s
This commit is contained in:
@@ -7,11 +7,21 @@ import { crearSesionVacia, calcularPuntajeFinal } from '~/types/catacion'
|
|||||||
|
|
||||||
export type TabCatacion = 'organoleptica' | 'descriptiva-afectiva' | 'defectos' | 'impresion-global'
|
export type TabCatacion = 'organoleptica' | 'descriptiva-afectiva' | 'defectos' | 'impresion-global'
|
||||||
|
|
||||||
|
// Subcategorías para Organoléptica
|
||||||
|
export type SubcategoriaOrganoleptica = 'fragancia-aroma' | 'sabor' | 'sensacion-boca' | 'gustos-predominantes'
|
||||||
|
|
||||||
|
// Subcategorías para Descriptiva/Afectiva
|
||||||
|
export type SubcategoriaDescriptivaAfectiva = 'descriptiva' | 'afectiva' | 'fragancia' | 'aroma' | 'sabor' | 'sabor-residual' | 'acidez' | 'dulzor' | 'sensacion-boca' | 'impresion-global'
|
||||||
|
|
||||||
|
// Tipo unión de todas las subcategorías
|
||||||
|
export type Subcategoria = SubcategoriaOrganoleptica | SubcategoriaDescriptivaAfectiva | null
|
||||||
|
|
||||||
export const useCatacion = () => {
|
export const useCatacion = () => {
|
||||||
const { sesionActiva, cargando, error, guardar, actualizar, eliminar } = useIndexedDB()
|
const { sesionActiva, cargando, error, guardar, actualizar, eliminar } = useIndexedDB()
|
||||||
|
|
||||||
// Estado de la UI
|
// Estado de la UI
|
||||||
const tabActiva = useState<TabCatacion>('tab-activa', () => 'organoleptica')
|
const tabActiva = useState<TabCatacion>('tab-activa', () => 'organoleptica')
|
||||||
|
const subcategoriaActiva = useState<Subcategoria>('subcategoria-activa', () => null)
|
||||||
const accordionAbierto = useState<string[]>('accordion-abierto', () => [])
|
const accordionAbierto = useState<string[]>('accordion-abierto', () => [])
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -293,6 +303,7 @@ export const useCatacion = () => {
|
|||||||
|
|
||||||
// Estado de la UI
|
// Estado de la UI
|
||||||
tabActiva,
|
tabActiva,
|
||||||
|
subcategoriaActiva,
|
||||||
accordionAbierto,
|
accordionAbierto,
|
||||||
|
|
||||||
// Estadísticas
|
// Estadísticas
|
||||||
|
|||||||
@@ -125,6 +125,33 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Subcategorías -->
|
||||||
|
<div v-if="subcategoriasDisponibles.length > 0" class="subcategorias-container border-t">
|
||||||
|
<div class="container mx-auto px-4">
|
||||||
|
<div class="flex overflow-x-auto gap-1 py-1.5">
|
||||||
|
<button
|
||||||
|
v-for="subcategoria in subcategoriasDisponibles"
|
||||||
|
:key="subcategoria.value"
|
||||||
|
:class="[
|
||||||
|
'subcategoria-chip',
|
||||||
|
{ 'subcategoria-chip-active': subcategoriaActiva === subcategoria.value },
|
||||||
|
]"
|
||||||
|
@click="subcategoriaActiva = subcategoria.value"
|
||||||
|
>
|
||||||
|
{{ subcategoria.label }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
v-if="subcategoriaActiva !== null"
|
||||||
|
class="subcategoria-chip subcategoria-chip-clear"
|
||||||
|
@click="subcategoriaActiva = null"
|
||||||
|
>
|
||||||
|
<UIcon name="i-lucide-x" class="w-3 h-3 inline mr-1" />
|
||||||
|
Limpiar
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Accordions de Muestras -->
|
<!-- Accordions de Muestras -->
|
||||||
@@ -180,7 +207,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { TabCatacion } from '~/composables/useCatacion'
|
import type { TabCatacion, Subcategoria } from '~/composables/useCatacion'
|
||||||
import type { Muestra } from '~/types/catacion'
|
import type { Muestra } from '~/types/catacion'
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -188,6 +215,7 @@ const {
|
|||||||
cargando,
|
cargando,
|
||||||
error,
|
error,
|
||||||
tabActiva,
|
tabActiva,
|
||||||
|
subcategoriaActiva,
|
||||||
accordionAbierto,
|
accordionAbierto,
|
||||||
exportarSesion,
|
exportarSesion,
|
||||||
eliminarSesionActual,
|
eliminarSesionActual,
|
||||||
@@ -231,6 +259,35 @@ const tabs = [
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
// Definición de subcategorías por tab
|
||||||
|
const subcategoriasPorTab = {
|
||||||
|
'organoleptica': [
|
||||||
|
{ value: 'fragancia-aroma' as Subcategoria, label: 'Fragancia/Aroma' },
|
||||||
|
{ value: 'sabor' as Subcategoria, label: 'Sabor' },
|
||||||
|
{ value: 'sensacion-boca' as Subcategoria, label: 'Sensación en la Boca' },
|
||||||
|
{ value: 'gustos-predominantes' as Subcategoria, label: 'Gustos Predominantes' },
|
||||||
|
],
|
||||||
|
'descriptiva-afectiva': [
|
||||||
|
{ value: 'descriptiva' as Subcategoria, label: 'Descriptiva' },
|
||||||
|
{ value: 'afectiva' as Subcategoria, label: 'Afectiva' },
|
||||||
|
{ value: 'fragancia' as Subcategoria, label: 'Fragancia' },
|
||||||
|
{ value: 'aroma' as Subcategoria, label: 'Aroma' },
|
||||||
|
{ value: 'sabor' as Subcategoria, label: 'Sabor' },
|
||||||
|
{ value: 'sabor-residual' as Subcategoria, label: 'Sabor Residual' },
|
||||||
|
{ value: 'acidez' as Subcategoria, label: 'Acidez' },
|
||||||
|
{ value: 'dulzor' as Subcategoria, label: 'Dulzor' },
|
||||||
|
{ value: 'sensacion-boca' as Subcategoria, label: 'Sensación en la Boca' },
|
||||||
|
{ value: 'impresion-global' as Subcategoria, label: 'Impresión Global' },
|
||||||
|
],
|
||||||
|
'defectos': [],
|
||||||
|
'impresion-global': [],
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subcategorías disponibles para la tab activa
|
||||||
|
const subcategoriasDisponibles = computed(() => {
|
||||||
|
return subcategoriasPorTab[tabActiva.value] || []
|
||||||
|
})
|
||||||
|
|
||||||
// Items del accordion
|
// Items del accordion
|
||||||
const accordionItems = computed(() => {
|
const accordionItems = computed(() => {
|
||||||
if (!sesionActiva.value) return []
|
if (!sesionActiva.value) return []
|
||||||
@@ -280,6 +337,7 @@ onMounted(async () => {
|
|||||||
// Cambiar tab
|
// Cambiar tab
|
||||||
const cambiarTab = (tab: TabCatacion) => {
|
const cambiarTab = (tab: TabCatacion) => {
|
||||||
tabActiva.value = tab
|
tabActiva.value = tab
|
||||||
|
subcategoriaActiva.value = null // Limpiar subcategoría al cambiar de tab
|
||||||
mostrarMenu.value = false
|
mostrarMenu.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -382,6 +440,50 @@ onMounted(() => {
|
|||||||
border-color: color-mix(in srgb, var(--cata-primary) 30%, transparent);
|
border-color: color-mix(in srgb, var(--cata-primary) 30%, transparent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Subcategorías */
|
||||||
|
.subcategorias-container {
|
||||||
|
background-color: var(--cata-bg);
|
||||||
|
border-color: color-mix(in srgb, var(--cata-primary) 20%, transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.subcategoria-chip {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
padding: 0.25rem 0.75rem;
|
||||||
|
border-radius: 9999px;
|
||||||
|
white-space: nowrap;
|
||||||
|
transition: all 0.2s;
|
||||||
|
background-color: color-mix(in srgb, var(--cata-primary) 10%, transparent);
|
||||||
|
color: var(--cata-text);
|
||||||
|
border: 1px solid color-mix(in srgb, var(--cata-primary) 30%, transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.subcategoria-chip:hover {
|
||||||
|
background-color: color-mix(in srgb, var(--cata-primary) 20%, transparent);
|
||||||
|
border-color: color-mix(in srgb, var(--cata-primary) 50%, transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.subcategoria-chip-active {
|
||||||
|
background-color: var(--cata-primary);
|
||||||
|
color: white;
|
||||||
|
border-color: var(--cata-primary);
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subcategoria-chip-active:hover {
|
||||||
|
background-color: color-mix(in srgb, var(--cata-primary) 90%, black);
|
||||||
|
}
|
||||||
|
|
||||||
|
.subcategoria-chip-clear {
|
||||||
|
background-color: color-mix(in srgb, var(--cata-primary) 5%, transparent);
|
||||||
|
border-color: color-mix(in srgb, var(--cata-primary) 20%, transparent);
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subcategoria-chip-clear:hover {
|
||||||
|
opacity: 1;
|
||||||
|
background-color: color-mix(in srgb, var(--cata-primary) 15%, transparent);
|
||||||
|
}
|
||||||
|
|
||||||
/* Accordion hover usando color personalizado */
|
/* Accordion hover usando color personalizado */
|
||||||
:deep(.accordion-trigger:hover) {
|
:deep(.accordion-trigger:hover) {
|
||||||
background-color: color-mix(in srgb, var(--cata-primary) 5%, transparent);
|
background-color: color-mix(in srgb, var(--cata-primary) 5%, transparent);
|
||||||
|
|||||||
Reference in New Issue
Block a user