Añade personalización SEO/Quiénes somos y testimonios gestionables por reformista

- Panel/empresa: title y meta description SEO personalizables; foto, texto y
  años de experiencia para el bloque "Quiénes somos" (toggle on/off).
- Funnel por slug: metadata SEO desde el tenant, bloque "Quiénes somos" y
  testimonios servidos desde DB (sustituye los hardcodeados).
- Flujo de opiniones: el reformista solicita la opinión desde la ficha de un
  lead ganado; el cliente la deja en un funnel dedicado /opinion/[id] con
  estrellas + texto + fotos; entra como pendiente y el reformista la modera
  (publicar/ocultar/eliminar) en /panel/opiniones antes de mostrarla.
- Schema: columnas SEO/about en tenants, testimonioSolicitadoAt en leads,
  enum testimonio_estado, tablas testimonios + testimonio_fotos (migración 0006).
- Seed: opiniones demo (2 publicadas, 1 pendiente) y contenido "Quiénes somos".

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Carlos Narro
2026-06-01 12:26:13 +02:00
parent 1a1caaf0df
commit a91fe5ce2c
25 changed files with 2638 additions and 66 deletions

View File

@@ -3,10 +3,11 @@ import { notFound } from 'next/navigation';
import Hero from '@/components/Hero/Hero';
import ReformaSlider from '@/components/ReformaSlider/ReformaSlider';
import Features from '@/components/Features/Features';
import Testimonials from '@/components/Testimonials/Testimonials';
import QuienesSomos from '@/components/funnel/QuienesSomos';
import TestimoniosCliente from '@/components/funnel/TestimoniosCliente';
import Footer from '@/components/Footer/Footer';
import TenantBrand from '@/components/funnel/TenantBrand';
import { getTenantBySlug } from '@/lib/funnel/public-queries';
import { getTenantBySlug, getPublishedTestimonios } from '@/lib/funnel/public-queries';
export const dynamic = 'force-dynamic';
@@ -19,8 +20,10 @@ export async function generateMetadata({
const tenant = await getTenantBySlug(slug);
if (!tenant) return { title: 'Reforma no encontrada' };
return {
title: `${tenant.nombreEmpresa} · Presupuesto de reforma`,
description: `Pide tu presupuesto de reforma a ${tenant.nombreEmpresa}. Render IA y presupuesto orientativo en minutos.`,
title: tenant.seoTitle ?? `${tenant.nombreEmpresa} · Presupuesto de reforma`,
description:
tenant.seoDescription ??
`Pide tu presupuesto de reforma a ${tenant.nombreEmpresa}. Render IA y presupuesto orientativo en minutos.`,
};
}
@@ -29,6 +32,8 @@ export default async function FunnelPage({ params }: { params: Promise<{ slug: s
const tenant = await getTenantBySlug(slug);
if (!tenant) notFound();
const testimonios = await getPublishedTestimonios(tenant.id);
return (
<>
<TenantBrand nombreEmpresa={tenant.nombreEmpresa} logoUrl={tenant.logoUrl} />
@@ -36,7 +41,15 @@ export default async function FunnelPage({ params }: { params: Promise<{ slug: s
<Hero slug={tenant.slug} />
<ReformaSlider />
<Features />
<Testimonials />
{tenant.aboutEnabled && tenant.aboutTexto && (
<QuienesSomos
nombreEmpresa={tenant.nombreEmpresa}
fotoUrl={tenant.aboutFotoUrl}
texto={tenant.aboutTexto}
aniosExperiencia={tenant.aniosExperiencia}
/>
)}
{testimonios.length > 0 && <TestimoniosCliente testimonios={testimonios} />}
</main>
<Footer />
</>