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:
@@ -284,7 +284,7 @@ async function main() {
|
||||
|
||||
console.log('Limpiando datos previos...');
|
||||
await db.execute(
|
||||
sql`TRUNCATE TABLE ${schema.precisionHistory}, ${schema.leadPipelineEventos}, ${schema.leadEstadoHistory}, ${schema.leadFotos}, ${schema.leads}, ${schema.sessions}, ${schema.users}, ${schema.plans}, ${schema.tenants} RESTART IDENTITY CASCADE`
|
||||
sql`TRUNCATE TABLE ${schema.testimonioFotos}, ${schema.testimonios}, ${schema.precisionHistory}, ${schema.leadPipelineEventos}, ${schema.leadEstadoHistory}, ${schema.leadFotos}, ${schema.leads}, ${schema.sessions}, ${schema.users}, ${schema.plans}, ${schema.tenants} RESTART IDENTITY CASCADE`
|
||||
);
|
||||
|
||||
console.log('Sembrando planes...');
|
||||
@@ -341,6 +341,13 @@ async function main() {
|
||||
planId: pro.id,
|
||||
subscriptionStatus: 'trial',
|
||||
trialEndsAt: new Date(Date.now() + 14 * 24 * 60 * 60 * 1000),
|
||||
seoTitle: 'Reformas Ejemplo · Reformas integrales en Madrid',
|
||||
seoDescription:
|
||||
'Pide tu presupuesto de reforma con render IA en minutos. Reformas de cocina, baño y vivienda completa en Madrid.',
|
||||
aboutEnabled: true,
|
||||
aboutTexto:
|
||||
'Somos un equipo de Madrid especializado en reformas integrales de cocinas, baños y viviendas completas. Cuidamos cada detalle y te acompañamos desde la primera idea hasta la entrega de llaves, con presupuestos claros y sin sorpresas.',
|
||||
aniosExperiencia: 15,
|
||||
})
|
||||
.returning();
|
||||
|
||||
@@ -430,6 +437,66 @@ async function main() {
|
||||
}
|
||||
}
|
||||
|
||||
// --- Opiniones demo (recogidas en el funnel de review, ya moderadas) ---
|
||||
console.log('Sembrando opiniones demo...');
|
||||
const leadsPorEmail = await db
|
||||
.select({ id: schema.leads.id, email: schema.leads.email })
|
||||
.from(schema.leads)
|
||||
.where(eq(schema.leads.tenantId, tenant.id));
|
||||
const leadIdPorEmail = new Map(leadsPorEmail.map((l) => [l.email, l.id]));
|
||||
|
||||
// Diego (ganado) ya tiene la opinión solicitada desde el panel.
|
||||
const diegoId = leadIdPorEmail.get('diego.romero@example.com');
|
||||
if (diegoId) {
|
||||
await db
|
||||
.update(schema.leads)
|
||||
.set({ testimonioSolicitadoAt: new Date(Date.now() - 20 * 24 * 60 * 60 * 1000) })
|
||||
.where(eq(schema.leads.id, diegoId));
|
||||
}
|
||||
|
||||
const [testiDiego] = await db
|
||||
.insert(schema.testimonios)
|
||||
.values({
|
||||
tenantId: tenant.id,
|
||||
leadId: diegoId ?? null,
|
||||
nombre: 'Diego Romero',
|
||||
contexto: 'Reforma integral · Valencia',
|
||||
rating: 5,
|
||||
texto:
|
||||
'Reformaron un piso heredado de 85 metros de arriba a abajo. El presupuesto inicial cuadró casi al detalle con el final y los plazos se cumplieron. Repetiría sin dudarlo.',
|
||||
estado: 'publicado',
|
||||
})
|
||||
.returning();
|
||||
if (testiDiego) {
|
||||
await db
|
||||
.insert(schema.testimonioFotos)
|
||||
.values({ testimonioId: testiDiego.id, url: '/despues.webp', orden: 0 });
|
||||
}
|
||||
|
||||
await db.insert(schema.testimonios).values([
|
||||
{
|
||||
tenantId: tenant.id,
|
||||
leadId: leadIdPorEmail.get('carmen.ibanez@example.com') ?? null,
|
||||
nombre: 'Carmen Ibáñez',
|
||||
contexto: 'Comedor · Madrid',
|
||||
rating: 5,
|
||||
texto:
|
||||
'Abrieron el comedor al salón y pusieron tarima nueva. El render que me enseñaron al principio era casi idéntico al resultado final. Trato impecable.',
|
||||
estado: 'publicado',
|
||||
},
|
||||
{
|
||||
// Pendiente de aprobar: aparece en el panel pero aún no en la landing.
|
||||
tenantId: tenant.id,
|
||||
leadId: leadIdPorEmail.get('tomas.herrero@example.com') ?? null,
|
||||
nombre: 'Tomás Herrero',
|
||||
contexto: 'Baño · Bilbao',
|
||||
rating: 4,
|
||||
texto:
|
||||
'Cambiaron todo el alicatado y los sanitarios del baño. Buen acabado y limpios. Solo se retrasaron un par de días por los materiales.',
|
||||
estado: 'pendiente',
|
||||
},
|
||||
]);
|
||||
|
||||
// --- Precios + catálogo demo (motor de presupuesto) ---
|
||||
const [tenantRow] = await db
|
||||
.select()
|
||||
|
||||
Reference in New Issue
Block a user