style: reskin NovaSpark en las 7 apps
Del look editorial (verde #1B4332, esquinas a 0, botones en mayusculas) al sistema NovaSpark: primario azul #2563EB, detalle naranja #F97316, fondo #F8FAFC con tarjetas blancas rounded-xl + shadow-sm, chips rounded-full semanticos, focus ring azul, transiciones 100-200ms. Incluye headers de tabla en los PDFs (cotizador y estado de cuenta).
This commit is contained in:
@@ -39,11 +39,11 @@ const esquemaCliente = z.object({
|
||||
type FormularioCliente = z.infer<typeof esquemaCliente>
|
||||
|
||||
const campoInputBase =
|
||||
'w-full border bg-white px-3 py-2.5 text-sm transition-colors duration-150 focus:border-neutral-900 focus:outline-none'
|
||||
const campoInput = `${campoInputBase} border-neutral-300`
|
||||
'w-full rounded-lg border bg-white px-3 py-2.5 text-sm transition-colors focus:border-accent focus:ring-2 focus:ring-accent/20 focus:outline-none'
|
||||
const campoInput = `${campoInputBase} border-[#D1D5DB]`
|
||||
const campoInputError = `${campoInputBase} border-red-400`
|
||||
|
||||
const etiquetaCampo = 'mb-2 block text-xs font-bold uppercase tracking-widest text-neutral-500'
|
||||
const etiquetaCampo = 'mb-2 block text-xs font-medium text-[#4B5563]'
|
||||
|
||||
export default function CotizadorPage() {
|
||||
const { unidades, cargando, error, usandoSeed } = useUnidades()
|
||||
@@ -254,18 +254,18 @@ export default function CotizadorPage() {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-white">
|
||||
<header className="border-b border-neutral-200 bg-white">
|
||||
<div className="min-h-screen bg-[#F8FAFC]">
|
||||
<header className="border-b border-[#E5E7EB] bg-white">
|
||||
<div className="mx-auto flex max-w-7xl flex-wrap items-baseline justify-between gap-2 px-4 py-6">
|
||||
<h1 className="text-lg tracking-widest text-neutral-900">
|
||||
<span className="font-bold">FRASA DESARROLLOS</span>{' '}
|
||||
<span className="font-light text-neutral-500">— Cotizador</span>
|
||||
<h1 className="text-lg text-[#111827]">
|
||||
<span className="font-bold">FRASA DESARROLLOS<span className="text-[#F97316]">.</span></span>{' '}
|
||||
<span className="font-normal text-[#64748B]">— Cotizador</span>
|
||||
</h1>
|
||||
<div className="text-right">
|
||||
<p className="text-xs font-bold tracking-widest text-neutral-500 uppercase capitalize">
|
||||
<p className="text-xs font-semibold uppercase tracking-wider text-[#64748B] capitalize">
|
||||
{formatoFechaLarga(new Date())}
|
||||
</p>
|
||||
<p className="mt-1 text-xs text-neutral-500">
|
||||
<p className="mt-1 text-xs text-[#64748B]">
|
||||
Vigencia: hasta el {formatoFechaLarga(calcularVigencia())}
|
||||
</p>
|
||||
</div>
|
||||
@@ -275,18 +275,18 @@ export default function CotizadorPage() {
|
||||
<main className="mx-auto grid max-w-7xl gap-x-8 gap-y-16 px-4 py-16 lg:grid-cols-[minmax(320px,380px)_1fr]">
|
||||
{/* Panel de configuración */}
|
||||
<aside className="space-y-16">
|
||||
<section className="space-y-8 border border-neutral-200 bg-white p-6">
|
||||
<section className="space-y-8 rounded-xl border border-[#E5E7EB] bg-white p-6 shadow-sm">
|
||||
{error && <p className="text-xs text-red-700">{error}</p>}
|
||||
{usandoSeed && !cargando && (
|
||||
<p className="text-xs text-amber-700">Datos de demostración (sin conexión al servidor)</p>
|
||||
)}
|
||||
{cargando ? (
|
||||
<p className="text-sm text-neutral-500">Cargando unidades…</p>
|
||||
<p className="text-sm text-[#64748B]">Cargando unidades…</p>
|
||||
) : (
|
||||
<>
|
||||
<div>
|
||||
<span className={etiquetaCampo}>Desarrollo</span>
|
||||
<div className="flex flex-wrap border border-neutral-300" role="group">
|
||||
<div className="flex flex-wrap overflow-hidden rounded-lg border border-[#D1D5DB]" role="group">
|
||||
{LISTA_DESARROLLOS.map((id) => {
|
||||
const activo = id === desarrollo
|
||||
const total = unidades.filter((u) => (u.desarrollo ?? 'DIAGONAL') === id).length
|
||||
@@ -299,21 +299,21 @@ export default function CotizadorPage() {
|
||||
type="button"
|
||||
aria-pressed={activo}
|
||||
onClick={() => setDesarrollo(id)}
|
||||
className={`px-4 py-2.5 text-xs font-bold tracking-widest uppercase transition-colors duration-150 ${
|
||||
className={`px-4 py-2.5 text-xs font-medium transition-colors duration-100 ${
|
||||
activo
|
||||
? 'bg-accent text-white'
|
||||
: 'bg-white text-neutral-600 hover:bg-neutral-100'
|
||||
: 'bg-white text-[#4B5563] hover:bg-[#F9FAFB]'
|
||||
}`}
|
||||
>
|
||||
{obtenerDesarrollo(id).nombre}
|
||||
<span className={`ml-1 font-normal ${activo ? 'text-white' : 'text-neutral-400'}`}>
|
||||
<span className={`ml-1 font-normal ${activo ? 'text-blue-100' : 'text-[#64748B]'}`}>
|
||||
{disponibles}/{total}
|
||||
</span>
|
||||
</button>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
<p className="mt-2 text-xs text-neutral-500">
|
||||
<p className="mt-2 text-xs text-[#64748B]">
|
||||
{unidadesDesarrollo.length} unidades · {disponiblesDesarrollo} disponibles
|
||||
</p>
|
||||
</div>
|
||||
@@ -330,7 +330,7 @@ export default function CotizadorPage() {
|
||||
{!params.precioUnico && (
|
||||
<div>
|
||||
<span className={etiquetaCampo}>Cocina y closet</span>
|
||||
<div className="inline-flex border border-neutral-300" role="group">
|
||||
<div className="inline-flex overflow-hidden rounded-lg border border-[#D1D5DB]" role="group">
|
||||
{(['SI', 'NO'] as const).map((opcion) => {
|
||||
const activo = (opcion === 'SI') === acabados
|
||||
return (
|
||||
@@ -339,10 +339,10 @@ export default function CotizadorPage() {
|
||||
type="button"
|
||||
aria-pressed={activo}
|
||||
onClick={() => setAcabados(opcion === 'SI')}
|
||||
className={`px-5 py-2.5 text-xs font-bold tracking-widest uppercase transition-colors duration-150 ${
|
||||
className={`px-5 py-2.5 text-xs font-medium transition-colors duration-100 ${
|
||||
activo
|
||||
? 'bg-accent text-white'
|
||||
: 'bg-white text-neutral-600 hover:bg-neutral-100'
|
||||
: 'bg-white text-[#4B5563] hover:bg-[#F9FAFB]'
|
||||
}`}
|
||||
>
|
||||
{opcion}
|
||||
@@ -372,7 +372,7 @@ export default function CotizadorPage() {
|
||||
{errorApartado ? (
|
||||
<p className="mt-1 text-xs text-red-700">{errorApartado}</p>
|
||||
) : (
|
||||
<p className="mt-1 text-xs text-neutral-500">{formatoMoneda(apartado)}</p>
|
||||
<p className="mt-1 text-xs text-[#64748B]">{formatoMoneda(apartado)}</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -418,11 +418,11 @@ export default function CotizadorPage() {
|
||||
</div>
|
||||
|
||||
{unidad && (
|
||||
<div className="border-t border-neutral-200 pt-8">
|
||||
<p className="text-xs font-bold tracking-widest text-neutral-500 uppercase">
|
||||
<div className="border-t border-[#E5E7EB] pt-8">
|
||||
<p className="text-xs font-semibold uppercase tracking-wider text-[#64748B]">
|
||||
Precio de lista
|
||||
</p>
|
||||
<p className="mt-2 text-3xl font-light text-neutral-900">
|
||||
<p className="mt-2 text-3xl font-semibold text-[#111827]">
|
||||
{formatoMoneda(precioLista)}
|
||||
</p>
|
||||
</div>
|
||||
@@ -432,7 +432,7 @@ export default function CotizadorPage() {
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleNueva}
|
||||
className="w-full border border-neutral-300 bg-white px-5 py-2.5 text-xs font-bold tracking-widest text-neutral-600 uppercase transition-colors duration-150 hover:bg-neutral-100"
|
||||
className="w-full rounded-lg border border-[#D1D5DB] bg-white px-5 py-2.5 text-sm font-medium text-[#374151] shadow-sm transition-all duration-100 ease-out hover:bg-[#F9FAFB]"
|
||||
>
|
||||
Nueva cotización
|
||||
</button>
|
||||
@@ -452,8 +452,8 @@ export default function CotizadorPage() {
|
||||
/>
|
||||
|
||||
{!unidad ? (
|
||||
<div className="mt-16 flex h-64 items-center justify-center border border-dashed border-neutral-300 bg-white">
|
||||
<p className="text-sm text-neutral-500">
|
||||
<div className="mt-16 flex h-64 items-center justify-center rounded-xl border border-dashed border-[#D1D5DB] bg-white">
|
||||
<p className="text-sm text-[#64748B]">
|
||||
Selecciona una unidad para ver las propuestas comerciales.
|
||||
</p>
|
||||
</div>
|
||||
@@ -485,7 +485,7 @@ export default function CotizadorPage() {
|
||||
id="propuesta-elegida"
|
||||
value={Math.min(propuestaIndex, propuestas.length - 1)}
|
||||
onChange={(e) => setPropuestaIndex(Number(e.target.value))}
|
||||
className="border border-neutral-300 bg-white px-3 py-2.5 text-sm transition-colors duration-150 focus:border-neutral-900 focus:outline-none"
|
||||
className="rounded-lg border border-[#D1D5DB] bg-white px-3 py-2.5 text-sm transition-colors focus:border-accent focus:ring-2 focus:ring-accent/20 focus:outline-none"
|
||||
>
|
||||
{propuestas.map((p, i) => (
|
||||
<option key={p.tipo} value={i}>
|
||||
@@ -498,7 +498,7 @@ export default function CotizadorPage() {
|
||||
type="button"
|
||||
onClick={handleCompartir}
|
||||
disabled={!puedeGenerarPdf}
|
||||
className="border border-accent bg-white px-5 py-2.5 text-xs font-bold tracking-widest text-accent uppercase transition-colors duration-150 hover:bg-accent hover:text-white disabled:cursor-not-allowed disabled:opacity-40"
|
||||
className="rounded-lg border border-accent bg-white px-5 py-2.5 text-sm font-medium text-accent shadow-sm transition-all duration-100 ease-out hover:bg-accent hover:text-white disabled:cursor-not-allowed disabled:opacity-40"
|
||||
>
|
||||
Compartir
|
||||
</button>
|
||||
@@ -506,14 +506,14 @@ export default function CotizadorPage() {
|
||||
type="button"
|
||||
onClick={handleGenerarPdf}
|
||||
disabled={!puedeGenerarPdf}
|
||||
className="bg-accent px-5 py-2.5 text-xs font-bold tracking-widest text-white uppercase transition-colors duration-150 hover:bg-accent-hover disabled:cursor-not-allowed disabled:opacity-40"
|
||||
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 disabled:cursor-not-allowed disabled:opacity-40"
|
||||
>
|
||||
{guardando ? 'Guardando…' : 'Guardar y generar PDF'}
|
||||
</button>
|
||||
</div>
|
||||
{aviso && <p className="mt-2 text-right text-xs text-neutral-500">{aviso}</p>}
|
||||
{aviso && <p className="mt-2 text-right text-xs text-[#64748B]">{aviso}</p>}
|
||||
{!puedeGenerarPdf && !guardando && (
|
||||
<p className="mt-2 text-right text-xs text-neutral-400">
|
||||
<p className="mt-2 text-right text-xs text-[#64748B]">
|
||||
Selecciona una unidad para guardar y generar el PDF.
|
||||
</p>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user