Rediseña panel y auth con la identidad de la landing B2C
- Vista de leads en tarjetas + tabla con toggle (tarjetas por defecto, preferencia persistida) - Galería de trabajos: gestión en /panel/galeria y bloque público en el funnel - Selector de tema por reformista (presets + color de marca opcional) aplicado a la landing - Login y registro rediseñados a pantalla partida 50/50 con foto de reforma - Enlace "Entrar" funcional en la cabecera del funnel; elimina Navbar muerto - Unifica tipografía y botones del panel con los tokens de la landing Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
54
mvp/b2c/src/components/funnel/GaleriaTrabajos.tsx
Normal file
54
mvp/b2c/src/components/funnel/GaleriaTrabajos.tsx
Normal file
@@ -0,0 +1,54 @@
|
||||
import type { PublicGaleriaFoto } from '@/lib/funnel/public-queries';
|
||||
|
||||
type GaleriaTrabajosProps = {
|
||||
fotos: PublicGaleriaFoto[];
|
||||
nombreEmpresa: string;
|
||||
};
|
||||
|
||||
// Galería de trabajos del reformista en su landing pública. Solo se muestra si
|
||||
// el reformista ha subido fotos desde su panel.
|
||||
export default function GaleriaTrabajos({ fotos, nombreEmpresa }: GaleriaTrabajosProps) {
|
||||
if (fotos.length === 0) return null;
|
||||
|
||||
return (
|
||||
<section id="galeria" className="bg-gray-50 section" aria-label="Galería de trabajos">
|
||||
<div className="container">
|
||||
<div className="max-w-2xl mb-10 md:mb-14">
|
||||
<span
|
||||
className="badge mb-4"
|
||||
style={{ backgroundColor: 'var(--brand)', color: 'var(--brand-contrast)' }}
|
||||
>
|
||||
Nuestros trabajos
|
||||
</span>
|
||||
<h2 className="text-[clamp(1.75rem,4vw,2.75rem)] font-black tracking-tight text-black leading-tight">
|
||||
Reformas que ya hemos hecho
|
||||
</h2>
|
||||
<p className="text-gray-500 mt-3 leading-relaxed">
|
||||
Una muestra real del trabajo de {nombreEmpresa}. Calidad de acabados, plazos cumplidos.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 md:grid-cols-3 gap-3 md:gap-4">
|
||||
{fotos.map((f) => (
|
||||
<figure
|
||||
key={f.id}
|
||||
className="group relative aspect-[4/3] overflow-hidden rounded-xl bg-gray-100 border border-gray-200"
|
||||
>
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img
|
||||
src={f.url}
|
||||
alt={f.titulo ?? `Reforma de ${nombreEmpresa}`}
|
||||
className="w-full h-full object-cover transition-transform duration-500 group-hover:scale-105"
|
||||
/>
|
||||
{f.titulo && (
|
||||
<figcaption className="absolute inset-x-0 bottom-0 bg-gradient-to-t from-black/70 to-transparent p-3 text-white text-sm font-semibold opacity-0 group-hover:opacity-100 transition-opacity duration-300">
|
||||
{f.titulo}
|
||||
</figcaption>
|
||||
)}
|
||||
</figure>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -1,7 +1,10 @@
|
||||
import Link from 'next/link';
|
||||
|
||||
type TenantBrandProps = {
|
||||
nombreEmpresa: string;
|
||||
logoUrl: string | null;
|
||||
subtitle?: string;
|
||||
showLogin?: boolean;
|
||||
};
|
||||
|
||||
function iniciales(nombre: string): string {
|
||||
@@ -14,7 +17,12 @@ function iniciales(nombre: string): string {
|
||||
|
||||
// Cabecera de marca del reformista para el funnel público y las páginas de
|
||||
// solicitud. El cliente final ve el branding del reformista, no el de Reformix.
|
||||
export default function TenantBrand({ nombreEmpresa, logoUrl, subtitle }: TenantBrandProps) {
|
||||
export default function TenantBrand({
|
||||
nombreEmpresa,
|
||||
logoUrl,
|
||||
subtitle,
|
||||
showLogin = false,
|
||||
}: TenantBrandProps) {
|
||||
return (
|
||||
<header className="bg-white border-b border-gray-200">
|
||||
<div className="container py-4 flex items-center justify-between gap-4">
|
||||
@@ -27,7 +35,10 @@ export default function TenantBrand({ nombreEmpresa, logoUrl, subtitle }: Tenant
|
||||
className="h-9 w-auto max-w-[160px] object-contain"
|
||||
/>
|
||||
) : (
|
||||
<span className="h-9 w-9 rounded-lg bg-black text-white text-sm font-black flex items-center justify-center shrink-0">
|
||||
<span
|
||||
className="h-9 w-9 rounded-lg text-sm font-black flex items-center justify-center shrink-0"
|
||||
style={{ backgroundColor: 'var(--brand, #0a0a0a)', color: 'var(--brand-contrast, #fff)' }}
|
||||
>
|
||||
{iniciales(nombreEmpresa)}
|
||||
</span>
|
||||
)}
|
||||
@@ -35,9 +46,27 @@ export default function TenantBrand({ nombreEmpresa, logoUrl, subtitle }: Tenant
|
||||
{nombreEmpresa}
|
||||
</span>
|
||||
</div>
|
||||
<span className="text-xs font-semibold uppercase tracking-widest text-gray-400 shrink-0">
|
||||
{subtitle ?? 'Presupuesto de reforma'}
|
||||
</span>
|
||||
{showLogin ? (
|
||||
<Link
|
||||
href="/login"
|
||||
className="shrink-0 inline-flex items-center gap-1.5 rounded-lg border border-gray-300 px-3.5 py-2 text-sm font-semibold text-gray-700 transition-colors hover:border-gray-900 hover:text-black"
|
||||
>
|
||||
Entrar
|
||||
<svg width="15" height="15" viewBox="0 0 16 16" fill="none" aria-hidden="true">
|
||||
<path
|
||||
d="M2 8h12M10 4l4 4-4 4"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
</Link>
|
||||
) : (
|
||||
<span className="text-xs font-semibold uppercase tracking-widest text-gray-400 shrink-0">
|
||||
{subtitle ?? 'Presupuesto de reforma'}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user