Genera el PDF del presupuesto con galería antes/después por zona

- build-presupuesto.ts: construirPresupuestoPdf(leadId) agrupa fotos y notas
  por zona (fallback al tipoReforma del lead), convierte las imágenes con
  resolverImagenPdf y arma el PDF. Carga el lead por id sin scoping (uso
  interno desde el route del panel y desde la finalización pública).
- tenant-queries: getTenantPerfilById(tenantId) sin auth; getTenantPerfil lo
  reutiliza con el tenant de la sesión.
- PresupuestoDoc: prop zonas + sección "Imágenes de tu reforma" (antes/después
  lado a lado + notas por zona).
- route del panel: refactor para reutilizar construirPresupuestoPdf (DRY),
  manteniendo getLead como guardia de auth/404.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Carlos Narro
2026-06-03 19:04:02 +02:00
parent 737496ed89
commit bcc882e37d
4 changed files with 203 additions and 70 deletions

View File

@@ -23,8 +23,29 @@ export type TenantPerfil = {
themeColor: string | null;
};
export async function getTenantPerfil(): Promise<TenantPerfil> {
const tenantId = await getTenantId();
const TENANT_PERFIL_FALLBACK: TenantPerfil = {
nombreEmpresa: 'Reformix',
slug: '',
logoUrl: null,
provincia: null,
cif: null,
direccion: null,
telefono: null,
email: null,
web: null,
seoTitle: null,
seoDescription: null,
aboutEnabled: false,
aboutFotoUrl: null,
aboutTexto: null,
aniosExperiencia: null,
themePreset: 'pizarra',
themeColor: null,
};
// Perfil del reformista por id, sin depender del contexto de auth. Lo usa el builder de PDF
// y la finalización, que corren desde el EP público (sin sesión).
export async function getTenantPerfilById(tenantId: string): Promise<TenantPerfil> {
const [row] = await db
.select({
nombreEmpresa: tenants.nombreEmpresa,
@@ -49,27 +70,12 @@ export async function getTenantPerfil(): Promise<TenantPerfil> {
.where(eq(tenants.id, tenantId))
.limit(1);
return (
row ?? {
nombreEmpresa: 'Reformix',
slug: '',
logoUrl: null,
provincia: null,
cif: null,
direccion: null,
telefono: null,
email: null,
web: null,
seoTitle: null,
seoDescription: null,
aboutEnabled: false,
aboutFotoUrl: null,
aboutTexto: null,
aniosExperiencia: null,
themePreset: 'pizarra',
themeColor: null,
}
);
return row ?? TENANT_PERFIL_FALLBACK;
}
export async function getTenantPerfil(): Promise<TenantPerfil> {
const tenantId = await getTenantId();
return getTenantPerfilById(tenantId);
}
// Galería de trabajos del reformista, para gestionarla desde el panel.