chore(seed): demo client, project, users and cards
This commit is contained in:
parent
76438f6e90
commit
857ef1a994
2 changed files with 162 additions and 1 deletions
|
|
@ -42,6 +42,7 @@ src/
|
|||
│ ├── globals.css # REWRITE — token system
|
||||
│ ├── layout.tsx # REWRITE — font, theme, no-flash
|
||||
│ ├── page.tsx # REWRITE — role-aware home
|
||||
│ ├── api/owner/cards/[id]/move/route.ts # owner drag persistence
|
||||
│ └── p/[clientSlug]/[projectSlug]/
|
||||
│ └── page.tsx # board route (server component)
|
||||
├── components/
|
||||
|
|
@ -2200,6 +2201,8 @@ git add src/components/board
|
|||
git commit -m "feat(board): refresh the board when the window regains focus"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 13: Full verification
|
||||
|
||||
- [ ] **Step 1: Run every test**
|
||||
|
|
@ -2216,7 +2219,7 @@ Expected: PASS. Unit tests now total 68 — the 47 from Plan 1a plus 10 in `date
|
|||
npm run build
|
||||
```
|
||||
|
||||
Expected: build succeeds with no type errors. The route list includes `/`, `/login`, and `/p/[clientSlug]/[projectSlug]`.
|
||||
Expected: build succeeds with no type errors. The route list includes `/`, `/login`, `/p/[clientSlug]/[projectSlug]`, and `/api/owner/cards/[id]/move`.
|
||||
|
||||
- [ ] **Step 3: Check both themes at both roles**
|
||||
|
||||
|
|
@ -2249,4 +2252,6 @@ git commit -m "fix(theme): resolve contrast and token issues found in review"
|
|||
|
||||
Plan 1c: card detail view with description, subtasks, comments and activity timeline; admin pages for clients, projects and users; the Inbox accept flow that sets `accepted = true` and `start_date`; card creation UI for both roles; archive.
|
||||
|
||||
Also deferred to Plan 1c: **true PocketBase realtime**. It needs an authenticated PocketBase connection in the browser, which means exposing the auth token to JavaScript. That is a security tradeoff worth making deliberately, with the alternatives compared, rather than acquiring by accident while building a board. Task 12's refresh-on-focus covers the case that actually matters in the meantime.
|
||||
|
||||
Phases 2–5 per the spec: Forgejo ingestion and the effort dashboard — which is where the KPI row with sparklines from the second design reference belongs — AI humanization, the client intake wizard, and the roadmap view.
|
||||
|
|
|
|||
156
scripts/seed-demo.ts
Normal file
156
scripts/seed-demo.ts
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
/**
|
||||
* Seed a demo dataset: one client, one project with default columns, an owner,
|
||||
* a client manager, and a few cards.
|
||||
*
|
||||
* Idempotent — re-running updates the existing records rather than duplicating
|
||||
* them, so it is safe to run between test runs.
|
||||
*
|
||||
* Run with: npm run seed (PocketBase must be running)
|
||||
*/
|
||||
|
||||
import PocketBase from "pocketbase";
|
||||
|
||||
import { buildColumnRecords, assertColumnInvariants } from "../src/lib/columns/seed";
|
||||
import { SPACING } from "../src/lib/ordering/fractional";
|
||||
|
||||
const PB_URL = process.env.NEXT_PUBLIC_PB_URL ?? "http://127.0.0.1:8090";
|
||||
|
||||
export const DEMO = {
|
||||
clientSlug: "wf",
|
||||
projectSlug: "wfdash-webapp",
|
||||
ownerEmail: "owner@example.com",
|
||||
ownerPassword: "owner-password-123",
|
||||
managerEmail: "manager@wf.example.com",
|
||||
managerPassword: "manager-password-123",
|
||||
};
|
||||
|
||||
async function upsert<T extends { id: string }>(
|
||||
pb: PocketBase,
|
||||
collection: string,
|
||||
filter: string,
|
||||
data: Record<string, unknown>
|
||||
): Promise<T> {
|
||||
const existing = await pb
|
||||
.collection(collection)
|
||||
.getFirstListItem<T>(filter)
|
||||
.catch(() => null);
|
||||
|
||||
if (existing) {
|
||||
return pb.collection(collection).update<T>(existing.id, data);
|
||||
}
|
||||
return pb.collection(collection).create<T>(data);
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const email = process.env.PB_SUPERUSER_EMAIL;
|
||||
const password = process.env.PB_SUPERUSER_PASSWORD;
|
||||
|
||||
if (!email || !password) {
|
||||
throw new Error(
|
||||
"PB_SUPERUSER_EMAIL and PB_SUPERUSER_PASSWORD must be set. Copy .env.example to .env.local and export them."
|
||||
);
|
||||
}
|
||||
|
||||
const pb = new PocketBase(PB_URL);
|
||||
pb.autoCancellation(false);
|
||||
await pb.collection("_superusers").authWithPassword(email, password);
|
||||
|
||||
const client = await upsert<{ id: string }>(
|
||||
pb,
|
||||
"clients",
|
||||
`slug = "${DEMO.clientSlug}"`,
|
||||
{
|
||||
name: "WF",
|
||||
slug: DEMO.clientSlug,
|
||||
brand_color: "#3b82f6",
|
||||
active: true,
|
||||
}
|
||||
);
|
||||
|
||||
const project = await upsert<{ id: string }>(
|
||||
pb,
|
||||
"projects",
|
||||
`slug = "${DEMO.projectSlug}"`,
|
||||
{
|
||||
client: client.id,
|
||||
name: "WFDash Web App",
|
||||
slug: DEMO.projectSlug,
|
||||
description: "Operations web application.",
|
||||
status: "active",
|
||||
color: "#3b82f6",
|
||||
repo_owner: "David",
|
||||
repo_name: "WFDASH-WEBAPP",
|
||||
}
|
||||
);
|
||||
|
||||
const columnInputs = buildColumnRecords(project.id);
|
||||
assertColumnInvariants(columnInputs);
|
||||
|
||||
const columns: Record<string, string> = {};
|
||||
for (const input of columnInputs) {
|
||||
const record = await upsert<{ id: string }>(
|
||||
pb,
|
||||
"columns",
|
||||
`project = "${project.id}" && name = "${input.name}"`,
|
||||
input
|
||||
);
|
||||
columns[input.name] = record.id;
|
||||
}
|
||||
|
||||
await upsert(pb, "users", `email = "${DEMO.ownerEmail}"`, {
|
||||
email: DEMO.ownerEmail,
|
||||
password: DEMO.ownerPassword,
|
||||
passwordConfirm: DEMO.ownerPassword,
|
||||
name: "Dave",
|
||||
role: "owner",
|
||||
verified: true,
|
||||
});
|
||||
|
||||
await upsert(pb, "users", `email = "${DEMO.managerEmail}"`, {
|
||||
email: DEMO.managerEmail,
|
||||
password: DEMO.managerPassword,
|
||||
passwordConfirm: DEMO.managerPassword,
|
||||
name: "WF Manager",
|
||||
role: "client_manager",
|
||||
client: client.id,
|
||||
verified: true,
|
||||
});
|
||||
|
||||
const cards = [
|
||||
{ title: "Quote attachments split", column: "In Progress", priority: "high" },
|
||||
{ title: "Stock inline quantity editing", column: "To Do", priority: "medium" },
|
||||
{ title: "Delivery note grouping", column: "Done", priority: "low" },
|
||||
];
|
||||
|
||||
let position = SPACING;
|
||||
for (const card of cards) {
|
||||
await upsert(
|
||||
pb,
|
||||
"cards",
|
||||
`project = "${project.id}" && title = "${card.title}"`,
|
||||
{
|
||||
project: project.id,
|
||||
column: columns[card.column],
|
||||
title: card.title,
|
||||
description: "",
|
||||
position,
|
||||
priority: card.priority,
|
||||
created_by_role: "owner",
|
||||
accepted: true,
|
||||
archived: false,
|
||||
}
|
||||
);
|
||||
position += SPACING;
|
||||
}
|
||||
|
||||
console.log("Seeded:");
|
||||
console.log(` client ${client.id} (${DEMO.clientSlug})`);
|
||||
console.log(` project ${project.id} (${DEMO.projectSlug})`);
|
||||
console.log(` owner ${DEMO.ownerEmail} / ${DEMO.ownerPassword}`);
|
||||
console.log(` manager ${DEMO.managerEmail} / ${DEMO.managerPassword}`);
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
||||
Loading…
Reference in a new issue