# Expo App: Tasks & Etapes Feature Design

**Date:** 2026-04-15
**Scope:** Add task and etape (stage) management to the Expo mobile app, reusing the existing Supabase backend.

## Context

The web app (`apps/main`) has a full kanban-based task system with etapes (parent tasks/stages). The Expo app (`xtablo-expo/`) currently has no task management. This spec brings task and etape CRUD to mobile with a mobile-native UX.

### Key Backend Facts

- **No REST API for tasks** — the web app queries Supabase directly via PostgREST. The mobile app will do the same.
- **Etapes are tasks with `is_parent: true`** — not a separate table. Child tasks link via `parent_task_id`.
- **View:** `tasks_with_assignee` joins assignee profile info (name, avatar).
- **RLS** handles authorization — no manual user filtering needed.
- **Task statuses:** `todo`, `in_progress`, `in_review`, `done`.

## Navigation & Entry Point

### Tablo Detail Screen

New screen: `app/(app)/tablo/[id].tsx`

Accessed by tapping a tablo in the existing tablos list. Contains:
- Header: tablo name, color indicator, status badge
- Task list as primary content (described below)

This screen becomes the natural place to add more sections later (files, members, settings).

**Route update:** The tablos list screen (`app/(app)/(tabs)/tablos.tsx`) navigates to this new screen on tablo tap, instead of the current behavior.

## Task List UI

The task list is organized as **collapsible sections by etape**:

### Section Structure
- Each **etape** renders as a section header: etape title, task count badge, collapse/expand chevron
- A **"Sans Étape"** section at the bottom for tasks with no `parent_task_id`
- Sections are ordered by etape `position` (ascending)

### Within Each Section
- Tasks grouped by status with small color-coded status sub-headers (Todo, In Progress, In Review, Done)
- Tasks ordered by `position` within each status group

### Task Row
Each task row displays:
- Status color dot (blue=todo, yellow=in_progress, purple=in_review, green=done)
- Title (single line, truncated)
- Assignee avatar (small circle, or empty circle placeholder if unassigned)
- Due date badge (gray normally, red if overdue)

### Interactions
- **Tap section header** → collapse/expand the section
- **Tap task row** → navigate to task detail screen
- **Swipe left on task** → reveal delete action with confirmation
- **Pull-to-refresh** → refetch tasks and etapes
- **Floating "+" button** (bottom-right) → navigate to task creation screen

## Task Detail Screen

New screen: `app/(app)/task/[id].tsx`

Used for both creating and editing tasks. Routes:
- **Edit:** `/task/{taskId}?tabloId={tabloId}` — `id` param is the task UUID
- **Create:** `/task/new?tabloId={tabloId}` — `id` param is the literal string `"new"`

### Form Fields

| Field | Input Type | Required | Notes |
|-------|-----------|----------|-------|
| Title | Text input | Yes | Single line |
| Description | Multiline text input | No | Expandable |
| Status | Horizontal segmented control | Yes | 4 options, color-coded. Defaults to "todo" on create. |
| Assignee | Bottom sheet picker | No | Lists tablo members with avatars. "Unassigned" option at top. |
| Etape | Bottom sheet picker | No | Lists etapes for the tablo. "Sans Étape" option at top. |
| Due date | Native date picker | No | iOS DateTimePicker / Android date dialog. Format: YYYY-MM-DD. |

### Behavior
- **Create mode:** Status defaults to "todo". Etape and assignee default to null. "Save" button creates the task via `useCreateTask()`.
- **Edit mode:** All fields pre-populated from existing task data. "Save" button updates via `useUpdateTask()`.
- **Delete:** Red "Delete" button at the bottom of the edit screen. Confirmation dialog before deletion via `useDeleteTask()`.
- **No auto-save** — explicit save button to avoid accidental edits on mobile.

## Etape Management

Managed directly from the tablo detail screen — no separate screen.

### Interactions
- **Long-press etape section header** → action menu with "Edit" and "Delete"
- **"+" button** next to the etape headers area → create new etape
- **Edit** → bottom sheet with: title input, description input, due date picker
- **Delete** → confirmation dialog. Child tasks become orphaned (move to "Sans Étape" section).

### No Reorder on Mobile
Etapes display in `position` order from the database. Manual drag-to-reorder is not implemented on mobile — users can reorder from the web app.

## Tablo Members (Read-Only)

A read-only members fetch to support the task assignee picker:

- Hook: `useTabloMembers(tabloId)`
- Query: Supabase query for tablo members with name, avatar, role
- Used only within the assignee picker bottom sheet
- No invite/manage/remove functionality

## New Files

### Screens

| File | Purpose |
|------|---------|
| `app/(app)/tablo/[id].tsx` | Tablo detail screen with task list |
| `app/(app)/tablo/_layout.tsx` | Layout for tablo routes |
| `app/(app)/task/[id].tsx` | Task detail/create/edit screen |
| `app/(app)/task/_layout.tsx` | Layout for task routes |

### Components

| File | Purpose |
|------|---------|
| `components/TaskList.tsx` | Full task list with etape sections |
| `components/TaskRow.tsx` | Single task row in the list |
| `components/EtapeSection.tsx` | Collapsible etape section header |
| `components/EtapeSheet.tsx` | Bottom sheet for etape create/edit |
| `components/AssigneePicker.tsx` | Bottom sheet for assignee selection |
| `components/EtapePicker.tsx` | Bottom sheet for etape selection |
| `components/StatusControl.tsx` | Horizontal segmented status selector |

### Hooks

| Hook | Query Key | Purpose |
|------|-----------|---------|
| `useTasksByTablo(tabloId)` | `["tasks", "tablo", tabloId]` | Fetch tasks for a tablo (from `tasks_with_assignee` view, `is_parent: false`) |
| `useCreateTask()` | mutation | Insert into `tasks` table |
| `useUpdateTask()` | mutation | Update task fields |
| `useDeleteTask()` | mutation | Hard delete task |
| `useTabloEtapes(tabloId)` | `["tablo-etapes", tabloId]` | Fetch etapes (`is_parent: true`) for a tablo |
| `useCreateEtape()` | mutation | Insert parent task |
| `useUpdateEtape()` | mutation | Update etape fields |
| `useDeleteEtape()` | mutation | Hard delete etape |
| `useTabloMembers(tabloId)` | `["tablo-members", tabloId]` | Fetch members for assignee picker |

### Cache Invalidation

Mutations invalidate:
- Task mutations → `["tasks", "tablo", tabloId]`
- Etape mutations → `["tablo-etapes", tabloId]` and `["tasks", "tablo", tabloId]`

## Out of Scope

- Drag-and-drop reordering (tasks or etapes)
- Kanban board view
- Batch position updates
- File management on tablo detail
- Member management (invite/remove)
- Tablo settings editing
- Subscription/upgrade gating (freemium checks)
- Push notifications for task assignments
