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çutabs.
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_v1column onpublic.tablos.
- Responsibility: add nullable JSONB
- Modify:
supabase/tests/database/01_schema_structure.test.sql- Responsibility: assert new column exists and type is
jsonb(schema contract safety).
- Responsibility: assert new column exists and type is
- Modify:
packages/shared-types/src/database.types.ts(generated)- Responsibility: include
layout_overview_v1intablosRow/Insert/Update types.
- Responsibility: include
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/updateaccepts/saveslayout_overview_v1.
- Responsibility: ensure
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_v1appears intablostypes
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
isLayoutEditModestate and gate by editor permission. -
Read
tablo.layout_overview_v1, sanitize viasanitizeOverviewLayout. -
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 layoutbutton 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.