feat(schema): add cards.assignee and subtask date fields

Three new optional fields, idempotent ensure helpers:

- cards.assignee: free-text label rendered above the card bar
  on the new /plan Gantt. Empty by default.
- subtasks.start_date: ISO date or empty. Lets subtasks appear
  on the Gantt under their parent card with their own duration.
- subtasks.target_date: same, for the bar end.

Live migration confirmed idempotent — first run added the
fields, second run reported both already present.
This commit is contained in:
Dave 2026-09-21 16:12:29 +02:00
parent 16c8ea3644
commit a5dc6083dd
2 changed files with 59 additions and 0 deletions

View file

@ -360,6 +360,31 @@ async function ensureCardNarrativeFields(pb: PocketBase): Promise<void> {
}
}
async function ensureSubtaskDateFields(pb: PocketBase): Promise<void> {
const subtasks = await pb.collections.getOne("subtasks");
const have = new Set(subtasks.fields.map((f: { name: string }) => f.name));
const fields = [...(subtasks.fields as unknown[])];
let changed = false;
for (const field of [
{ name: "start_date", type: "date", required: false },
{ name: "target_date", type: "date", required: false },
]) {
if (!have.has(field.name)) {
fields.push({ ...field, presentable: false, hidden: false, system: false });
changed = true;
}
}
if (changed) {
await pb.collections.update(subtasks.id, { fields });
console.log(" + subtasks fields: start_date, target_date");
} else {
console.log(" = subtasks date fields already present");
}
}
async function ensureCardNumbers(pb: PocketBase): Promise<void> {
const cards = await pb.collections.getOne("cards");
const have = new Set(cards.fields.map((f: { name: string }) => f.name));
@ -505,6 +530,29 @@ async function ensureCardProposalFields(pb: PocketBase): Promise<void> {
}
}
async function ensureCardAssigneeField(pb: PocketBase): Promise<void> {
const cards = await pb.collections.getOne("cards");
const have = new Set(cards.fields.map((f: { name: string }) => f.name));
if (have.has("assignee")) {
console.log(" = cards.assignee already present");
return;
}
const fields = [...(cards.fields as unknown[])];
fields.push({
name: "assignee",
type: "text",
required: false,
max: 120,
presentable: false,
hidden: false,
system: false,
});
await pb.collections.update(cards.id, { fields });
console.log(" + cards field: assignee");
}
/**
* Extend the activity `action` select with Plan Puzzle v1's three new values:
* `column_changed`, `date_proposed`, `date_proposal_resolved`. Existing rows
@ -1525,6 +1573,11 @@ async function main(): Promise<void> {
// Add the Plan Puzzle v1 proposal fields and the partial index.
await ensureCardProposalFields(pb);
// Add the Gantt plan redesign's optional assignee text and the subtask
// date fields (Phase 10).
await ensureCardAssigneeField(pb);
await ensureSubtaskDateFields(pb);
// Extend activity.action with the three new puzzle actions.
await ensureActivityActionValues(pb);

View file

@ -114,6 +114,8 @@ export interface CardRecord extends BaseRecord {
proposed_target_date?: string;
proposed_by?: string;
proposed_at?: string;
/** Free-text label rendered above the card bar on the Gantt. Optional. */
assignee: string;
}
export interface SubtaskRecord extends BaseRecord {
@ -121,6 +123,10 @@ export interface SubtaskRecord extends BaseRecord {
title: string;
done: boolean;
position: number;
/** ISO date string, or "" when unset. Mirrors cards.start_date. */
start_date: string;
/** ISO date string, or "" when unset. Mirrors cards.target_date. */
target_date: string;
}
export interface CommentRecord extends BaseRecord {