From 1a1caaf0df3a351cc9293f7fc52f893968447240 Mon Sep 17 00:00:00 2001 From: Carlos Narro Date: Mon, 1 Jun 2026 11:09:44 +0200 Subject: [PATCH] =?UTF-8?q?Reorganiza=20el=20routing=20multi-tenant:=20fun?= =?UTF-8?q?nel=20por=20slug,=20B2B=20en=20ra=C3=ADz?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - / y /b2b sirven la landing B2B estática (rewrites beforeFiles) - /{slug} resuelve el funnel del reformista (app/[slug]/page.tsx) con branding propio (TenantBrand) y atribución de leads por tenant - crearLead(slug) y páginas /solicitud usan el tenant del lead - Panel: edición del slug del funnel + URL pública en /panel/empresa - Helper de slugs reservados para evitar colisiones con rutas reales Co-Authored-By: Claude Opus 4.7 --- mvp/b2c/next.config.ts | 15 +++-- mvp/b2c/src/app/[slug]/page.tsx | 44 ++++++++++++++ mvp/b2c/src/app/page.tsx | 22 ------- mvp/b2c/src/app/panel/empresa/actions.ts | 25 +++++++- mvp/b2c/src/app/panel/empresa/page.tsx | 32 ++++++++++ .../src/app/solicitud/[id]/estado/page.tsx | 10 +++- mvp/b2c/src/app/solicitud/[id]/fotos/page.tsx | 8 ++- mvp/b2c/src/app/solicitud/actions.ts | 21 ++++--- mvp/b2c/src/app/solicitud/layout.tsx | 18 +----- .../components/ContactForm/ContactForm.tsx | 4 +- mvp/b2c/src/components/Hero/Hero.tsx | 8 +-- mvp/b2c/src/components/funnel/TenantBrand.tsx | 44 ++++++++++++++ mvp/b2c/src/db/tenant-queries.ts | 3 + mvp/b2c/src/lib/funnel/public-queries.ts | 58 ++++++++++++------- mvp/b2c/src/lib/validation/signup.ts | 42 ++++++++++++++ 15 files changed, 268 insertions(+), 86 deletions(-) create mode 100644 mvp/b2c/src/app/[slug]/page.tsx delete mode 100644 mvp/b2c/src/app/page.tsx create mode 100644 mvp/b2c/src/components/funnel/TenantBrand.tsx diff --git a/mvp/b2c/next.config.ts b/mvp/b2c/next.config.ts index fffffc4..05c7a96 100644 --- a/mvp/b2c/next.config.ts +++ b/mvp/b2c/next.config.ts @@ -4,10 +4,17 @@ const nextConfig: NextConfig = { // @react-pdf/renderer usa módulos nativos/wasm (yoga, fontkit) que no deben bundlearse. serverExternalPackages: ['@react-pdf/renderer'], async rewrites() { - return [ - // Landing B2B estática (mvp/b2b) servida en /b2b. El fichero vive en public/b2b.html. - { source: "/b2b", destination: "/b2b.html" }, - ]; + // beforeFiles: estas reglas ganan a las rutas del filesystem (incluida [slug]). + // La raíz y /b2b sirven la landing B2B estática (public/b2b.html); cada reformista + // tiene su funnel en /{slug} vía app/[slug]/page.tsx. + return { + beforeFiles: [ + { source: "/", destination: "/b2b.html" }, + { source: "/b2b", destination: "/b2b.html" }, + ], + afterFiles: [], + fallback: [], + }; }, }; diff --git a/mvp/b2c/src/app/[slug]/page.tsx b/mvp/b2c/src/app/[slug]/page.tsx new file mode 100644 index 0000000..0bd8ef3 --- /dev/null +++ b/mvp/b2c/src/app/[slug]/page.tsx @@ -0,0 +1,44 @@ +import type { Metadata } from 'next'; +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 Footer from '@/components/Footer/Footer'; +import TenantBrand from '@/components/funnel/TenantBrand'; +import { getTenantBySlug } from '@/lib/funnel/public-queries'; + +export const dynamic = 'force-dynamic'; + +export async function generateMetadata({ + params, +}: { + params: Promise<{ slug: string }>; +}): Promise { + const { slug } = await params; + 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.`, + }; +} + +export default async function FunnelPage({ params }: { params: Promise<{ slug: string }> }) { + const { slug } = await params; + const tenant = await getTenantBySlug(slug); + if (!tenant) notFound(); + + return ( + <> + +
+ + + + +
+