{"version":3,"file":"ChartBarHorizontal-S32gKj1M.cjs","sources":["../src/dashboard/widgets/ChartBarHorizontal.tsx"],"sourcesContent":["import React, { useMemo } from 'react';\nimport { WidgetDataGuard } from '../components/WidgetDataGuard';\nimport type { WidgetProps } from '../types';\nimport { useChartData } from '../hooks/useChartData';\nimport { useDashboardTranslation } from '../i18n';\nimport { colorsForCategories, MAX_CATEGORIES } from '../utils/chartColors';\nimport { groupTail, isOthersRow } from '../utils/groupTail';\nimport { formatCompact, formatExact, resolveCurrency } from '../utils/formatNumber';\nimport { readFormat } from '../utils/widgetConfig';\n\n/**\n * Barras horizontales — el caballo de batalla del tablero.\n *\n * <b>Por qué es el único que no usa recharts.</b> El diseño pide la etiqueta y el\n * valor en la MISMA LÍNEA, encima de la barra, sobre una pista. Eso no es un\n * `BarChart layout=\"vertical\"`: recharts pone las etiquetas en un eje Y lateral que\n * se come entre el 30 % y el 40 % del ancho y trunca cualquier nombre largo —\n * \"Repuestos y accesorios\" acaba en \"Repuestos y…\".\n *\n * Con la etiqueta arriba, la barra dispone de todo el ancho de la tarjeta y ninguna\n * categoría se trunca. Son cuarenta líneas de DOM contra un motor de gráficos\n * entero, y encima se lee mejor.\n *\n * <b>Es el default para distribución por categoría</b>, en reemplazo de la dona:\n * comparar longitudes es más preciso que comparar ángulos, y escala a ocho\n * categorías donde un anillo ya no.\n */\nconst ChartBarHorizontal: React.FC<WidgetProps> = ({ definition, period, preference }) => {\n  const { t } = useDashboardTranslation();\n  const { chartData, valueKeys, categoryField, data, loading, error } =\n    useChartData(definition, preference, period);\n\n  // `readFormat` valida contra la unión: un `format` que no existe cae a `undefined` en vez de\n  // colarse como una cadena cualquiera hasta el formateo.\n  const currency = readFormat(definition.config_schema) === 'currency' ? resolveCurrency().symbol : null;\n\n  const valueKey = valueKeys[0];\n\n  const { rows, total, max } = useMemo(() => {\n    if (!valueKey || chartData.length === 0) return { rows: [], total: 0, max: 0 };\n\n    // Orden descendente y cola agrupada: un ranking desordenado no es un ranking.\n    const grouped = groupTail(chartData, categoryField, valueKey, MAX_CATEGORIES);\n    const toNum = (row: Record<string, unknown>) => Number(row[valueKey]) || 0;\n\n    return {\n      rows: grouped,\n      total: grouped.reduce((acc, row) => acc + toNum(row), 0),\n      max: grouped.reduce((acc, row) => Math.max(acc, toNum(row)), 0),\n    };\n  }, [chartData, categoryField, valueKey]);\n\n  /** Los colores de las barras, resueltos una vez: sin colisiones dentro del gráfico. */\n  const colores = useMemo(\n    () => colorsForCategories(rows.map(row => String(row[categoryField] ?? ''))),\n    [rows, categoryField],\n  );\n\n  return (\n    <WidgetDataGuard loading={loading} error={error} data={data} shape=\"bars\">\n      <div className=\"w-full h-full overflow-y-auto px-4 pb-4 flex flex-col gap-3\">\n        {rows.map((row, index) => {\n          const rawCategory = String(row[categoryField] ?? '');\n          const isOthers = isOthersRow(rawCategory);\n          const value = Number(row[valueKey]) || 0;\n          const label = isOthers ? t('dashboard.chart.others') : rawCategory;\n\n          // El porcentaje va sobre el TOTAL (cola incluida): es la cifra que el\n          // usuario espera al leer \"62 %\" en una distribución.\n          const share = total > 0 ? (value / total) * 100 : 0;\n          // El ancho, en cambio, es relativo al MÁXIMO. Con anchos proporcionales al\n          // total, un reparto plano dejaría todas las barras diminutas.\n          const width = max > 0 ? (value / max) * 100 : 0;\n\n          const exact = formatExact(value, { currency });\n\n          return (\n            <div key={`${rawCategory}-${index}`} className=\"flex flex-col gap-1\" data-testid=\"hbar-row\">\n              <div className=\"flex items-baseline justify-between gap-3 text-[12px]\">\n                <span className=\"truncate\" style={{ color: 'var(--dash-text-primary)' }} title={label}>\n                  {label}\n                  {isOthers && typeof row.__groupedCount === 'number' && (\n                    <span style={{ color: 'var(--dash-text-muted)' }}>{` (${row.__groupedCount})`}</span>\n                  )}\n                </span>\n                <span\n                  className=\"flex-shrink-0\"\n                  style={{ color: 'var(--dash-text-secondary)', fontVariantNumeric: 'tabular-nums' }}\n                  title={exact}\n                >\n                  {formatCompact(value, { currency })}\n                  <span style={{ color: 'var(--dash-text-muted)' }}>{` · ${share.toFixed(0)} %`}</span>\n                </span>\n              </div>\n\n              {/* Pista + barra. La pista es lo que da la referencia de cuánto falta. */}\n              <div\n                className=\"w-full rounded-full overflow-hidden\"\n                style={{ height: 6, background: 'var(--dash-surface-page)' }}\n                role=\"img\"\n                aria-label={`${label}: ${exact} (${share.toFixed(0)} %)`}\n              >\n                <div\n                  className=\"h-full rounded-full transition-[width] duration-300\"\n                  style={{ width: `${width}%`, background: colores[rawCategory] }}\n                />\n              </div>\n            </div>\n          );\n        })}\n      </div>\n    </WidgetDataGuard>\n  );\n};\n\nexport default ChartBarHorizontal;\n"],"names":["ChartBarHorizontal","definition","period","preference","t","useDashboardTranslation","chartData","valueKeys","categoryField","data","loading","error","useChartData","currency","readFormat","resolveCurrency","valueKey","rows","total","max","useMemo","grouped","groupTail","MAX_CATEGORIES","toNum","row","acc","colores","colorsForCategories","jsx","WidgetDataGuard","index","rawCategory","isOthers","isOthersRow","value","label","share","width","exact","formatExact","jsxs","formatCompact"],"mappings":"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"}