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:
@@ -30,18 +30,18 @@ export function ConfigPage({ onVolver }: Props) {
|
||||
<button
|
||||
type="button"
|
||||
onClick={onVolver}
|
||||
className="rounded-lg border border-[#D1D5DB] bg-white px-4 py-2 text-sm font-medium text-[#374151] shadow-sm transition-all duration-100 ease-out hover:bg-[#F9FAFB]"
|
||||
className="rounded-lg border border-[#D1D5DB] bg-white px-4 py-2 text-sm font-medium text-[#374151] shadow-sm transition-all duration-100 ease-out hover:bg-[#F9FAFB] dark:bg-[#0F172A] dark:border-[#334155] dark:text-[#CBD5E1]"
|
||||
>
|
||||
← Volver
|
||||
</button>
|
||||
<h1 className="text-2xl font-semibold tracking-tight text-[#111827] sm:text-3xl">
|
||||
<h1 className="text-2xl font-semibold tracking-tight text-[#111827] dark:text-[#E2E8F0] sm:text-3xl">
|
||||
Configuraciones
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
{/* Tabs */}
|
||||
<nav aria-label="Tabs de configuraciones" className="mt-8">
|
||||
<ul className="flex gap-1 rounded-xl border border-[#E5E7EB] bg-[#F9FAFB] p-1">
|
||||
<ul className="flex gap-1 rounded-xl border border-[#E5E7EB] bg-[#F9FAFB] p-1 dark:border-[#1E293B] dark:bg-[#1E293B]">
|
||||
{tabs.map((t) => (
|
||||
<li key={t.key}>
|
||||
<button
|
||||
@@ -49,8 +49,8 @@ export function ConfigPage({ onVolver }: Props) {
|
||||
onClick={() => setTab(t.key)}
|
||||
className={`rounded-lg px-4 py-2 text-sm font-medium transition-colors ${
|
||||
tab === t.key
|
||||
? 'bg-white text-[#111827] shadow-sm'
|
||||
: 'text-[#64748B] hover:text-[#374151]'
|
||||
? 'bg-white text-[#111827] shadow-sm dark:bg-[#1E293B] dark:text-[#E2E8F0]'
|
||||
: 'text-[#64748B] hover:text-[#374151] dark:text-[#94A3B8] dark:hover:text-[#CBD5E1]'
|
||||
}`}
|
||||
>
|
||||
{t.label}
|
||||
|
||||
@@ -75,22 +75,22 @@ export function ModoTrabajo() {
|
||||
|
||||
if (cargando) {
|
||||
return (
|
||||
<div className="rounded-xl border border-[#E5E7EB] bg-white p-6 shadow-sm">
|
||||
<p className="text-sm text-[#64748B]">Cargando estado del sistema…</p>
|
||||
<div className="rounded-xl border border-[#E5E7EB] bg-white p-6 shadow-sm dark:border-[#1E293B] dark:bg-[#1E293B]">
|
||||
<p className="text-sm text-[#64748B] dark:text-[#94A3B8]">Cargando estado del sistema…</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="rounded-xl border border-[#E5E7EB] bg-white p-6 shadow-sm">
|
||||
<h2 className="text-lg font-semibold text-[#111827]">Modo de trabajo</h2>
|
||||
<div className="rounded-xl border border-[#E5E7EB] bg-white p-6 shadow-sm dark:border-[#1E293B] dark:bg-[#1E293B]">
|
||||
<h2 className="text-lg font-semibold text-[#111827] dark:text-[#E2E8F0]">Modo de trabajo</h2>
|
||||
|
||||
{/* Indicador del estado de Monday */}
|
||||
<div className="mt-4 flex items-center gap-2">
|
||||
<span
|
||||
className={`h-2.5 w-2.5 rounded-full ${estado?.mondayOk ? 'bg-green-500' : 'bg-red-500'}`}
|
||||
/>
|
||||
<span className="text-sm text-[#64748B]">
|
||||
<span className="text-sm text-[#64748B] dark:text-[#94A3B8]">
|
||||
Monday: {estado?.mondayOk ? 'Conectado' : 'No disponible'}
|
||||
{estado && ` · ${estado.pendientesSync} pendiente(s) de sincronizar`}
|
||||
</span>
|
||||
@@ -116,10 +116,10 @@ export function ModoTrabajo() {
|
||||
/>
|
||||
</button>
|
||||
<div>
|
||||
<p className="text-sm font-medium text-[#111827]">
|
||||
<p className="text-sm font-medium text-[#111827] dark:text-[#E2E8F0]">
|
||||
{modoLocal ? 'Modo local (sin Monday)' : 'Vinculado a Monday'}
|
||||
</p>
|
||||
<p className="mt-0.5 text-xs text-[#64748B]">
|
||||
<p className="mt-0.5 text-xs text-[#64748B] dark:text-[#94A3B8]">
|
||||
{modoLocal
|
||||
? 'Las capturas se guardan localmente y se sincronizarán cuando Monday esté disponible.'
|
||||
: 'Los leads, actividades y contactos se sincronizan con Monday en tiempo real.'}
|
||||
@@ -133,7 +133,7 @@ export function ModoTrabajo() {
|
||||
type="button"
|
||||
onClick={() => void reconectar()}
|
||||
disabled={guardando}
|
||||
className="rounded-lg border border-[#D1D5DB] bg-white px-4 py-2 text-sm font-medium text-[#374151] shadow-sm transition-all duration-100 ease-out hover:bg-[#F9FAFB] disabled:opacity-50"
|
||||
className="rounded-lg border border-[#D1D5DB] bg-white px-4 py-2 text-sm font-medium text-[#374151] shadow-sm transition-all duration-100 ease-out hover:bg-[#F9FAFB] disabled:opacity-50 dark:bg-[#0F172A] dark:border-[#334155] dark:text-[#CBD5E1]"
|
||||
>
|
||||
{guardando ? 'Procesando…' : 'Forzar reconexión'}
|
||||
</button>
|
||||
|
||||
@@ -146,8 +146,8 @@ export function RolesPermisos() {
|
||||
|
||||
if (cargando) {
|
||||
return (
|
||||
<div className="rounded-xl border border-[#E5E7EB] bg-white p-6 shadow-sm">
|
||||
<p className="text-sm text-[#64748B]">Cargando configuración de roles…</p>
|
||||
<div className="rounded-xl border border-[#E5E7EB] bg-white p-6 shadow-sm dark:border-[#1E293B] dark:bg-[#1E293B]">
|
||||
<p className="text-sm text-[#64748B] dark:text-[#94A3B8]">Cargando configuración de roles…</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -168,7 +168,7 @@ export function RolesPermisos() {
|
||||
type="button"
|
||||
onClick={() => void restaurar()}
|
||||
disabled={guardando}
|
||||
className="rounded-lg border border-[#D1D5DB] bg-white px-4 py-2 text-sm font-medium text-[#374151] shadow-sm transition-all duration-100 ease-out hover:bg-[#F9FAFB] disabled:opacity-50"
|
||||
className="rounded-lg border border-[#D1D5DB] bg-white px-4 py-2 text-sm font-medium text-[#374151] shadow-sm transition-all duration-100 ease-out hover:bg-[#F9FAFB] disabled:opacity-50 dark:bg-[#0F172A] dark:border-[#334155] dark:text-[#CBD5E1]"
|
||||
>
|
||||
Restaurar valores por defecto
|
||||
</button>
|
||||
@@ -183,11 +183,11 @@ export function RolesPermisos() {
|
||||
)}
|
||||
|
||||
{/* Tabla de roles */}
|
||||
<div className="overflow-x-auto rounded-xl border border-[#E5E7EB] bg-white shadow-sm">
|
||||
<div className="overflow-x-auto rounded-xl border border-[#E5E7EB] bg-white shadow-sm dark:border-[#1E293B] dark:bg-[#1E293B]">
|
||||
<table className="w-full text-left text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-[#E5E7EB] bg-[#F9FAFB]">
|
||||
<th className="px-4 py-3 font-medium text-[#111827]">Rol</th>
|
||||
<tr className="border-b border-[#E5E7EB] bg-[#F9FAFB] dark:border-[#334155] dark:bg-[#1E293B]">
|
||||
<th className="px-4 py-3 font-medium text-[#111827] dark:text-[#E2E8F0]">Rol</th>
|
||||
{MODULOS_DISPONIBLES.map((m) => (
|
||||
<th key={m} className="px-3 py-3 text-center text-xs font-medium text-[#64748B]">
|
||||
{MODULO_LABELS[m] ?? m}
|
||||
@@ -205,8 +205,8 @@ export function RolesPermisos() {
|
||||
const estado = edicion[r.rol]
|
||||
if (!estado) return null
|
||||
return (
|
||||
<tr key={r.rol} className="border-b border-[#E5E7EB] last:border-0">
|
||||
<td className="whitespace-nowrap px-4 py-3 font-medium text-[#111827]">
|
||||
<tr key={r.rol} className="border-b border-[#E5E7EB] dark:border-[#334155] last:border-0">
|
||||
<td className="whitespace-nowrap px-4 py-3 font-medium text-[#111827] dark:text-[#E2E8F0]">
|
||||
{r.rol}
|
||||
</td>
|
||||
{MODULOS_DISPONIBLES.map((m) => (
|
||||
|
||||
@@ -19,7 +19,7 @@ const TTL_OPTIONS = [
|
||||
|
||||
const labelEstilo = 'block text-xs font-medium text-[#4B5563]'
|
||||
const inputEstilo =
|
||||
'mt-2 w-full rounded-lg border border-[#D1D5DB] bg-white px-3 py-2.5 text-sm text-[#111827] transition-colors focus:border-accent focus:outline-none focus:ring-2 focus:ring-accent/20'
|
||||
'mt-2 w-full rounded-lg border border-[#D1D5DB] bg-white px-3 py-2.5 text-sm text-[#111827] transition-colors focus:border-accent focus:outline-none focus:ring-2 focus:ring-accent/20 dark:bg-[#0F172A] dark:border-[#334155] dark:text-[#E2E8F0]'
|
||||
|
||||
export function Sistema() {
|
||||
const [form, setForm] = useState<Formulario>({
|
||||
@@ -75,15 +75,15 @@ export function Sistema() {
|
||||
|
||||
if (cargando) {
|
||||
return (
|
||||
<div className="rounded-xl border border-[#E5E7EB] bg-white p-6 shadow-sm">
|
||||
<p className="text-sm text-[#64748B]">Cargando configuraciones…</p>
|
||||
<div className="rounded-xl border border-[#E5E7EB] bg-white p-6 shadow-sm dark:border-[#1E293B] dark:bg-[#1E293B]">
|
||||
<p className="text-sm text-[#64748B] dark:text-[#94A3B8]">Cargando configuraciones…</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="rounded-xl border border-[#E5E7EB] bg-white p-6 shadow-sm">
|
||||
<h2 className="text-lg font-semibold text-[#111827]">Configuración del sistema</h2>
|
||||
<div className="rounded-xl border border-[#E5E7EB] bg-white p-6 shadow-sm dark:border-[#1E293B] dark:bg-[#1E293B]">
|
||||
<h2 className="text-lg font-semibold text-[#111827] dark:text-[#E2E8F0]">Configuración del sistema</h2>
|
||||
|
||||
<div className="mt-6 grid gap-6 sm:grid-cols-2">
|
||||
{/* Recordatorio inactividad */}
|
||||
|
||||
@@ -120,8 +120,8 @@ export function Usuarios() {
|
||||
|
||||
if (cargando) {
|
||||
return (
|
||||
<div className="rounded-xl border border-[#E5E7EB] bg-white p-6 shadow-sm">
|
||||
<p className="text-sm text-[#64748B]">Cargando usuarios…</p>
|
||||
<div className="rounded-xl border border-[#E5E7EB] bg-white p-6 shadow-sm dark:border-[#1E293B] dark:bg-[#1E293B]">
|
||||
<p className="text-sm text-[#64748B] dark:text-[#94A3B8]">Cargando usuarios…</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -133,8 +133,8 @@ export function Usuarios() {
|
||||
)}
|
||||
|
||||
{/* Búsqueda */}
|
||||
<div className="rounded-xl border border-[#E5E7EB] bg-white p-4 shadow-sm">
|
||||
<label htmlFor="buscar-usuario" className="block text-xs font-medium text-[#4B5563]">
|
||||
<div className="rounded-xl border border-[#E5E7EB] bg-white p-4 shadow-sm dark:border-[#1E293B] dark:bg-[#1E293B]">
|
||||
<label htmlFor="buscar-usuario" className="block text-xs font-medium text-[#4B5563] dark:text-[#CBD5E1]">
|
||||
Buscar usuario
|
||||
</label>
|
||||
<input
|
||||
@@ -143,20 +143,20 @@ export function Usuarios() {
|
||||
value={busqueda}
|
||||
onChange={(e) => setBusqueda(e.target.value)}
|
||||
placeholder="Nombre o usuario…"
|
||||
className="mt-2 w-full max-w-sm rounded-lg border border-[#D1D5DB] bg-white px-3 py-2 text-sm text-[#111827] transition-colors focus:border-accent focus:outline-none focus:ring-2 focus:ring-accent/20"
|
||||
className="mt-2 w-full max-w-sm rounded-lg border border-[#D1D5DB] bg-white px-3 py-2 text-sm text-[#111827] transition-colors focus:border-accent focus:outline-none focus:ring-2 focus:ring-accent/20 dark:bg-[#0F172A] dark:border-[#334155] dark:text-[#E2E8F0]"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Tabla */}
|
||||
<div className="overflow-x-auto rounded-xl border border-[#E5E7EB] bg-white shadow-sm">
|
||||
<div className="overflow-x-auto rounded-xl border border-[#E5E7EB] bg-white shadow-sm dark:border-[#1E293B] dark:bg-[#1E293B]">
|
||||
<table className="w-full text-left text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-[#E5E7EB] bg-[#F9FAFB]">
|
||||
<th className="px-4 py-3 font-medium text-[#111827]">Nombre</th>
|
||||
<th className="px-4 py-3 font-medium text-[#111827]">Usuario</th>
|
||||
<th className="px-4 py-3 font-medium text-[#111827]">Rol</th>
|
||||
<th className="px-4 py-3 font-medium text-[#111827]">Asignación</th>
|
||||
<th className="px-4 py-3 text-center font-medium text-[#111827]">Activo</th>
|
||||
<tr className="border-b border-[#E5E7EB] bg-[#F9FAFB] dark:border-[#334155] dark:bg-[#1E293B]">
|
||||
<th className="px-4 py-3 font-medium text-[#111827] dark:text-[#E2E8F0]">Nombre</th>
|
||||
<th className="px-4 py-3 font-medium text-[#111827] dark:text-[#E2E8F0]">Usuario</th>
|
||||
<th className="px-4 py-3 font-medium text-[#111827] dark:text-[#E2E8F0]">Rol</th>
|
||||
<th className="px-4 py-3 font-medium text-[#111827] dark:text-[#E2E8F0]">Asignación</th>
|
||||
<th className="px-4 py-3 text-center font-medium text-[#111827] dark:text-[#E2E8F0]">Activo</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -168,14 +168,14 @@ export function Usuarios() {
|
||||
</tr>
|
||||
) : (
|
||||
filtrados.map((u) => (
|
||||
<tr key={u.id} className="border-b border-[#E5E7EB] last:border-0">
|
||||
<td className="whitespace-nowrap px-4 py-3 text-[#111827]">{u.nombre}</td>
|
||||
<td className="whitespace-nowrap px-4 py-3 text-[#64748B]">{u.username}</td>
|
||||
<tr key={u.id} className="border-b border-[#E5E7EB] dark:border-[#334155] last:border-0">
|
||||
<td className="whitespace-nowrap px-4 py-3 text-[#111827] dark:text-[#E2E8F0]">{u.nombre}</td>
|
||||
<td className="whitespace-nowrap px-4 py-3 text-[#64748B] dark:text-[#94A3B8]">{u.username}</td>
|
||||
<td className="px-4 py-3">
|
||||
<select
|
||||
value={u.rol}
|
||||
onChange={(e) => void cambiarRol(u.id, e.target.value)}
|
||||
className="rounded-lg border border-[#D1D5DB] bg-white px-2 py-1.5 text-xs text-[#111827] focus:border-accent focus:outline-none focus:ring-2 focus:ring-accent/20"
|
||||
className="rounded-lg border border-[#D1D5DB] bg-white px-2 py-1.5 text-xs text-[#111827] focus:border-accent focus:outline-none focus:ring-2 focus:ring-accent/20 dark:bg-[#0F172A] dark:border-[#334155] dark:text-[#E2E8F0]"
|
||||
>
|
||||
{ROLES.map((r) => (
|
||||
<option key={r} value={r}>{ROL_LABELS[r]}</option>
|
||||
@@ -186,7 +186,7 @@ export function Usuarios() {
|
||||
<select
|
||||
value={u.asignacion ?? ''}
|
||||
onChange={(e) => void cambiarAsignacion(u.id, e.target.value)}
|
||||
className="rounded-lg border border-[#D1D5DB] bg-white px-2 py-1.5 text-xs text-[#111827] focus:border-accent focus:outline-none focus:ring-2 focus:ring-accent/20"
|
||||
className="rounded-lg border border-[#D1D5DB] bg-white px-2 py-1.5 text-xs text-[#111827] focus:border-accent focus:outline-none focus:ring-2 focus:ring-accent/20 dark:bg-[#0F172A] dark:border-[#334155] dark:text-[#E2E8F0]"
|
||||
>
|
||||
{ASIGNACIONES.map((a) => (
|
||||
<option key={a} value={a}>{ASIGNACION_LABELS[a]}</option>
|
||||
|
||||
Reference in New Issue
Block a user