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

@@ -2,6 +2,7 @@ import { headers } from 'next/headers';
import { getTenantPerfil } from '@/db/tenant-queries';
import { actualizarEmpresa } from './actions';
import LogoUploader from '@/components/panel/LogoUploader';
import AboutFotoUploader from '@/components/panel/AboutFotoUploader';
export const dynamic = 'force-dynamic';
@@ -115,11 +116,87 @@ export default async function EmpresaPage() {
className="w-full border border-gray-300 rounded-lg px-3 py-2"
/>
</label>
<div className="md:col-span-2 border-t border-gray-100 pt-5 mt-1">
<h3 className="font-bold text-black">SEO de tu funnel</h3>
<p className="text-xs text-gray-400 mt-1">
Personaliza cómo aparece tu página en Google y al compartirla. Si lo dejas vacío,
usamos un texto por defecto con el nombre de tu empresa.
</p>
</div>
<label className="text-sm md:col-span-2">
<span className="block text-gray-500 mb-1">Título (title)</span>
<input
name="seoTitle"
defaultValue={perfil.seoTitle ?? ''}
maxLength={70}
placeholder={`${perfil.nombreEmpresa} · Presupuesto de reforma`}
className="w-full border border-gray-300 rounded-lg px-3 py-2"
/>
</label>
<label className="text-sm md:col-span-2">
<span className="block text-gray-500 mb-1">Descripción (meta description)</span>
<textarea
name="seoDescription"
defaultValue={perfil.seoDescription ?? ''}
maxLength={160}
rows={2}
placeholder="Pide tu presupuesto de reforma con render IA en minutos."
className="w-full border border-gray-300 rounded-lg px-3 py-2"
/>
</label>
<div className="md:col-span-2 border-t border-gray-100 pt-5 mt-1">
<h3 className="font-bold text-black">Bloque Quiénes somos</h3>
<p className="text-xs text-gray-400 mt-1">
Si lo activas, en tu funnel aparece un bloque con tu foto, tu historia y tus años de
experiencia.
</p>
</div>
<label className="text-sm md:col-span-2 flex items-center gap-2">
<input
type="checkbox"
name="aboutEnabled"
defaultChecked={perfil.aboutEnabled}
className="w-4 h-4 accent-black"
/>
<span className="text-gray-700">Mostrar el bloque Quiénes somos en mi funnel</span>
</label>
<label className="text-sm">
<span className="block text-gray-500 mb-1">Años de experiencia</span>
<input
name="aniosExperiencia"
type="number"
min="1"
max="100"
defaultValue={perfil.aniosExperiencia ?? ''}
className="w-full border border-gray-300 rounded-lg px-3 py-2"
/>
</label>
<label className="text-sm md:col-span-2">
<span className="block text-gray-500 mb-1">Texto de presentación</span>
<textarea
name="aboutTexto"
defaultValue={perfil.aboutTexto ?? ''}
rows={5}
placeholder="Cuéntale al cliente quién eres, tu trayectoria y por qué confiar en ti."
className="w-full border border-gray-300 rounded-lg px-3 py-2"
/>
</label>
<button className="md:col-span-2 justify-self-start bg-black text-white rounded-lg px-4 py-2 text-sm font-medium">
Guardar datos
</button>
</form>
</section>
<section className="bg-white rounded-xl border border-gray-200 p-6">
<h2 className="font-bold text-black mb-1">Foto de Quiénes somos</h2>
<p className="text-sm text-gray-500 mb-4">
Tu foto o la de tu equipo. Aparece en el bloque Quiénes somos de tu funnel.
</p>
<AboutFotoUploader fotoUrl={perfil.aboutFotoUrl} />
</section>
</div>
);
}