Feat: Implementar SCAA Score y conversión bidireccional
All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 1m7s

CAMBIOS PRINCIPALES:

1. Nueva fórmula de SCAA Score:
   - S = 0.65625 × Σhᵢ + 52.75 - 2u - 4d
   - Donde hᵢ = puntaje afectivo (1-9)
   - u = tazas no uniformes, d = tazas defectuosas

2. Funciones de cálculo (catacion.ts):
   - calcularSumatoriaAfectiva(): suma de valores afectivos
   - calcularSCAA(): calcula SCAA Score con penalizaciones
   - scaaASumatoriaAfectiva(): conversión inversa (sin penalizaciones)
   - sumatoriaAfectivaASCAA(): conversión directa (sin penalizaciones)

3. FormularioMuestra.vue:
   - Mostrar ambos scores en el acordeón
   - "Sumatoria Afectiva" (entero, suma de afectivos)
   - "SCAA Score" (decimal, con 2 decimales)

4. ModalAsignacionRapida.vue:
   - Doble input: Sumatoria Afectiva + SCAA Score
   - Sincronización bidireccional automática
   - Al modificar Sumatoria → actualiza SCAA
   - Al modificar SCAA → calcula Sumatoria más cercana
   - Validación y redondeo para mantener enteros válidos
   - Rango: Sumatoria 9-90, SCAA 58.65-112.00

NOTAS:
- En asignación rápida no se toman en cuenta penalizaciones
- SCAA Score acepta decimales, Sumatoria solo enteros
- Múltiples SCAA Scores pueden apuntar al mismo entero
This commit is contained in:
2025-10-19 02:26:37 -06:00
parent e5275d223e
commit 58751467cf
3 changed files with 145 additions and 20 deletions

View File

