WFDASH-WEBAPP/scripts/migrations/2026-07-07-order-items-collection-photo.ts
SJCoetz b9828def38 feat(collection-pool): add unified redesigned collection pool view
Remove redundant Assign Drivers top-level navigation link since driver assignment is now embedded directly in the collection pool page per-row, and add an Other nav section for leftover navigation items
Extract collection pool view logic into a dedicated view-model module with full Vitest test coverage for assignment state classification, row sorting, pool age labeling, and proximity marker grouping
Flatten the collection pool page into a single unified list replacing the old grouped assigned/unassigned layout, with updated row UI showing all required operational details and context-aware actions
Add database migration to add optional `collection_photo` field to order_items for driver pickup proof photos
Add complete design documentation, planning artifacts, and brainstorming session files for the collection pool redesign project
2026-07-07 20:49:31 +02:00

108 lines
No EOL
3.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Migration — add `collection_photo` (single file) to `order_items`.
*
* Why:
* The driver app's Collect flow needs a place to attach the optional
* proof-of-pickup photo (driver takes a snapshot of the goods at the
* supplier, before signing off the row). Today there's no file field
* on `order_items`, so drivers had no way to attach anything from
* the field — operators had to take their word on quantity
* discrepancies without visual context.
*
* Lifecycle:
* - Optional on every collection. Driver can skip it.
* - Single image per row (multi-photo would balloon PB storage
* and isn't part of the current spec).
* - Stamped alongside `qty_collected` / `drv_collected_at` so the
* audit trail ties proof to numbers and time.
*
* Field spec:
* - 10MB max — modern phone cameras run ~3–6MB JPEG; 10MB gives
* headroom for HEIC and burst shots without throttling the UI.
* - image/* mime allowlist. PDF and other docs go elsewhere.
* - maxSelect: 1. A second photo would be a separate feature.
*
* Idempotent — skips if the field already exists.
*
* Run with (from WFDASH_NEXTJS so .env.local is found):
* npx tsx scripts/migrations/2026-07-07-order-items-collection-photo.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");
const FIELD_NAME = "collection_photo";
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(`[order-items-collection-photo] Authenticated against ${url}`);
const col = await pb.collections.getOne("order_items");
const fields = (col.fields as Array<Record<string, unknown>>).slice();
if (fields.some((f) => f.name === FIELD_NAME)) {
console.log(
`[order-items-collection-photo] ${FIELD_NAME} already exists — skip field add`,
);
} else {
fields.push({
name: FIELD_NAME,
type: "file",
required: false,
presentable: false,
options: {
maxSelect: 1,
maxSize: 10 * 1024 * 1024, // 10MB — phone cameras, room to breathe
mimeTypes: ["image/jpeg", "image/png", "image/webp", "image/heic", "image/heif"],
},
});
await pb.collections.update(col.id, { fields });
console.log(
`[order-items-collection-photo] added \`${FIELD_NAME}\` (file, optional, max 10MB, image/* only) to order_items`,
);
}
const fresh = await pb.collections.getOne("order_items");
const verify = (fresh.fields as Array<{ name: string; type: string }>)
.filter((f) => f.name === FIELD_NAME)
.map((f) => `${f.name}:${f.type}`)
.join(", ");
console.log(
`[order-items-collection-photo] Post-update → ${verify || "FIELD MISSING"}`,
);
console.log("[order-items-collection-photo] Migration complete.");
}
main().catch((err) => {
console.error("[order-items-collection-photo] failed:", err);
process.exit(1);
});