/* eslint-disable */ /** * Migration: add `coordinates` (geoPoint) to the `companies` collection. * * Why this exists * ─────────────── * Companies need a location for future routing / delivery work (we'll * either query nearest-company for order allocation, or build a small * internal delivery app so the driver team can see where they're going). * Today the field is unstored; tomorrow it's the foundation of those * features. * * Schema * ────── * companies.coordinates geoPoint (optional in form) * * Why `geoPoint` and not `text "lat, lng"` * ──────────────────────────────────────── * Decision (operator): use the native PB `geoPoint` type. Customers + * suppliers store coordinates as combined text (`"-26.123, 28.456"`) * with a `parseCoordinates` helper on the supplier map. Companies * diverge on purpose so we can use PB's `within` filter for radius * queries without re-parsing. If the project later wants a single * convention across all three collections, that's a separate migration. * * PB geoPoint gotcha * ────────────────── * The field is non-nullable with a zero default of `{"lon":0,"lat":0}` * ("Null Island" in the Atlantic). UI must treat `{0, 0}` as empty, * otherwise every company shows up at sea. Form submits also send * `{0, 0}` for blank — the UI is what hides it. * * Idempotent — safe to re-run. * * Run with: * npx tsx scripts/migrations/2026-06-28-add-companies-coordinates.ts */ import PocketBase from "pocketbase"; import { readFileSync } from "node:fs"; import { resolve } from "node:path"; function loadEnvFile(name: string) { try { const raw = readFileSync(resolve(process.cwd(), name), "utf8"); for (const line of raw.split("\n")) { const trimmed = line.trim(); if (!trimmed || trimmed.startsWith("#")) continue; const m = trimmed.match(/^([A-Z0-9_]+)\s*=\s*(.+)$/i); if (m && !process.env[m[1]]) { process.env[m[1]] = m[2].replace(/^["']|["']$/g, "").trim(); } } } catch { // missing file — fine } } loadEnvFile(".env"); loadEnvFile(".env.local"); async function main() { const url = process.env.NEXT_PUBLIC_POCKETBASE_URL; const email = process.env.PB_ADMIN_EMAIL; const password = process.env.PB_ADMIN_PASSWORD; if (!url || !email || !password) { throw new Error( "Set NEXT_PUBLIC_POCKETBASE_URL, PB_ADMIN_EMAIL, PB_ADMIN_PASSWORD (e.g. from .env.local)." ); } const pb = new PocketBase(url); await pb.collection("_superusers").authWithPassword(email, password); console.log(`[companies-coordinates] Authenticated against ${url}`); const companies = await pb.collections.getOne("companies"); if (companies.fields.some((f) => f.name === "coordinates")) { console.log("[companies-coordinates] companies.coordinates already present — skipping."); return; } await pb.collections.update(companies.id, { fields: [ ...companies.fields, { // Native PB geoPoint. Stored as JSON {"lon": , "lat": }. // Zero default is Null Island {0,0}; the UI treats that as empty. // No `options` block — geoPoint doesn't take one in PB 0.26+. name: "coordinates", type: "geoPoint", required: false, presentable: false, }, ], }); console.log("[companies-coordinates] Added companies.coordinates (geoPoint)."); console.log("[companies-coordinates] Migration complete."); } main().catch((err) => { console.error("[companies-coordinates] Migration failed:", err); process.exit(1); });