fix(dark-mode): dark mode completo + gráficas adaptadas
Dark mode fixes: - Cards secundarias: Mis capturas, Mis clientes, Actividades, Apartados por vencer, Equipo - Botones: Exportar, Descartar, Afluencia, Agendo, Forzar reconexión, Subir archivo - Config: ModoTrabajo, Usuarios, Sistema, RolesPermisos, ConfigPage - Cotizador: inputs, tabs, placeholder, botón compartir - Alta clientes: BotonOcrIne, FileUploader - Cobranza: toggle dashboard, modal abono - Dashboards: tabs Gráficas Recharts: - CSS variables para colores chart (grid, text, tooltip) - Helper isDark() + axisTextColor() + gridStroke() + tooltipProps() - DashboardVendedor: grid, axes, tooltips adaptados - TableroGeneral: grid, axes, tooltips (BarChart, Funnel, PieChart) - MiTablero: tooltip adaptado - Perfilamiento: grid, axes, tooltip 7 apps construidas limpias.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { Cell, Funnel, FunnelChart, LabelList, ResponsiveContainer, Tooltip } from 'recharts'
|
||||
import { tooltipProps } from '../lib/chart-theme'
|
||||
import KpiCard from './KpiCard'
|
||||
import { EstadoVacio, TarjetaGrafica, type AccionesTablero } from './comunes'
|
||||
import { funnelMisLeads, type ResumenMiDia } from '../lib/mi-tablero'
|
||||
@@ -95,7 +96,7 @@ export default function MiTablero({ alCambiarAcciones }: { alCambiarAcciones: (a
|
||||
<div className="h-72">
|
||||
<ResponsiveContainer width="100%" height="100%">
|
||||
<FunnelChart>
|
||||
<Tooltip />
|
||||
<Tooltip {...tooltipProps()} />
|
||||
<Funnel dataKey="valor" data={funnel} nameKey="etapa" isAnimationActive={false}>
|
||||
{funnel.map((etapa, i) => (
|
||||
<Cell key={etapa.etapa} fill={COLORES_FUNNEL[i % COLORES_FUNNEL.length]} />
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Bar, BarChart, CartesianGrid, ResponsiveContainer, Tooltip, XAxis, YAxis } from 'recharts'
|
||||
import { axisTextColor, gridStroke, tooltipProps } from '../lib/chart-theme'
|
||||
import { EstadoVacio, TituloSeccion } from './comunes'
|
||||
import { rebanadasDe, type ResumenReportes } from '../lib/reportes'
|
||||
|
||||
@@ -28,10 +29,10 @@ function TarjetaDimension({ titulo, datos }: { titulo: string; datos: Record<str
|
||||
<div className="h-48">
|
||||
<ResponsiveContainer width="100%" height="100%">
|
||||
<BarChart data={rebanadas} layout="vertical" margin={{ top: 4, right: 24, left: 24, bottom: 0 }}>
|
||||
<CartesianGrid strokeDasharray="3 3" horizontal={false} />
|
||||
<XAxis type="number" allowDecimals={false} tick={{ fontSize: 11 }} />
|
||||
<YAxis type="category" dataKey="nombre" width={90} tick={{ fontSize: 11 }} />
|
||||
<Tooltip />
|
||||
<CartesianGrid strokeDasharray="3 3" stroke={gridStroke()} horizontal={false} />
|
||||
<XAxis type="number" allowDecimals={false} tick={{ fontSize: 11, fill: axisTextColor() }} />
|
||||
<YAxis type="category" dataKey="nombre" width={90} tick={{ fontSize: 11, fill: axisTextColor() }} />
|
||||
<Tooltip {...tooltipProps()} />
|
||||
<Bar dataKey="valor" name="Leads" fill="#2563EB" isAnimationActive={false} />
|
||||
</BarChart>
|
||||
</ResponsiveContainer>
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
XAxis,
|
||||
YAxis,
|
||||
} from 'recharts'
|
||||
import { axisTextColor, gridStroke, tooltipProps } from '../lib/chart-theme'
|
||||
import KpiCard from './KpiCard'
|
||||
import Perfilamiento from './Perfilamiento'
|
||||
import SeccionEquipo from './SeccionEquipo'
|
||||
@@ -160,10 +161,10 @@ export default function TableroGeneral({
|
||||
<div className="h-72">
|
||||
<ResponsiveContainer width="100%" height="100%">
|
||||
<BarChart data={unidades} margin={{ top: 8, right: 8, left: -16, bottom: 0 }}>
|
||||
<CartesianGrid strokeDasharray="3 3" vertical={false} />
|
||||
<XAxis dataKey="desarrollo" tick={{ fontSize: 11 }} />
|
||||
<YAxis allowDecimals={false} tick={{ fontSize: 11 }} />
|
||||
<Tooltip />
|
||||
<CartesianGrid strokeDasharray="3 3" stroke={gridStroke()} vertical={false} />
|
||||
<XAxis dataKey="desarrollo" tick={{ fontSize: 11, fill: axisTextColor() }} />
|
||||
<YAxis allowDecimals={false} tick={{ fontSize: 11, fill: axisTextColor() }} />
|
||||
<Tooltip {...tooltipProps()} />
|
||||
<Legend wrapperStyle={{ fontSize: 11 }} />
|
||||
{Object.keys(COLORES_STATUS).map((status) => (
|
||||
<Bar key={status} dataKey={status} stackId="unidades" fill={COLORES_STATUS[status]} />
|
||||
@@ -183,10 +184,10 @@ export default function TableroGeneral({
|
||||
<div className="h-80">
|
||||
<ResponsiveContainer width="100%" height="100%">
|
||||
<BarChart data={pipeline} layout="vertical" margin={{ top: 8, right: 24, left: 24, bottom: 0 }}>
|
||||
<CartesianGrid strokeDasharray="3 3" horizontal={false} />
|
||||
<XAxis type="number" allowDecimals={false} tick={{ fontSize: 11 }} />
|
||||
<YAxis type="category" dataKey="etapa" width={90} tick={{ fontSize: 11 }} />
|
||||
<Tooltip />
|
||||
<CartesianGrid strokeDasharray="3 3" stroke={gridStroke()} horizontal={false} />
|
||||
<XAxis type="number" allowDecimals={false} tick={{ fontSize: 11, fill: axisTextColor() }} />
|
||||
<YAxis type="category" dataKey="etapa" width={90} tick={{ fontSize: 11, fill: axisTextColor() }} />
|
||||
<Tooltip {...tooltipProps()} />
|
||||
<Bar dataKey="valor" name="Unidades" fill="#2563EB" isAnimationActive={false} />
|
||||
</BarChart>
|
||||
</ResponsiveContainer>
|
||||
@@ -203,7 +204,7 @@ export default function TableroGeneral({
|
||||
<div className="h-72">
|
||||
<ResponsiveContainer width="100%" height="100%">
|
||||
<FunnelChart>
|
||||
<Tooltip />
|
||||
<Tooltip {...tooltipProps()} />
|
||||
<Funnel dataKey="valor" data={funnel} nameKey="etapa" isAnimationActive={false}>
|
||||
{funnel.map((etapa, i) => (
|
||||
<Cell key={etapa.etapa} fill={COLORES_FUNNEL[i % COLORES_FUNNEL.length]} />
|
||||
@@ -226,6 +227,7 @@ export default function TableroGeneral({
|
||||
<div className="h-72">
|
||||
<ResponsiveContainer width="100%" height="100%">
|
||||
<PieChart>
|
||||
<Tooltip {...tooltipProps()} />
|
||||
<Pie
|
||||
data={cobranza}
|
||||
dataKey="valor"
|
||||
@@ -240,7 +242,6 @@ export default function TableroGeneral({
|
||||
<Cell key={rebanada.nombre} fill={COLORES_COBRANZA[i % COLORES_COBRANZA.length]} />
|
||||
))}
|
||||
</Pie>
|
||||
<Tooltip />
|
||||
</PieChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
|
||||
@@ -8,6 +8,21 @@
|
||||
--color-accent-hover: #1D4ED8;
|
||||
}
|
||||
|
||||
:root {
|
||||
--chart-grid: #E5E7EB;
|
||||
--chart-text: #64748B;
|
||||
--chart-tooltip-bg: white;
|
||||
--chart-tooltip-border: #E5E7EB;
|
||||
--chart-tooltip-text: #111827;
|
||||
}
|
||||
.dark {
|
||||
--chart-grid: #334155;
|
||||
--chart-text: #94A3B8;
|
||||
--chart-tooltip-bg: #1E293B;
|
||||
--chart-tooltip-border: #334155;
|
||||
--chart-tooltip-text: #E2E8F0;
|
||||
}
|
||||
|
||||
@layer base {
|
||||
body {
|
||||
@apply bg-[#F8FAFC] text-[#111827] antialiased;
|
||||
|
||||
24
apps/dashboards/src/lib/chart-theme.ts
Normal file
24
apps/dashboards/src/lib/chart-theme.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* Helpers de tema para gráficas Recharts.
|
||||
*
|
||||
* Recharts renderiza SVG puro y no resuelve CSS custom properties en
|
||||
* atributos como `fill` / `stroke`, así que necesitamos funciones que
|
||||
* lean el estado dark-mode del documento y devuelvan los colores
|
||||
* apropiados como strings literales.
|
||||
*/
|
||||
|
||||
/** `true` cuando el `<html>` lleva la clase `dark` (Tailwind convention). */
|
||||
export const isDark = (): boolean =>
|
||||
typeof document !== 'undefined' && document.documentElement.classList.contains('dark')
|
||||
|
||||
/** Color de texto para los ejes (XAxis / YAxis tick fill). */
|
||||
export const axisTextColor = (): string => (isDark() ? '#94A3B8' : '#64748B')
|
||||
|
||||
/** Color de la cuadrícula (CartesianGrid stroke). */
|
||||
export const gridStroke = (): string => (isDark() ? '#334155' : '#E5E7EB')
|
||||
|
||||
/** Props comunes de Tooltip para aplicar estilo dark/light. */
|
||||
export const tooltipProps = (): Record<string, unknown> =>
|
||||
isDark()
|
||||
? { contentStyle: { backgroundColor: '#1E293B', borderColor: '#334155', color: '#E2E8F0', borderRadius: 8, fontSize: 12 } }
|
||||
: { contentStyle: { borderRadius: 8, fontSize: 12 } }
|
||||
@@ -64,7 +64,7 @@ export default function DashboardPage() {
|
||||
<DarkModeToggle />
|
||||
{permitidas.length > 1 && (
|
||||
<div
|
||||
className="inline-flex rounded-lg border border-[#E5E7EB] bg-[#F1F5F9] p-0.5 sm:p-1"
|
||||
className="inline-flex rounded-lg border border-[#E5E7EB] bg-[#F1F5F9] p-0.5 dark:border-[#334155] dark:bg-[#1E293B] sm:p-1"
|
||||
role="tablist"
|
||||
aria-label="Vista del tablero"
|
||||
>
|
||||
@@ -77,8 +77,8 @@ export default function DashboardPage() {
|
||||
onClick={() => setElegida(v.id)}
|
||||
className={
|
||||
vista === v.id
|
||||
? 'rounded-md bg-white px-2 py-1.5 text-[10px] font-medium text-[#111827] dark:text-[#E2E8F0] shadow-sm transition-all duration-100 ease-out sm:px-3 sm:text-xs'
|
||||
: 'rounded-md px-2 py-1.5 text-[10px] font-medium text-[#64748B] dark:text-[#94A3B8] transition-all duration-100 ease-out hover:text-[#111827] dark:text-[#E2E8F0] sm:px-3 sm:text-xs'
|
||||
? 'rounded-md bg-white px-2 py-1.5 text-[10px] font-medium text-[#111827] dark:text-[#E2E8F0] shadow-sm transition-all duration-100 ease-out sm:px-3 sm:text-xs dark:bg-[#1E293B]'
|
||||
: 'rounded-md px-2 py-1.5 text-[10px] font-medium text-[#64748B] dark:text-[#94A3B8] transition-all duration-100 ease-out hover:text-[#111827] dark:hover:text-[#E2E8F0] sm:px-3 sm:text-xs'
|
||||
}
|
||||
>
|
||||
{v.etiqueta}
|
||||
|
||||
Reference in New Issue
Block a user