Alinear panel y auth con la identidad B2B "Architectural Warmth"
Sustituye la paleta negra/azul B2C del panel del reformista por el verde de marca, neutros cálidos y titulares en Instrument Serif de la landing B2B. Añade tokens --color-primary-*, --color-stone-50 y --font-display al @theme. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -44,6 +44,15 @@
|
||||
|
||||
/* Fonts */
|
||||
--font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||
--font-display: 'Instrument Serif', Georgia, 'Times New Roman', serif;
|
||||
|
||||
/* Paleta de marca B2B "Architectural Warmth" — panel y área autenticada */
|
||||
--color-primary-50: #f4f8f5;
|
||||
--color-primary-100: #e8f0eb;
|
||||
--color-primary-500: #4d8a6d;
|
||||
--color-primary-700: #2f5c46;
|
||||
--color-primary-900: #1f3a2e;
|
||||
--color-stone-50: #f7f8f7;
|
||||
|
||||
/* Transitions */
|
||||
--transition-fast: 150ms ease;
|
||||
|
||||
@@ -16,7 +16,7 @@ export default function LoginPage() {
|
||||
>
|
||||
<form action={formAction} className="flex flex-col gap-5">
|
||||
<div>
|
||||
<h1 className="text-2xl font-black tracking-tight text-black">Entra en tu panel</h1>
|
||||
<h1 className="font-display text-3xl tracking-tight text-black">Entra en tu panel</h1>
|
||||
<p className="mt-1 text-sm text-gray-500">Gestiona tus leads y tu funnel.</p>
|
||||
</div>
|
||||
|
||||
@@ -27,7 +27,7 @@ export default function LoginPage() {
|
||||
type="email"
|
||||
required
|
||||
autoComplete="email"
|
||||
className="rounded-lg border border-gray-300 px-3 py-2.5 focus:border-gray-900 focus:outline-none focus:ring-1 focus:ring-gray-900"
|
||||
className="rounded-lg border border-gray-300 px-3 py-2.5 focus:border-primary-700 focus:outline-none focus:ring-1 focus:ring-primary-700"
|
||||
/>
|
||||
</label>
|
||||
<label className="flex flex-col gap-1 text-sm">
|
||||
@@ -37,19 +37,19 @@ export default function LoginPage() {
|
||||
type="password"
|
||||
required
|
||||
autoComplete="current-password"
|
||||
className="rounded-lg border border-gray-300 px-3 py-2.5 focus:border-gray-900 focus:outline-none focus:ring-1 focus:ring-gray-900"
|
||||
className="rounded-lg border border-gray-300 px-3 py-2.5 focus:border-primary-700 focus:outline-none focus:ring-1 focus:ring-primary-700"
|
||||
/>
|
||||
</label>
|
||||
|
||||
{error && <p className="text-sm text-red-600">{error}</p>}
|
||||
|
||||
<button type="submit" disabled={pending} className="btn btn-primary w-full disabled:opacity-60">
|
||||
<button type="submit" disabled={pending} className="btn bg-primary-700 text-white hover:bg-primary-900 w-full disabled:opacity-60">
|
||||
{pending ? 'Entrando…' : 'Entrar'}
|
||||
</button>
|
||||
|
||||
<p className="text-center text-sm text-gray-500">
|
||||
¿No tienes cuenta?{' '}
|
||||
<Link href="/signup" className="font-semibold text-black hover:underline">
|
||||
<Link href="/signup" className="font-semibold text-primary-700 hover:underline">
|
||||
Empieza gratis
|
||||
</Link>
|
||||
</p>
|
||||
|
||||
@@ -47,7 +47,7 @@ export default async function LeadDetailPage({ params }: { params: Promise<{ id:
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-6">
|
||||
<Link href="/panel" className="text-sm text-gray-500 hover:text-black w-fit">
|
||||
<Link href="/panel" className="text-sm text-gray-500 hover:text-primary-700 w-fit">
|
||||
← Volver a leads
|
||||
</Link>
|
||||
|
||||
@@ -55,7 +55,7 @@ export default async function LeadDetailPage({ params }: { params: Promise<{ id:
|
||||
<div className="bg-white border border-gray-200 rounded-xl p-5 flex flex-col gap-4">
|
||||
<div className="flex flex-wrap items-start justify-between gap-4">
|
||||
<div className="flex flex-col gap-1">
|
||||
<h1 className="text-2xl font-black tracking-tight text-black">{lead.nombre}</h1>
|
||||
<h1 className="font-display text-3xl tracking-tight text-black">{lead.nombre}</h1>
|
||||
<p className="text-sm text-gray-500">
|
||||
{lead.tipoReforma ? TIPO_LABEL[lead.tipoReforma] : 'Reforma'} ·{' '}
|
||||
{lead.provincia ?? '—'} · entró {formatFecha(lead.createdAt)}
|
||||
@@ -80,7 +80,7 @@ export default async function LeadDetailPage({ params }: { params: Promise<{ id:
|
||||
<p className="text-sm text-gray-500">
|
||||
Solicitada el {formatFecha(lead.testimonioSolicitadoAt)}. Comparte este enlace con el
|
||||
cliente para que deje su opinión. Cuando la envíe, la verás en{' '}
|
||||
<Link href="/panel/opiniones" className="text-black underline underline-offset-2">
|
||||
<Link href="/panel/opiniones" className="text-primary-700 underline underline-offset-2">
|
||||
Opiniones
|
||||
</Link>{' '}
|
||||
para aprobarla.
|
||||
@@ -96,7 +96,7 @@ export default async function LeadDetailPage({ params }: { params: Promise<{ id:
|
||||
<form action={solicitarOpinion.bind(null, lead.id)}>
|
||||
<button
|
||||
type="submit"
|
||||
className="inline-flex items-center gap-2 px-4 py-2 rounded-lg bg-black text-white text-sm font-semibold w-fit hover:bg-gray-800"
|
||||
className="inline-flex items-center gap-2 px-4 py-2 rounded-lg bg-primary-700 text-white text-sm font-semibold w-fit hover:bg-primary-900"
|
||||
>
|
||||
Solicitar opinión al cliente
|
||||
</button>
|
||||
@@ -254,7 +254,7 @@ export default async function LeadDetailPage({ params }: { params: Promise<{ id:
|
||||
<div className="flex flex-wrap items-center gap-3">
|
||||
<a
|
||||
href={`/panel/${lead.id}/presupuesto?download=1`}
|
||||
className="inline-flex items-center gap-2 px-4 py-2 rounded-lg bg-black text-white text-sm font-semibold w-fit hover:bg-gray-800"
|
||||
className="inline-flex items-center gap-2 px-4 py-2 rounded-lg bg-primary-700 text-white text-sm font-semibold w-fit hover:bg-primary-900"
|
||||
>
|
||||
Descargar PDF
|
||||
</a>
|
||||
@@ -262,7 +262,7 @@ export default async function LeadDetailPage({ params }: { params: Promise<{ id:
|
||||
href={`/panel/${lead.id}/presupuesto`}
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
className="text-sm font-medium text-gray-500 hover:text-black"
|
||||
className="text-sm font-medium text-gray-500 hover:text-primary-700"
|
||||
>
|
||||
Ver en el navegador
|
||||
</a>
|
||||
@@ -320,7 +320,7 @@ export default async function LeadDetailPage({ params }: { params: Promise<{ id:
|
||||
<form action={recalcularPresupuesto.bind(null, lead.id)}>
|
||||
<button
|
||||
type="submit"
|
||||
className="inline-flex items-center gap-2 px-4 py-2 rounded-lg bg-black text-white text-sm font-semibold w-fit hover:bg-gray-800"
|
||||
className="inline-flex items-center gap-2 px-4 py-2 rounded-lg bg-primary-700 text-white text-sm font-semibold w-fit hover:bg-primary-900"
|
||||
>
|
||||
Recalcular desde el catálogo
|
||||
</button>
|
||||
|
||||
@@ -18,7 +18,7 @@ export default async function EmpresaPage() {
|
||||
return (
|
||||
<div className="space-y-10 max-w-2xl">
|
||||
<div>
|
||||
<h1 className="text-2xl font-black tracking-tight text-black">Datos de empresa</h1>
|
||||
<h1 className="font-display text-3xl tracking-tight text-black">Datos de empresa</h1>
|
||||
<p className="text-sm text-gray-500 mt-1">
|
||||
Estos datos y el logo aparecen en la cabecera de los presupuestos en PDF que recibe el
|
||||
cliente. Manténlos al día.
|
||||
@@ -62,7 +62,7 @@ export default async function EmpresaPage() {
|
||||
href={funnelUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-black underline underline-offset-2 break-all"
|
||||
className="text-primary-700 underline underline-offset-2 break-all"
|
||||
>
|
||||
{funnelUrl}
|
||||
</a>
|
||||
@@ -159,7 +159,7 @@ export default async function EmpresaPage() {
|
||||
type="checkbox"
|
||||
name="aboutEnabled"
|
||||
defaultChecked={perfil.aboutEnabled}
|
||||
className="w-4 h-4 accent-black"
|
||||
className="w-4 h-4 accent-[#2f5c46]"
|
||||
/>
|
||||
<span className="text-gray-700">Mostrar el bloque “Quiénes somos” en mi funnel</span>
|
||||
</label>
|
||||
@@ -185,7 +185,7 @@ export default async function EmpresaPage() {
|
||||
/>
|
||||
</label>
|
||||
|
||||
<button className="md:col-span-2 justify-self-start inline-flex items-center justify-center rounded-lg bg-black px-4 py-2 text-sm font-semibold text-white transition hover:bg-gray-900">
|
||||
<button className="md:col-span-2 justify-self-start inline-flex items-center justify-center rounded-lg bg-primary-700 px-4 py-2 text-sm font-semibold text-white transition hover:bg-primary-900">
|
||||
Guardar datos
|
||||
</button>
|
||||
</form>
|
||||
|
||||
@@ -11,7 +11,7 @@ export default async function GaleriaPage() {
|
||||
return (
|
||||
<div className="space-y-8 max-w-3xl">
|
||||
<div>
|
||||
<h1 className="text-2xl font-black tracking-tight text-black">Galería de trabajos</h1>
|
||||
<h1 className="font-display text-3xl tracking-tight text-black">Galería de trabajos</h1>
|
||||
<p className="text-sm text-gray-500 mt-1">
|
||||
Sube fotos de reformas que ya has hecho. Aparecen en tu funnel para dar confianza al
|
||||
cliente antes de pedir presupuesto.
|
||||
|
||||
@@ -27,14 +27,14 @@ export default async function PanelLayout({ children }: { children: React.ReactN
|
||||
const nombreEmpresa = tenant?.nombreEmpresa ?? 'Reformix';
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50">
|
||||
<div className="min-h-screen bg-stone-50">
|
||||
<header className="sticky top-0 z-20 bg-white border-b border-gray-200">
|
||||
<div className="relative max-w-6xl mx-auto px-6 h-16 flex items-center justify-between">
|
||||
<Link href="/panel" className="flex items-center gap-2 min-w-0">
|
||||
<span className="inline-flex shrink-0 items-center justify-center w-8 h-8 rounded-lg bg-black text-white font-black italic text-lg leading-none">
|
||||
<span className="inline-flex shrink-0 items-center justify-center w-8 h-8 rounded-lg bg-primary-700 text-white font-black italic text-lg leading-none">
|
||||
R
|
||||
</span>
|
||||
<span className="font-black tracking-tight text-black">Reformix</span>
|
||||
<span className="font-bold tracking-tight text-black">Reformix</span>
|
||||
<span className="hidden sm:inline text-gray-300">/</span>
|
||||
<span className="hidden sm:inline text-sm font-medium text-gray-600 truncate">
|
||||
{nombreEmpresa}
|
||||
|
||||
@@ -33,7 +33,7 @@ export default async function OpinionesPage() {
|
||||
return (
|
||||
<div className="space-y-8 max-w-3xl">
|
||||
<div>
|
||||
<h1 className="text-2xl font-black tracking-tight text-black">Opiniones</h1>
|
||||
<h1 className="font-display text-3xl tracking-tight text-black">Opiniones</h1>
|
||||
<p className="text-sm text-gray-500 mt-1">
|
||||
Las opiniones que te dejan tus clientes. Aprueba las que quieras mostrar en tu funnel; solo
|
||||
las publicadas aparecen en tu página.
|
||||
|
||||
@@ -50,7 +50,7 @@ export default async function PanelPage({
|
||||
return (
|
||||
<div className="flex flex-col gap-6">
|
||||
<div className="flex flex-col gap-1">
|
||||
<h1 className="text-2xl font-black tracking-tight text-black">Leads</h1>
|
||||
<h1 className="font-display text-3xl tracking-tight text-black">Leads</h1>
|
||||
<p className="text-sm text-gray-500">
|
||||
{resumen.total} leads en total · {resumen.porEstado['nuevo'] ?? 0} sin contactar
|
||||
</p>
|
||||
@@ -79,7 +79,7 @@ export default async function PanelPage({
|
||||
href={f.value === 'todos' ? '/panel' : `/panel?estado=${f.value}`}
|
||||
className={`px-3 py-1.5 rounded-full text-sm font-medium border transition-colors ${
|
||||
active
|
||||
? 'bg-black text-white border-black'
|
||||
? 'bg-primary-700 text-white border-primary-700'
|
||||
: 'bg-white text-gray-600 border-gray-200 hover:border-gray-400'
|
||||
}`}
|
||||
>
|
||||
|
||||
@@ -28,7 +28,7 @@ export default async function PreciosPage() {
|
||||
return (
|
||||
<div className="space-y-10">
|
||||
<div>
|
||||
<h1 className="text-2xl font-black tracking-tight text-black">Tabla de precios</h1>
|
||||
<h1 className="font-display text-3xl tracking-tight text-black">Tabla de precios</h1>
|
||||
<p className="text-sm text-gray-500 mt-1">
|
||||
Define los precios unitarios y la mano de obra. El motor calcula el presupuesto a
|
||||
partir de estos valores y las medidas del lead.
|
||||
@@ -73,7 +73,7 @@ export default async function PreciosPage() {
|
||||
</span>
|
||||
</span>
|
||||
</label>
|
||||
<button className="self-start bg-black text-white rounded-lg px-4 py-2 text-sm font-medium">
|
||||
<button className="self-start bg-primary-700 text-white rounded-lg px-4 py-2 text-sm font-medium">
|
||||
Guardar preferencia
|
||||
</button>
|
||||
</form>
|
||||
@@ -112,7 +112,7 @@ export default async function PreciosPage() {
|
||||
/>
|
||||
</label>
|
||||
))}
|
||||
<button className="col-span-2 md:col-span-5 justify-self-start bg-black text-white rounded-lg px-4 py-2 text-sm font-medium">
|
||||
<button className="col-span-2 md:col-span-5 justify-self-start bg-primary-700 text-white rounded-lg px-4 py-2 text-sm font-medium">
|
||||
Guardar configuración
|
||||
</button>
|
||||
</form>
|
||||
@@ -212,7 +212,7 @@ export default async function PreciosPage() {
|
||||
<label className="col-span-2 flex items-center gap-2 text-gray-500 sm:col-auto">
|
||||
<input type="checkbox" name="esDefault" /> Marcar como default
|
||||
</label>
|
||||
<button className="col-span-2 bg-black text-white rounded-lg px-3 py-2 font-medium sm:col-auto sm:py-1.5">
|
||||
<button className="col-span-2 bg-primary-700 text-white rounded-lg px-3 py-2 font-medium sm:col-auto sm:py-1.5">
|
||||
Añadir
|
||||
</button>
|
||||
</form>
|
||||
@@ -234,7 +234,7 @@ export default async function PreciosPage() {
|
||||
placeholder="categoria,nombre,calidad,precio,unidad,descriptor_render,sku"
|
||||
className="w-full border border-gray-300 rounded-lg px-3 py-2 font-mono text-xs"
|
||||
/>
|
||||
<button className="mt-2 bg-black text-white rounded-lg px-4 py-2 text-sm font-medium">
|
||||
<button className="mt-2 bg-primary-700 text-white rounded-lg px-4 py-2 text-sm font-medium">
|
||||
Importar
|
||||
</button>
|
||||
</form>
|
||||
|
||||
@@ -6,7 +6,7 @@ import { signup } from './actions';
|
||||
import AuthShell from '@/components/auth/AuthShell';
|
||||
|
||||
const inputClass =
|
||||
'rounded-lg border border-gray-300 px-3 py-2.5 text-sm focus:border-gray-900 focus:outline-none focus:ring-1 focus:ring-gray-900';
|
||||
'rounded-lg border border-gray-300 px-3 py-2.5 text-sm focus:border-primary-700 focus:outline-none focus:ring-1 focus:ring-primary-700';
|
||||
|
||||
export default function SignupPage() {
|
||||
const [error, formAction, pending] = useActionState(signup, null);
|
||||
@@ -19,7 +19,7 @@ export default function SignupPage() {
|
||||
>
|
||||
<form action={formAction} className="flex flex-col gap-4">
|
||||
<div>
|
||||
<h1 className="text-2xl font-black tracking-tight text-black">Empieza gratis 14 días</h1>
|
||||
<h1 className="font-display text-3xl tracking-tight text-black">Empieza gratis 14 días</h1>
|
||||
<p className="mt-1 text-sm text-gray-500">
|
||||
Sin tarjeta. Configura tu catálogo y recibe leads.
|
||||
</p>
|
||||
@@ -51,19 +51,19 @@ export default function SignupPage() {
|
||||
className={inputClass}
|
||||
/>
|
||||
<label className="flex items-center gap-2 text-xs text-gray-500">
|
||||
<input name="optInMarketing" type="checkbox" className="h-4 w-4 accent-black" /> Quiero
|
||||
<input name="optInMarketing" type="checkbox" className="h-4 w-4 accent-[#2f5c46]" /> Quiero
|
||||
recibir novedades de Reformix
|
||||
</label>
|
||||
|
||||
{error && <p className="text-sm text-red-600">{error}</p>}
|
||||
|
||||
<button type="submit" disabled={pending} className="btn btn-primary w-full disabled:opacity-60">
|
||||
<button type="submit" disabled={pending} className="btn bg-primary-700 text-white hover:bg-primary-900 w-full disabled:opacity-60">
|
||||
{pending ? 'Creando cuenta…' : 'Crear cuenta'}
|
||||
</button>
|
||||
|
||||
<p className="text-center text-sm text-gray-500">
|
||||
¿Ya tienes cuenta?{' '}
|
||||
<Link href="/login" className="font-semibold text-black hover:underline">
|
||||
<Link href="/login" className="font-semibold text-primary-700 hover:underline">
|
||||
Entrar
|
||||
</Link>
|
||||
</p>
|
||||
|
||||
Reference in New Issue
Block a user