import { describe, it, expect } from "vitest"; import { buildPlanSections } from "@/lib/roadmap/load"; import type { CardRecord, ClientRecord, ColumnRecord, ProjectRecord, SubtaskRecord, } from "@/lib/pb/types"; function project(overrides: Partial = {}): ProjectRecord { return { id: "p1", created: "2026-01-01 00:00:00.000Z", updated: "2026-01-01 00:00:00.000Z", client: "c1", name: "Client Board", slug: "client-board", description: "", status: "active", kind: "project", color: "#0ea5e9", repo_owner: "", repo_name: "", repo_url: "", webhook_secret: "", last_ingest_at: "", session_gap_minutes: 15, pre_session_minutes: 5, post_session_minutes: 5, background_md: "", show_in_intake: false, ...overrides, }; } function card(overrides: Partial = {}): CardRecord { return { id: "k1", created: "2026-01-01 00:00:00.000Z", updated: "2026-01-01 00:00:00.000Z", project: "p1", column: "col1", title: "Refactor billing modal", description: "", position: 0, priority: "medium", target_date: "", created_by: "", created_by_role: "owner", accepted: true, start_date: "", scope_rule: "", archived: false, number: 1, narrative: "", narrative_version: 0, narrative_shas: "", brief: null, transcript: null, size_band: "", assignee: "", ...overrides, }; } function subtask(overrides: Partial = {}): SubtaskRecord { return { id: "s1", created: "2026-01-01 00:00:00.000Z", updated: "2026-01-01 00:00:00.000Z", card: "k1", title: "Write tests", done: false, position: 0, start_date: "", target_date: "", ...overrides, }; } function column(overrides: Partial = {}): ColumnRecord { return { id: "col1", created: "", updated: "", project: "p1", name: "To Do", position: 0, is_inbox: false, is_done: false, ...overrides, }; } function client(overrides: Partial = {}): ClientRecord { return { id: "c1", created: "", updated: "", name: "Acme", slug: "acme", brand_color: "#0ea5e9", active: true, ...overrides, }; } describe("buildPlanSections — groups cards by project, sorts by date", () => { it("returns one section per active project, sorted client-then-project", () => { const sections = buildPlanSections({ clients: [client({ id: "c2", name: "Bravo" }), client({ id: "c1", name: "Acme" })], projects: [ project({ id: "p2", client: "c2", name: "Bravo Site", slug: "bravo-site" }), project({ id: "p1", client: "c1", name: "Acme App", slug: "acme-app" }), ], columns: [column()], cards: [card({ project: "p2" }), card({ project: "p1" })], subtasks: [], }); expect(sections.map((s) => s.project.name)).toEqual(["Acme App", "Bravo Site"]); }); it("excludes archived projects and Done column cards", () => { const sections = buildPlanSections({ clients: [client()], projects: [project({ status: "archived" })], columns: [ column({ id: "col-done", name: "Done", is_done: true }), column({ id: "col-todo", name: "To Do" }), ], cards: [ card({ id: "k-archived-project", column: "col-todo" }), card({ id: "k-done", column: "col-done" }), ], subtasks: [], }); expect(sections).toEqual([]); }); it("sorts cards within a project: dated first by start_date asc, then undated by title", () => { const sections = buildPlanSections({ clients: [client()], projects: [project()], columns: [column()], cards: [ card({ id: "z-late", title: "Late", start_date: "2026-03-01", target_date: "2026-03-15" }), card({ id: "a-early", title: "Early", start_date: "2026-02-01", target_date: "2026-02-10" }), card({ id: "n-zeta", title: "Zeta" }), card({ id: "n-alpha", title: "Alpha" }), ], subtasks: [], }); expect(sections[0].cards.map((c) => c.id)).toEqual([ "a-early", "z-late", "n-alpha", "n-zeta", ]); }); it("groups subtasks under their parent card and splits dated vs undated", () => { const sections = buildPlanSections({ clients: [client()], projects: [project()], columns: [column()], cards: [card({ id: "k1", start_date: "2026-02-01", target_date: "2026-02-10" })], subtasks: [ subtask({ id: "s-dated", position: 1, start_date: "2026-02-01", target_date: "2026-02-05" }), subtask({ id: "s-undated", position: 0 }), ], }); const c = sections[0].cards[0]; expect(c.datedSubtasks.map((s) => s.id)).toEqual(["s-dated"]); expect(c.undatedSubtaskCount).toBe(1); }); it("sorts subtasks by position within a card", () => { const sections = buildPlanSections({ clients: [client()], projects: [project()], columns: [column()], cards: [card({ start_date: "2026-02-01", target_date: "2026-02-10" })], subtasks: [ subtask({ id: "s2", position: 2, start_date: "2026-02-01", target_date: "2026-02-05" }), subtask({ id: "s1", position: 1, start_date: "2026-02-01", target_date: "2026-02-03" }), ], }); expect(sections[0].cards[0].datedSubtasks.map((s) => s.id)).toEqual(["s1", "s2"]); }); });