overhaul the collection pool page's ready status UI: split the clickable chip into a static status indicator and dedicated toggle button with loading state, improving user clarity between status and actionable elements update all related copy (toast messages, filter labels, button text) to use consistent "awaiting supplier confirmation" terminology across the interface remove invalid `transparent` prop from traffic tile layers in both the collection map and traffic tiles components, update comments to reflect Leaflet does not support this option add wildcard to allowedDevOrigins in next.config.ts to simplify local development testing across different network interfaces add a comprehensive database backfill script to fix assigned order items missing the `ready_for_collection` flag, ensuring drivers see their correct assigned stops in the driver app
126 lines
No EOL
4.5 KiB
TypeScript
126 lines
No EOL
4.5 KiB
TypeScript
/**
|
|
* Backfill — flip `ready_for_collection` to `true` on every row that is
|
|
* already `item_status = "assigned"` but never had the flag set.
|
|
*
|
|
* Why this exists as a sibling to the original migration:
|
|
*
|
|
* `2026-07-07-order-items-ready-for-collection.ts` introduced the
|
|
* `ready_for_collection` bool AND backfilled every existing
|
|
* `collection_pool` row to `true`. It deliberately did NOT touch
|
|
* rows that were already `assigned` — at the time the migration
|
|
* ran, the theory was "assigned items have already been hand-vetted
|
|
* by the operator, so the flag should already be on them, so we
|
|
* don't need to backfill".
|
|
*
|
|
* That theory was wrong. In practice, several `assigned` rows had
|
|
* been created via paths that did not set the flag (direct PB admin
|
|
* edits, an older assignment workflow that pre-dated the column).
|
|
* The driver app's `getMyAssignedItems` filter requires
|
|
* `ready_for_collection = true`, so the driver sees an empty
|
|
* Assigned tab even though they have real stops in the DB.
|
|
*
|
|
* This script mirrors the original pool-backfill shape so the gap
|
|
* closes without forcing the operator to re-toggle every row in the
|
|
* dashboard.
|
|
*
|
|
* Idempotent — only flips rows where `ready_for_collection != true`.
|
|
*
|
|
* Run with (from WFDASH_NEXTJS so .env.local is found):
|
|
* npx tsx scripts/migrations/2026-07-07-backfill-assigned-rfc.ts
|
|
*/
|
|
/* eslint-disable */
|
|
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 {
|
|
/* fine — rely on shell env */
|
|
}
|
|
}
|
|
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. sourced from .env.local).",
|
|
);
|
|
}
|
|
|
|
const pb = new PocketBase(url);
|
|
await pb.collection("_superusers").authWithPassword(email, password);
|
|
console.log(`[backfill-assigned-rfc] Authenticated against ${url}`);
|
|
|
|
// Sanity check — make sure the column exists before we query on it.
|
|
const col = await pb.collections.getOne("order_items");
|
|
const hasField = (col.fields as Array<{ name: string }>).some(
|
|
(f) => f.name === "ready_for_collection",
|
|
);
|
|
if (!hasField) {
|
|
throw new Error(
|
|
"ready_for_collection field missing on order_items — run 2026-07-07-order-items-ready-for-collection.ts first.",
|
|
);
|
|
}
|
|
|
|
// Find every assigned row that isn't already ready.
|
|
// `!= true` matches false AND null — null is the default for rows
|
|
// created before the column existed, plus any rows where the
|
|
// boolean was never written.
|
|
const rows = await pb.collection("order_items").getFullList({
|
|
filter: 'item_status = "assigned" && ready_for_collection != true',
|
|
fields: "id,item_status,ready_for_collection,drv_id",
|
|
});
|
|
|
|
console.log(
|
|
`[backfill-assigned-rfc] candidates (assigned, rfc!=true): ${rows.length}`,
|
|
);
|
|
|
|
let flipped = 0;
|
|
let failed = 0;
|
|
for (const row of rows) {
|
|
try {
|
|
await pb.collection("order_items").update(row.id, {
|
|
ready_for_collection: true,
|
|
});
|
|
flipped++;
|
|
} catch (err) {
|
|
failed++;
|
|
console.error(
|
|
`[backfill-assigned-rfc] update failed for ${row.id}:`,
|
|
err instanceof Error ? err.message : err,
|
|
);
|
|
}
|
|
}
|
|
|
|
// Post-state snapshot — confirm the filter now returns 0.
|
|
const remaining = await pb.collection("order_items").getFullList({
|
|
filter: 'item_status = "assigned" && ready_for_collection != true',
|
|
fields: "id",
|
|
});
|
|
|
|
console.log(`[backfill-assigned-rfc] flipped: ${flipped}`);
|
|
console.log(`[backfill-assigned-rfc] failed: ${failed}`);
|
|
console.log(
|
|
`[backfill-assigned-rfc] post-state: ${remaining.length} assigned rows still have rfc!=true`,
|
|
);
|
|
console.log("[backfill-assigned-rfc] Done.");
|
|
}
|
|
|
|
main().catch((err) => {
|
|
console.error("[backfill-assigned-rfc] failed:", err);
|
|
process.exit(1);
|
|
}); |