xtablo-source/docs/superpowers/plans/2026-03-14-tablo-overview-nocode-builder.md
2026-03-15 19:09:35 +01:00

14 KiB

Tablo Overview No-Code Builder V1 Implementation Plan

For agentic workers: REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Let authorized users reorder the Aperçu blocks in tablo-details via drag-and-drop (fixed left/right zones), auto-save per tablo, and share the same layout with all viewers.

Architecture: Persist a typed layout_overview_v1 JSON payload on public.tablos, then render overview blocks from a sanitized layout model instead of hardcoded JSX order. Add a small DnD interaction layer (same-zone only) with optimistic UI + rollback on save failure. Keep scope strictly to reorder existing overview blocks (description, myTasks, files, info) with a reset-to-default action.

Tech Stack: Supabase migrations + pgTAP, TypeScript, React 19, React Query, Vitest, Testing Library


Spec Reference

  • /Users/arthur.belleville/Documents/perso/projects/xtablo-source/docs/superpowers/specs/2026-03-14-tablo-overview-nocode-builder-design.md

Scope Guardrails (YAGNI)

  • No new widget types in V1.
  • No hide/show or resize in V1.
  • No cross-zone moves in V1.
  • No changes to non-Aperçu tabs.

File Structure (lock boundaries before coding)

Database + shared contract

  • Create: supabase/migrations/20260314110000_add_tablo_overview_layout_v1.sql
    • Responsibility: add nullable JSONB layout_overview_v1 column on public.tablos.
  • Modify: supabase/tests/database/01_schema_structure.test.sql
    • Responsibility: assert new column exists and type is jsonb (schema contract safety).
  • Modify: packages/shared-types/src/database.types.ts (generated)
    • Responsibility: include layout_overview_v1 in tablos Row/Insert/Update types.

Frontend layout domain + UI

  • Create: apps/main/src/pages/tablo-details/overviewLayout.ts
    • Responsibility: typed layout model, defaults, sanitizer, serialization helpers.
  • Create: apps/main/src/pages/tablo-details/overviewLayout.test.ts
    • Responsibility: unit tests for sanitize/default/recovery behavior.
  • Create: apps/main/src/pages/tablo-details/overviewReorder.ts
    • Responsibility: pure reorder helpers (same-zone only, immutable updates).
  • Create: apps/main/src/pages/tablo-details/overviewReorder.test.ts
    • Responsibility: reorder correctness and invalid-move behavior tests.
  • Modify: apps/main/src/pages/tablo-details.tsx
    • Responsibility: edit-mode UX, draggable rendering, optimistic save, rollback, reset.
  • Create: apps/main/src/pages/tablo-details.layout.test.tsx
    • Responsibility: behavior tests for default render, persisted render, save payload, rollback, reset.

API confidence

  • Modify: apps/api/src/__tests__/routes/tablo.test.ts
    • Responsibility: ensure PATCH /tablos/update accepts/saves layout_overview_v1.

Chunk 1: Data Contract and Persistence Foundation

Task 1.1: Add DB column with schema test first

Files:

  • Create: supabase/migrations/20260314110000_add_tablo_overview_layout_v1.sql

  • Modify: supabase/tests/database/01_schema_structure.test.sql

  • Test: supabase/tests/database/01_schema_structure.test.sql

  • Step 1: Write the failing schema test

-- Add near other tablos column checks
SELECT has_column('public', 'tablos', 'layout_overview_v1', 'tablos should have layout_overview_v1 column');
SELECT col_type_is('public', 'tablos', 'layout_overview_v1', 'jsonb', 'tablos.layout_overview_v1 should be jsonb');
  • Step 2: Update pgTAP plan count
select plan(99); -- was 97, +2 tests for new column
  • Step 3: Run schema test and verify it fails before migration

Run: supabase test db --file supabase/tests/database/01_schema_structure.test.sql
Expected: FAIL mentioning missing layout_overview_v1 column.

  • Step 4: Add migration
alter table public.tablos
  add column if not exists layout_overview_v1 jsonb;

comment on column public.tablos.layout_overview_v1 is
  'Per-tablo overview layout config (v1) for tablo-details Aperçu drag-and-drop order.';
  • Step 5: Re-run schema test to verify pass

Run: supabase test db --file supabase/tests/database/01_schema_structure.test.sql
Expected: PASS for new column and type checks.

  • Step 6: Commit
git add supabase/migrations/20260314110000_add_tablo_overview_layout_v1.sql supabase/tests/database/01_schema_structure.test.sql
git commit -m "feat(db): add tablo overview layout json column"

Task 1.2: Regenerate shared DB types and ensure compile contract

