WFDASH-WEBAPP/scripts/migrations/2026-06-30-add-raw-price-list.ts
SJCoetz 7f70258037 feat(management): new Pricelist page with drag-drop upload + queue table
Adds /pricelist under the Management section. Operators drag a supplier
price-list file (PDF / CSV / text / image) into the upload zone — or
use the explicit Choose file button — and it lands in PocketBase as an
attachment on the raw_price_list collection. The table below shows the
queue: file, supplier (relation to suppliers), status badge, log
summary preview, upload date, and a delete action per row.

Layout matches the rest of the app: Card for the upload zone, DataTable
for the queue, DeleteDialog for the destructive action. Live updates
via the PB subscribe channel so other tabs / the n8n ingest workflow
show new rows without a manual refresh.

PB schema is sourced from docs/pocketbase/raw_price_list.collection.json.
The migration script is idempotent: it creates the collection from the
schema file on a fresh deploy, and on a pre-existing one (the live PB
already had it, with PB's default null/superuser-only rules) it relaxes
the auth rules to @request.auth.id != '' so any logged-in user can
CRUD — matching every other collection in the app.

Field names match the live PB collection (discovered via admin API):
  - supplier_file (the file)
  - supplier_name (relation to suppliers)
  - status        (select: Pending / Processing / Completed / Failed)
  - log_summary   (free text)
2026-06-30 09:14:36 +02:00

81 lines
3.1 KiB
TypeScript

/**
* Migration — create / repair the `raw_price_list` PocketBase collection.
*
* Two responsibilities:
*
* 1. Create the collection from `docs/pocketbase/raw_price_list.collection.json`
* if it doesn't exist. Schema is the single source of truth; this script
* strips the leading `_comment` keys before sending to PB.
*
* 2. On a pre-existing collection (the user's case — the live PB had the
* collection already, but with PB's default `null` superuser-only rules),
* relax the rules to `@request.auth.id != ''` so any logged-in user can
* CRUD. We DON'T touch the schema on a pre-existing collection — the
* field names there are the source of truth (the live one uses
* `supplier_file` and `supplier_name` as a relation).
*
* Idempotent — safe to re-run.
*
* Run with:
* npm run pb:migrate:raw-price-list
*/
import fs from "node:fs";
import path from "node:path";
import PocketBase from "pocketbase";
const PB_URL = process.env.NEXT_PUBLIC_POCKETBASE_URL || "http://127.0.0.1:8090";
const ADMIN_EMAIL = process.env.PB_ADMIN_EMAIL;
const ADMIN_PASSWORD = process.env.PB_ADMIN_PASSWORD;
if (!ADMIN_EMAIL || !ADMIN_PASSWORD) {
throw new Error("PB_ADMIN_EMAIL and PB_ADMIN_PASSWORD are required.");
}
const ROOT = path.resolve(process.cwd(), "docs", "pocketbase");
const AUTH_RULE = "@request.auth.id != ''";
async function main() {
const pb = new PocketBase(PB_URL);
await pb.collection("_superusers").authWithPassword(ADMIN_EMAIL, ADMIN_PASSWORD);
const existing = await pb.collections.getList(1, 1, { filter: `name = "raw_price_list"` });
if (existing.items.length === 0) {
// ── Create from schema file ─────────────────────────────────
const def = JSON.parse(
fs.readFileSync(path.join(ROOT, "raw_price_list.collection.json"), "utf8"),
);
const sanitized = JSON.parse(
JSON.stringify(def, (key, value) => (key.startsWith("_") ? undefined : value)),
);
delete sanitized.id; // let PB assign its own 15-char id
const created = await pb.collections.create(sanitized);
console.log(`Created raw_price_list (${created.id}) with auth rules = @request.auth.id != ''`);
return;
}
// ── Pre-existing collection: relax the rules ─────────────────
const col = existing.items[0];
const updates: Record<string, unknown> = {};
if (col.listRule !== AUTH_RULE) updates.listRule = AUTH_RULE;
if (col.viewRule !== AUTH_RULE) updates.viewRule = AUTH_RULE;
if (col.createRule !== AUTH_RULE) updates.createRule = AUTH_RULE;
if (col.updateRule !== AUTH_RULE) updates.updateRule = AUTH_RULE;
if (col.deleteRule !== AUTH_RULE) updates.deleteRule = AUTH_RULE;
if (Object.keys(updates).length === 0) {
console.log(`raw_price_list (${col.id}) already has correct auth rules, nothing to do.`);
return;
}
await pb.collections.update(col.id, updates);
console.log(`Relaxed raw_price_list (${col.id}) auth rules → ${AUTH_RULE}`);
console.log("Pricelist migration complete.");
}
main().catch((err) => {
console.error(err);
process.exit(1);
});