# 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<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**

```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.

