# 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** ```sql -- 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** ```sql 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** ```sql 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** ```bash 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** ```bash 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** ```ts 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** ```ts 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** ```bash 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** ```ts 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** ```ts export function moveWithinZone(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** ```bash 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** ```ts 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** ```ts 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** ```bash 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** ```ts 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** ```bash 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** ```bash 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.