Etiqueta fotos por zona/momento y añade tabla lead_notas
Prepara el modelo de datos para la ingesta multicanal del perfil del lead: - lead_fotos: columnas momento (foto_momento antes/despues, default antes) y zona (tipo_reforma, nullable con fallback al tipoReforma del lead). - lead_notas: tabla append-only de datos de texto por zona (ej. "suelo premium"), con origen (ep|funnel|panel) para auditar quién los aportó. - Migración 0008 + regenerado db-schema/schema.sql. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -47,6 +47,9 @@ export const tipoReforma = pgEnum('tipo_reforma', [
|
||||
|
||||
export const calidad = pgEnum('calidad', ['basica', 'media', 'premium']);
|
||||
|
||||
// Momento de una foto del lead: el estado antes de la reforma o el render del después.
|
||||
export const fotoMomento = pgEnum('foto_momento', ['antes', 'despues']);
|
||||
|
||||
export const urgencia = pgEnum('urgencia', ['alta', 'media', 'baja']);
|
||||
|
||||
export const categoriaMaterial = pgEnum('categoria_material', [
|
||||
@@ -214,17 +217,34 @@ export const leads = pgTable(
|
||||
]
|
||||
);
|
||||
|
||||
// Fotos subidas por el cliente (paso 3, 2-4 fotos)
|
||||
// Fotos del lead, etiquetadas por zona y momento. Las "antes" las sube el cliente (funnel o EP);
|
||||
// las "despues" (renders) las devuelve el flujo de generación externo por el mismo EP de ingesta.
|
||||
// zona es nullable por compatibilidad con filas antiguas (fallback al tipoReforma del lead).
|
||||
export const leadFotos = pgTable('lead_fotos', {
|
||||
id: uuid('id').primaryKey().defaultRandom(),
|
||||
leadId: uuid('lead_id')
|
||||
.notNull()
|
||||
.references(() => leads.id, { onDelete: 'cascade' }),
|
||||
url: text('url').notNull(),
|
||||
momento: fotoMomento('momento').notNull().default('antes'),
|
||||
zona: tipoReforma('zona'),
|
||||
orden: integer('orden').notNull().default(0),
|
||||
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
|
||||
});
|
||||
|
||||
// Datos de texto que enriquecen el perfil del lead por zona (ej. "Baño: suelo premium").
|
||||
// Append-only: cada llamada al EP de ingesta puede añadir notas que el agente externo homologará.
|
||||
export const leadNotas = pgTable('lead_notas', {
|
||||
id: uuid('id').primaryKey().defaultRandom(),
|
||||
leadId: uuid('lead_id')
|
||||
.notNull()
|
||||
.references(() => leads.id, { onDelete: 'cascade' }),
|
||||
zona: tipoReforma('zona'),
|
||||
texto: text('texto').notNull(),
|
||||
origen: text('origen').notNull().default('ep'), // ep | funnel | panel
|
||||
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
|
||||
});
|
||||
|
||||
// Opiniones del cliente final, recogidas en el funnel de review (/opinion/[id]).
|
||||
// El reformista las solicita desde el panel y aprueba antes de que salgan en su landing.
|
||||
export const testimonios = pgTable(
|
||||
@@ -350,6 +370,9 @@ export type Tenant = typeof tenants.$inferSelect;
|
||||
export type Lead = typeof leads.$inferSelect;
|
||||
export type NewLead = typeof leads.$inferInsert;
|
||||
export type LeadFoto = typeof leadFotos.$inferSelect;
|
||||
export type NewLeadFoto = typeof leadFotos.$inferInsert;
|
||||
export type LeadNota = typeof leadNotas.$inferSelect;
|
||||
export type NewLeadNota = typeof leadNotas.$inferInsert;
|
||||
export type Testimonio = typeof testimonios.$inferSelect;
|
||||
export type NewTestimonio = typeof testimonios.$inferInsert;
|
||||
export type TestimonioFoto = typeof testimonioFotos.$inferSelect;
|
||||
|
||||
Reference in New Issue
Block a user