Files:

  • Modify: packages/shared-types/src/database.types.ts

  • Test: packages/shared-types/src/database.types.ts (type presence check via typecheck)

  • Step 1: Regenerate database types from local Supabase

Run: supabase gen types typescript --local > packages/shared-types/src/database.types.ts
Expected: command completes with no stderr.

  • Step 2: Verify layout_overview_v1 appears in tablos types

Run: rg -n "layout_overview_v1" packages/shared-types/src/database.types.ts
Expected: matches in Row, Insert, and Update for tablos.

  • Step 3: Run focused typecheck

Run: pnpm --filter @xtablo/main typecheck
Expected: PASS (or unrelated pre-existing failures only).

  • Step 4: Commit
git add packages/shared-types/src/database.types.ts
git commit -m "chore(types): regenerate shared db types for overview layout"

Chunk 2: Frontend Layout Model + UI (TDD-first)

Task 2.1: Add pure layout model and sanitizer with tests

Files:

  • Create: apps/main/src/pages/tablo-details/overviewLayout.ts

  • Create: apps/main/src/pages/tablo-details/overviewLayout.test.ts

  • Test: apps/main/src/pages/tablo-details/overviewLayout.test.ts

  • Step 1: Write failing unit tests for sanitize/default behavior

import { describe, expect, it } from "vitest";
import { DEFAULT_OVERVIEW_LAYOUT, sanitizeOverviewLayout } from "./overviewLayout";

describe("sanitizeOverviewLayout", () => {
  it("returns default when input is null", () => {
    expect(sanitizeOverviewLayout(null)).toEqual(DEFAULT_OVERVIEW_LAYOUT);
  });

  it("drops unknown block ids and restores required ids", () => {
    expect(
      sanitizeOverviewLayout({
        version: 1,
        leftZone: ["description", "unknown"],
        rightZone: [],
      })
    ).toEqual({
      version: 1,
      leftZone: ["description", "myTasks"],
      rightZone: ["files", "info"],
    });
  });
});
  • Step 2: Run test to confirm failure

Run: pnpm --filter @xtablo/main test -- src/pages/tablo-details/overviewLayout.test.ts
Expected: FAIL because module/functions do not exist yet.

  • Step 3: Implement minimal layout model and sanitizer
export const OVERVIEW_LEFT_BLOCKS = ["description", "myTasks"] as const;
export const OVERVIEW_RIGHT_BLOCKS = ["files", "info"] as const;
export const DEFAULT_OVERVIEW_LAYOUT = { version: 1 as const, leftZone: [...OVERVIEW_LEFT_BLOCKS], rightZone: [...OVERVIEW_RIGHT_BLOCKS] };

export function sanitizeOverviewLayout(input: unknown): OverviewLayoutV1 {
  // Parse object safely, keep only allowed ids, dedupe, reinsert required ids.
}
  • Step 4: Re-run test to verify pass

Run: pnpm --filter @xtablo/main test -- src/pages/tablo-details/overviewLayout.test.ts
Expected: PASS.

  • Step 5: Commit
git add apps/main/src/pages/tablo-details/overviewLayout.ts apps/main/src/pages/tablo-details/overviewLayout.test.ts
git commit -m "feat(main): add overview layout schema and sanitizer"

Task 2.2: Add pure reorder helpers with tests

Files:

  • Create: apps/main/src/pages/tablo-details/overviewReorder.ts

  • Create: apps/main/src/pages/tablo-details/overviewReorder.test.ts

  • Test: apps/main/src/pages/tablo-details/overviewReorder.test.ts

  • Step 1: Write failing tests for same-zone reorder and cross-zone block

import { describe, expect, it } from "vitest";
import { moveWithinZone } from "./overviewReorder";

it("reorders items within a zone", () => {
  expect(moveWithinZone(["a", "b", "c"], 0, 2)).toEqual(["b", "c", "a"]);
});

it("returns original list on invalid indices", () => {
  expect(moveWithinZone(["a"], 0, 3)).toEqual(["a"]);
});
  • Step 2: Run test to confirm failure

Run: pnpm --filter @xtablo/main test -- src/pages/tablo-details/overviewReorder.test.ts
Expected: FAIL because helper is missing.

  • Step 3: Implement minimal immutable reorder helper
export function moveWithinZone<T>(items: T[], from: number, to: number): T[] {
  // Validate bounds, clone array, splice move, return new array.
}
  • Step 4: Re-run tests to verify pass

Run: pnpm --filter @xtablo/main test -- src/pages/tablo-details/overviewReorder.test.ts
Expected: PASS.

  • Step 5: Commit
git add apps/main/src/pages/tablo-details/overviewReorder.ts apps/main/src/pages/tablo-details/overviewReorder.test.ts
git commit -m "test(main): cover overview reorder helper"