@@ -801,16 +801,22 @@
</div>
</div>
<!-- Puntaje Final (solo lectura) -->
<div class="global-section p-4 cata-outline-box rounded-lg">
<div class="puntaje-final">
<!-- Puntajes (solo lectura) -->
<div class="global-section p-4 cata-outline-box rounded-lg space-y-4">
<!-- Sumatoria Afectiva -->
<div class="puntaje-item">
<div class="flex items-baseline justify-between">
<span class="text-sm cata-text opacity-75">Puntaje Final:</span>
<span class="text-3xl font-bold cata-text">{{ muestra.puntajeFinal }}</span>
<span class="text-sm cata-text opacity-75">Sumatoria Afectiva:</span>
<span class="text-2xl font-bold cata-text">{{ sumatoriaAfectiva }}</span>
</div>
</div>
<!-- SCAA Score -->
<div class="puntaje-item">
<div class="flex items-baseline justify-between">
<span class="text-sm cata-text opacity-75">SCAA Score:</span>
<span class="text-3xl font-bold cata-text">{{ scaaScore.toFixed(2) }}</span>
</div>
<p class="text-xs cata-text opacity-60 mt-1">
Suma de valores afectivos
</p>
</div>
</div>
</div>
@@ -820,7 +826,7 @@
<script setup lang="ts">
import type { Muestra, NotaSeleccionada, TipoDefecto, SensacionBoca, GustoPredominante } from '~/types/catacion'
import type { TabCatacion, Subcategoria } from '~/composables/useCatacion'
import { SENSACIONES_BOCA, GUSTOS_PREDOMINANTES, TIPOS_DEFECTOS } from '~/types/catacion'
import { SENSACIONES_BOCA, GUSTOS_PREDOMINANTES, TIPOS_DEFECTOS, calcularSumatoriaAfectiva, calcularSCAA } from '~/types/catacion'
interface FormularioMuestraProps {
/** Muestra a editar */
@@ -858,6 +864,10 @@ const sensacionesBoca = SENSACIONES_BOCA
const gustosPredominantes = GUSTOS_PREDOMINANTES
const tiposDefectos = TIPOS_DEFECTOS
// Cálculos de puntajes
const sumatoriaAfectiva = computed(() => calcularSumatoriaAfectiva(props.muestra))
const scaaScore = computed(() => calcularSCAA(props.muestra))
// Helpers para filtrado por subcategorías
const deberMostrarSeccion = (subcategorias: Subcategoria[]): boolean => {
// Si no hay filtros activos, mostrar todo

View File

@@ -22,15 +22,40 @@
<template #body>
<div class="space-y-4">
<!-- Paso 1: Ingresar puntaje total deseado -->
<div v-if="paso === 1">
<input
v-model.number="puntajeDeseado"
type="number"
:min="9"
:max="90"
placeholder="Puntaje Total (9-90)"
class="cata-input w-full px-3 py-2 rounded-md text-center text-lg"
/>
<div v-if="paso === 1" class="space-y-3">
<!-- Input de Sumatoria Afectiva -->
<div>
<label class="block text-xs cata-text opacity-75 mb-1 text-center">
Sumatoria Afectiva
</label>
<input
v-model.number="puntajeDeseado"
type="number"
:min="9"
:max="90"
step="1"
placeholder="9-90"
class="cata-input w-full px-3 py-2 rounded-md text-center text-lg"
@input="onSumatoriaChange"
/>
</div>
<!-- Input de SCAA Score -->
<div>
<label class="block text-xs cata-text opacity-75 mb-1 text-center">
SCAA Score
</label>
<input
v-model.number="scaaDeseado"
type="number"
:min="scaaMin"
:max="scaaMax"
step="0.01"
placeholder="58.65-112.00"
class="cata-input w-full px-3 py-2 rounded-md text-center text-lg"
@input="onScaaChange"
/>
</div>
</div>
<!-- Paso 2: Seleccionar categorías que sobresalen o palidecen -->
@@ -121,6 +146,7 @@
<script setup lang="ts">
import type { Muestra } from '~/types/catacion'
import { scaaASumatoriaAfectiva, sumatoriaAfectivaASCAA } from '~/types/catacion'
interface Props {
modelValue: boolean
@@ -155,17 +181,70 @@ const isOpen = computed({
// Estado del formulario
const paso = ref(1)
const puntajeDeseado = ref<number>(45)
const scaaDeseado = ref<number>(sumatoriaAfectivaASCAA(45))
const categoriasSeleccionadas = ref<string[]>([])
// Flag para evitar loops infinitos en la sincronización
const actualizandoInput = ref(false)
// Límites de SCAA Score (basados en sumatoria afectiva 9-90)
const scaaMin = sumatoriaAfectivaASCAA(9)
const scaaMax = sumatoriaAfectivaASCAA(90)
// Resetear estado cuando el modal se abre
watch(isOpen, (newValue) => {
if (newValue) {
paso.value = 1
puntajeDeseado.value = 45
scaaDeseado.value = sumatoriaAfectivaASCAA(45)
categoriasSeleccionadas.value = []
}
})
// Handler para cuando se modifica Sumatoria Afectiva
const onSumatoriaChange = () => {
if (actualizandoInput.value) return
actualizandoInput.value = true
// Validar rango
if (puntajeDeseado.value < 9) puntajeDeseado.value = 9
if (puntajeDeseado.value > 90) puntajeDeseado.value = 90
// Redondear a entero
puntajeDeseado.value = Math.round(puntajeDeseado.value)
// Actualizar SCAA Score
scaaDeseado.value = sumatoriaAfectivaASCAA(puntajeDeseado.value)
actualizandoInput.value = false
}
// Handler para cuando se modifica SCAA Score
const onScaaChange = () => {
if (actualizandoInput.value) return
actualizandoInput.value = true
// Validar rango
if (scaaDeseado.value < scaaMin) scaaDeseado.value = scaaMin
if (scaaDeseado.value > scaaMax) scaaDeseado.value = scaaMax
// Convertir a Sumatoria Afectiva
const sumatoriaCalculada = scaaASumatoriaAfectiva(scaaDeseado.value)
// Redondear al entero más cercano (sumatoria afectiva debe ser entero)
const sumatoriaRedondeada = Math.round(sumatoriaCalculada)
// Asegurar rango válido
puntajeDeseado.value = Math.max(9, Math.min(90, sumatoriaRedondeada))
// Recalcular SCAA Score con el valor redondeado
scaaDeseado.value = sumatoriaAfectivaASCAA(puntajeDeseado.value)
actualizandoInput.value = false
}
// Cálculos
const puntajeValido = computed(() => {
return puntajeDeseado.value >= 9 && puntajeDeseado.value <= 90