diff --git a/src/db/repositories.ts b/src/db/repositories.ts index 7000afb..fd738f7 100644 --- a/src/db/repositories.ts +++ b/src/db/repositories.ts @@ -390,6 +390,20 @@ export async function getTemplates(): Promise { })); } +/** + * Persiste el catálogo global de plantillas (GET /templates) para tenerlas + * SIEMPRE en local y poder inspeccionar offline, aunque el proyecto concreto + * aún no se haya abierto. Upsert por id: no borra las que ya hubiera. + */ +export async function saveTemplates(templates: Template[]): Promise { + const db = await getDb(); + await db.withTransactionAsync(async () => { + for (const t of templates) { + await upsertById(db, 'templates', pickTemplate(t)); + } + }); +} + export async function getFeatureTypes(): Promise { const db = await getDb(); return db.getAllAsync('SELECT * FROM feature_types ORDER BY name'); @@ -409,6 +423,22 @@ export async function getMediaFor( } +/** + * Proyecta el valor crudo del servidor (un `Model::toArray()`) a SOLO las + * columnas que existen en la tabla local, reutilizando los mismos `pick*` del + * PULL. Imprescindible: el `toArray()` trae columnas inexistentes en local + * (uuid, created_at, deleted_at, client_updated_at, properties…) y, en features, + * `geometry` como array —que SQLite no puede bindear—. Sin esto, un conflicto + * lanzaría "no such column"/error de bind y abortaría el ciclo de sync entero. + */ +const CONFLICT_PROJECTOR: Record Record> = { + features: (v) => pickFeature(v as Feature), + issues: (v) => pickIssue(v as Issue), + issue_tasks: (v) => pickIssueTask(v as IssueTask), + inspections: (v) => pickInspection(v as Inspection), + issue_comments: (v) => pickIssueComment(v as IssueComment), +}; + /** Aplica el valor del servidor (recibido en un conflicto) a la fila local. */ export async function applyServerValue( table: string, @@ -416,7 +446,11 @@ export async function applyServerValue( serverValue: Record, ): Promise { const db = await getDb(); - await upsertById(db, table, { id, ...serverValue }); + const project = CONFLICT_PROJECTOR[table]; + // Si no hay proyector conocido, cae a un id-only (no-op seguro) en vez de + // volcar columnas crudas que podrían no existir en la tabla local. + const row = project ? project(serverValue as never) : { id }; + await upsertById(db, table, { ...row, id }); } // ---------- creaciones offline (optimista + reconciliación) ----------