Task 2.3: Integrate edit mode + DnD + auto-save in tablo-details.tsx

Files:

  • Modify: apps/main/src/pages/tablo-details.tsx

  • Create: apps/main/src/pages/tablo-details.layout.test.tsx

  • Test: apps/main/src/pages/tablo-details.layout.test.tsx

  • Step 1: Write failing page test for persisted layout render order

it("renders overview blocks in persisted order", async () => {
  // Mock useTablosList returning tablo.layout_overview_v1 with swapped left-zone order.
  // Assert rendered block headings follow persisted order.
});
  • Step 2: Write failing page test for save payload on drop
it("persists layout_overview_v1 after drop", async () => {
  // Mock useUpdateTablo mutate; trigger drag/drop; assert mutate payload includes layout_overview_v1.
});
  • Step 3: Run tests to capture failure

Run: pnpm --filter @xtablo/main test -- src/pages/tablo-details.layout.test.tsx
Expected: FAIL (no layout state/edit mode yet).

  • Step 4: Implement UI integration minimally

Implementation checklist:

  • Add isLayoutEditMode state and gate by editor permission.

  • Read tablo.layout_overview_v1, sanitize via sanitizeOverviewLayout.

  • Render overview cards from zone arrays (not hardcoded order).

  • Add drag handlers in edit mode (same-zone reorder only).

  • On drop: optimistic state update + useUpdateTablo({ id, layout_overview_v1 }).

  • On mutation error: rollback previous layout + toast error.

  • Add Reset default layout button in edit mode.

  • Step 5: Re-run test file and fix until green

Run: pnpm --filter @xtablo/main test -- src/pages/tablo-details.layout.test.tsx
Expected: PASS.

  • Step 6: Run nearby regression tests

Run: pnpm --filter @xtablo/main test -- src/pages/tablo.test.tsx
Expected: PASS.

  • Step 7: Commit
git add apps/main/src/pages/tablo-details.tsx apps/main/src/pages/tablo-details.layout.test.tsx
git commit -m "feat(main): add no-code overview reorder mode with autosave"

Chunk 3: API Confidence + End-to-End Verification

Task 3.1: Add API route test for layout payload update

Files:

  • Modify: apps/api/src/__tests__/routes/tablo.test.ts

  • Test: apps/api/src/__tests__/routes/tablo.test.ts

  • Step 1: Write failing API test

it("should allow owner to update layout_overview_v1", async () => {
  const res = await updateTabloRequest(ownerUser, client, "test_tablo_owner_private", {
    layout_overview_v1: {
      version: 1,
      leftZone: ["myTasks", "description"],
      rightZone: ["files", "info"],
    },
  });
  expect(res.status).toBe(200);
});
  • Step 2: Run focused API test to verify behavior

Run: pnpm --filter @xtablo/api test -- src/__tests__/routes/tablo.test.ts
Expected: PASS after migration/types are in place; FAIL before if column not available in test DB.

  • Step 3: If failing due missing migration in test DB, run DB reset once

Run: supabase db reset
Expected: migrations re-applied, test DB aligned.

  • Step 4: Re-run API test

Run: pnpm --filter @xtablo/api test -- src/__tests__/routes/tablo.test.ts
Expected: PASS.

  • Step 5: Commit
git add apps/api/src/__tests__/routes/tablo.test.ts
git commit -m "test(api): cover tablo layout_overview_v1 updates"

Task 3.2: Final verification sweep before merge

Files:

  • Modify: none (verification only)

  • Step 1: Run main app tests for changed files

Run: pnpm --filter @xtablo/main test -- src/pages/tablo-details/overviewLayout.test.ts src/pages/tablo-details/overviewReorder.test.ts src/pages/tablo-details.layout.test.tsx
Expected: PASS.

  • Step 2: Run API test for changed route test file

Run: pnpm --filter @xtablo/api test -- src/__tests__/routes/tablo.test.ts
Expected: PASS.

  • Step 3: Run typecheck for touched apps

Run: pnpm --filter @xtablo/main typecheck && pnpm --filter @xtablo/api typecheck
Expected: PASS.

  • Step 4: Run format/lint on touched workspaces if needed

Run: pnpm --filter @xtablo/main lint && pnpm --filter @xtablo/api lint
Expected: PASS or only pre-existing unrelated failures.

  • Step 5: Squash fixups if needed and prepare PR summary
git log --oneline --max-count=8

Expected: clean, reviewable commit sequence.


Notes for Execution Agent

  • Follow @superpowers/test-driven-development for each code task (tests first).
  • Before declaring completion, follow @superpowers/verification-before-completion.
  • Keep edits localized; do not refactor unrelated tabs/components.
  • If a file grows too large during Task 2.3, extract small focused helpers instead of adding complexity in-place.