style: rediseno de layout profesional en las 7 apps
Headers sticky con marca y acciones, contenedores max-w-[1280px], grids de contenido con rails laterales sticky (cotizador, mi-dia, detalle de cobranza), stepper vertical en el wizard de alta, toolbar+ tabla unificadas en cobranza, tabs como segmented control en dashboards, chat con barra de entrada fija y preguntas sugeridas, landing con navbar + hero + grid de modulos + footer.
This commit is contained in:
@@ -214,9 +214,9 @@ export default function DashboardPage() {
|
||||
)
|
||||
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
<div className="flex items-center justify-between">
|
||||
<h1 className="text-3xl font-light text-[#111827]">Módulo Cobranza — Planes de enganche</h1>
|
||||
<div className="space-y-6">
|
||||
<div className="flex flex-wrap items-center justify-between gap-4">
|
||||
<h1 className="text-xl font-semibold text-[#111827]">Módulo Cobranza — Planes de enganche</h1>
|
||||
<Link
|
||||
to="/nuevo"
|
||||
className="rounded-lg bg-accent px-5 py-2.5 text-sm font-medium text-white shadow-sm transition-all duration-100 ease-out hover:bg-accent-hover"
|
||||
@@ -292,8 +292,10 @@ export default function DashboardPage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Filtros */}
|
||||
<div className="flex flex-wrap items-center gap-4">
|
||||
{/* Tarjeta unificada: toolbar de filtros/búsqueda + tabla de planes */}
|
||||
<div className="overflow-hidden rounded-xl border border-[#E5E7EB] bg-white shadow-sm">
|
||||
{/* Filtros */}
|
||||
<div className="flex flex-wrap items-center gap-3 border-b border-[#E5E7EB] p-4 sm:gap-4 sm:px-5">
|
||||
{/* Toggle Mis planes / Todos (?mios=1 filtra en el API por el vendedor del token) */}
|
||||
<div className="flex rounded-lg border border-[#D1D5DB] bg-white" role="group" aria-label="Alcance de planes">
|
||||
<button
|
||||
@@ -358,10 +360,10 @@ export default function DashboardPage() {
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Tabla de planes */}
|
||||
<div className="overflow-x-auto rounded-xl border border-[#E5E7EB] bg-white shadow-sm">
|
||||
{/* Tabla de planes */}
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-left text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-[#E5E7EB] bg-[#F8FAFC] text-xs font-semibold uppercase tracking-wider text-[#64748B]">
|
||||
@@ -417,6 +419,7 @@ export default function DashboardPage() {
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -43,9 +43,9 @@ export default function HoyPage() {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<h1 className="text-3xl font-light text-[#111827]">Cobranza del día</h1>
|
||||
<h1 className="text-xl font-semibold text-[#111827]">Cobranza del día</h1>
|
||||
<p className="mt-1 text-sm text-[#64748B]">
|
||||
Pagos vencidos y vencimientos de los próximos 7 días, en todos los planes activos.
|
||||
</p>
|
||||
|
||||
@@ -88,8 +88,8 @@ export default function NuevoPlanPage() {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mx-auto max-w-3xl space-y-8">
|
||||
<h1 className="text-3xl font-light text-[#111827]">Nuevo plan de enganche</h1>
|
||||
<div className="mx-auto max-w-3xl space-y-6">
|
||||
<h1 className="text-xl font-semibold text-[#111827]">Nuevo plan de enganche</h1>
|
||||
|
||||
<form
|
||||
className="rounded-xl border border-[#E5E7EB] bg-white p-8 shadow-sm"
|
||||
|
||||
@@ -133,40 +133,24 @@ export default function PlanDetallePage() {
|
||||
}
|
||||
}
|
||||
|
||||
const avance = avancePlan(plan)
|
||||
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
<div className="flex flex-wrap items-start justify-between gap-6">
|
||||
<div>
|
||||
<Link to="/" className="text-sm text-accent hover:underline">
|
||||
← Volver al dashboard
|
||||
</Link>
|
||||
<h1 className="mt-2 text-3xl font-light text-[#111827]">{plan.clienteNombre}</h1>
|
||||
<p className="mt-1 text-sm text-[#64748B]">
|
||||
{plan.unidadCodigo} · {plan.proyecto} · Venta {plan.ventaRef || '—'} ·{' '}
|
||||
<span className="font-semibold text-[#111827]">{status}</span>
|
||||
{atraso > 0 && <span className="text-red-700"> · {atraso} días de atraso</span>}
|
||||
</p>
|
||||
<p className="mt-1 text-sm text-[#64748B]">
|
||||
Tel. {plan.clienteTelefono || '—'}
|
||||
{tasaMora > 0 && <span> · Mora {tasaMora}% mensual</span>}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex gap-3">
|
||||
<button
|
||||
onClick={() => descargar(plan)}
|
||||
className="rounded-lg border border-[#D1D5DB] bg-white px-5 py-2.5 text-sm font-medium text-[#374151] transition-all duration-100 ease-out hover:bg-[#F9FAFB]"
|
||||
>
|
||||
Estado de cuenta PDF
|
||||
</button>
|
||||
{status === 'ACTIVO' && saldo > 0 && (
|
||||
<button
|
||||
onClick={cancelar}
|
||||
className="rounded-lg border border-red-600 bg-white px-5 py-2.5 text-sm font-medium text-red-600 transition-all duration-100 ease-out hover:bg-red-600 hover:text-white"
|
||||
>
|
||||
Cancelar plan
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<Link to="/" className="text-sm text-accent hover:underline">
|
||||
← Volver al dashboard
|
||||
</Link>
|
||||
<h1 className="mt-2 text-xl font-semibold text-[#111827]">{plan.clienteNombre}</h1>
|
||||
<p className="mt-1 text-sm text-[#64748B]">
|
||||
{plan.unidadCodigo} · {plan.proyecto} · Venta {plan.ventaRef || '—'} ·{' '}
|
||||
<span className="font-semibold text-[#111827]">{status}</span>
|
||||
{atraso > 0 && <span className="text-red-700"> · {atraso} días de atraso</span>}
|
||||
</p>
|
||||
<p className="mt-1 text-sm text-[#64748B]">
|
||||
Tel. {plan.clienteTelefono || '—'}
|
||||
{tasaMora > 0 && <span> · Mora {tasaMora}% mensual</span>}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{offline && (
|
||||
@@ -179,21 +163,52 @@ export default function PlanDetallePage() {
|
||||
<div className="rounded-lg border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700">{error}</div>
|
||||
)}
|
||||
|
||||
{/* KPIs del plan */}
|
||||
<div className="grid grid-cols-2 gap-6 lg:grid-cols-5">
|
||||
<KpiCard titulo="Enganche total" valor={formatoMoneda(plan.engancheTotal)} />
|
||||
<KpiCard titulo="Pagado" valor={formatoMoneda(pagadoTotal(plan))} />
|
||||
<KpiCard titulo="Saldo" valor={formatoMoneda(saldo)} />
|
||||
<KpiCard titulo="% avance" valor={formatoPorcentaje(avancePlan(plan))} />
|
||||
<KpiCard titulo="Mensualidad" valor={formatoMoneda(plan.mensualidad)} detalle={`${plan.numPagos} pagos`} />
|
||||
{mora > 0 && (
|
||||
<KpiCard
|
||||
titulo="Mora acumulada"
|
||||
valor={<span className="text-red-700">{formatoMoneda(mora)}</span>}
|
||||
detalle={`Tasa ${tasaMora}% mensual`}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{/* En lg: columna principal (flex-1) + rail derecho sticky de ~340px */}
|
||||
<div className="space-y-6 lg:flex lg:items-start lg:gap-6 lg:space-y-0">
|
||||
{/* Columna principal */}
|
||||
<div className="min-w-0 flex-1 space-y-6">
|
||||
{/* Resumen del plan */}
|
||||
<section
|
||||
className="rounded-xl border border-[#E5E7EB] bg-white p-5 shadow-sm sm:p-6"
|
||||
aria-labelledby="titulo-resumen"
|
||||
>
|
||||
<h2 id="titulo-resumen" className="text-xs font-semibold uppercase tracking-wider text-[#64748B]">
|
||||
Resumen del plan
|
||||
</h2>
|
||||
<div className="mt-5 grid grid-cols-2 gap-4 sm:grid-cols-3">
|
||||
<KpiCard plano titulo="Enganche total" valor={formatoMoneda(plan.engancheTotal)} />
|
||||
<KpiCard plano titulo="Pagado" valor={formatoMoneda(pagadoTotal(plan))} />
|
||||
<KpiCard plano titulo="Saldo" valor={formatoMoneda(saldo)} />
|
||||
<KpiCard plano titulo="% avance" valor={formatoPorcentaje(avance)} />
|
||||
<KpiCard
|
||||
plano
|
||||
titulo="Mensualidad"
|
||||
valor={formatoMoneda(plan.mensualidad)}
|
||||
detalle={`${plan.numPagos} pagos`}
|
||||
/>
|
||||
{mora > 0 && (
|
||||
<KpiCard
|
||||
plano
|
||||
titulo="Mora acumulada"
|
||||
valor={<span className="text-red-700">{formatoMoneda(mora)}</span>}
|
||||
detalle={`Tasa ${tasaMora}% mensual`}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{/* Barra de avance */}
|
||||
<div className="mt-6">
|
||||
<div className="flex items-center justify-between text-xs">
|
||||
<span className="font-medium text-[#64748B]">Avance del plan</span>
|
||||
<span className="font-semibold text-[#111827]">{formatoPorcentaje(avance)}</span>
|
||||
</div>
|
||||
<div className="mt-2 h-2 rounded-full bg-[#E5E7EB]">
|
||||
<div
|
||||
className="h-full rounded-full bg-accent"
|
||||
style={{ width: `${Math.min(100, avance * 100)}%` }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Tabla de pagos */}
|
||||
<div className="overflow-x-auto rounded-xl border border-[#E5E7EB] bg-white shadow-sm">
|
||||
@@ -299,10 +314,38 @@ export default function PlanDetallePage() {
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Bitácora del plan */}
|
||||
<section className="rounded-xl border border-[#E5E7EB] bg-white p-8 shadow-sm" aria-labelledby="titulo-bitacora">
|
||||
{/* Rail derecho: acciones y bitácora */}
|
||||
<aside className="space-y-6 lg:sticky lg:top-20 lg:w-[340px] lg:shrink-0">
|
||||
<section
|
||||
className="rounded-xl border border-[#E5E7EB] bg-white p-5 shadow-sm"
|
||||
aria-labelledby="titulo-acciones"
|
||||
>
|
||||
<h2 id="titulo-acciones" className="text-xs font-semibold uppercase tracking-wider text-[#64748B]">
|
||||
Acciones
|
||||
</h2>
|
||||
<div className="mt-4 space-y-3">
|
||||
<button
|
||||
onClick={() => descargar(plan)}
|
||||
className="w-full rounded-lg border border-[#D1D5DB] bg-white px-5 py-2.5 text-sm font-medium text-[#374151] transition-all duration-100 ease-out hover:bg-[#F9FAFB]"
|
||||
>
|
||||
Estado de cuenta PDF
|
||||
</button>
|
||||
{status === 'ACTIVO' && saldo > 0 && (
|
||||
<button
|
||||
onClick={cancelar}
|
||||
className="w-full rounded-lg border border-red-600 bg-white px-5 py-2.5 text-sm font-medium text-red-600 transition-all duration-100 ease-out hover:bg-red-600 hover:text-white"
|
||||
>
|
||||
Cancelar plan
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Bitácora del plan */}
|
||||
<section className="rounded-xl border border-[#E5E7EB] bg-white p-5 shadow-sm" aria-labelledby="titulo-bitacora">
|
||||
<h2 id="titulo-bitacora" className="text-xs font-semibold uppercase tracking-wider text-[#64748B]">
|
||||
Bitácora
|
||||
</h2>
|
||||
@@ -329,7 +372,7 @@ export default function PlanDetallePage() {
|
||||
</ul>
|
||||
)}
|
||||
<form
|
||||
className="mt-8"
|
||||
className="mt-6"
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault()
|
||||
void guardarNota()
|
||||
@@ -360,7 +403,9 @@ export default function PlanDetallePage() {
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
</section>
|
||||
</aside>
|
||||
</div>
|
||||
|
||||
{status === 'CANCELADO' && (
|
||||
<button
|
||||
|
||||
Reference in New Issue
Block a user