# Data Model: Dataset Lifecycle Workspace ## Entity Overview ``` GET /api/datasets?filter=all&page=1&page_size=10 GET /api/datasets/{id} ───────────────────────────────────────────────────────────── DatasetsResponse DatasetDetailResponse ├── datasets: DatasetItem[] ├── ... (existing fields) │ ├── id ├── metrics: MetricItem[] │ ├── table_name │ ├── id │ ├── schema │ ├── metric_name │ ├── database │ ├── expression │ ├── metric_count │ ├── verbose_name │ ├── mapped_fields │ ├── description │ └── last_task │ └── metric_type ├── stats: StatsCounts └── metric_count │ ├── total │ ├── unmapped_count │ ├── mapped_count │ └── linked_count ├── total, page, page_size, │ total_pages (existing) └── [unchanged pagination] PUT /api/datasets/{id}/columns/{col_id}/description PUT /api/datasets/{id}/metrics/{metric_id}/description ───────────────────────────────────────────────────────────── Request: ColumnDescriptionUpdate | MetricDescriptionUpdate { description: string } Response: { success: true, description: string } ``` --- ## Backend Pydantic Schemas (in `backend/src/api/routes/datasets.py`) ### MetricItem (new) ```python class MetricItem(BaseModel): id: int metric_name: str expression: str | None = None verbose_name: str | None = None description: str | None = None metric_type: str | None = None ``` ### StatsCounts (new) ```python class StatsCounts(BaseModel): total: int # all datasets unmapped_count: int # mapped_fields.mapped == 0 mapped_count: int # mapped_fields.mapped > 0 linked_count: int # linked_dashboard_count > 0 ``` ### DatasetsResponse (modified) ```python class DatasetsResponse(BaseModel): datasets: list[DatasetItem] stats: StatsCounts # NEW total: int page: int page_size: int total_pages: int ``` ### DatasetDetailResponse (modified) ```python class DatasetDetailResponse(BaseModel): # ... existing fields unchanged ... metrics: list[MetricItem] # NEW metric_count: int # NEW ``` ### ColumnDescriptionUpdate (new) ```python class ColumnDescriptionUpdate(BaseModel): description: str ``` ### MetricDescriptionUpdate (new) ```python class MetricDescriptionUpdate(BaseModel): description: str ``` --- ## Frontend Types (TypeScript / Svelte inferred) ```typescript interface StatsCounts { total: number; unmapped_count: number; mapped_count: number; linked_count: number; } interface MetricItem { id: number; metric_name: string; expression?: string; verbose_name?: string; description?: string; metric_type?: string; } // UI-only state types (not in API) type ColumnRow = { column_name: string; verbose_name?: string; type?: string; description?: string; is_dttm: boolean; is_active: boolean; id: number; isEditing?: boolean; // UI state for inline edit }; type MetricRow = { metric_name: string; expression?: string; verbose_name?: string; description?: string; metric_type?: string; id: number; isEditing?: boolean; // UI state for inline edit }; type StatsBarFilter = 'all' | 'unmapped' | 'mapped' | 'linked' | null; // API query parameter for server-side filtering // GET /api/datasets?filter=unmapped|mapped|linked|all&page=1&page_size=10 // When filter is active, backend filters full dataset list before pagination ``` --- ## WebSocket Message ```json { "type": "dataset.updated", "payload": { "env_id": "ss-dev", "dataset_ids": [32, 45, 78] } } ``` --- ## State Transitions ### Stats Bar Tile States ``` Idle → (click tile) → Active (highlighted, list filtered) Active → (click same tile) → Idle (filter cleared) Active → (click different tile) → Active (new tile, list re-filtered) ``` ### Inline Edit States ``` Display → (click ✏️) → Editing (textarea visible) Editing → (Save + API success) → Display (new description shown) Editing → (Save + API error) → Editing (error message, textarea stays) Editing → (Cancel) → Display (original description restored) ``` ### Detail Panel States ``` NoSelection → (click card) → Loading → Loaded Loaded → (click different card) → Loading → Loaded Loaded → (deselect / bulk mode) → NoSelection Loading → (API error) → Error → (Retry) → Loading ``` ### Bulk Selection States ``` None → (check 1+ cards) → Selecting (bottom panel hidden for 1) Selecting → (check 2+ cards) → BulkActive (bottom panel visible) BulkActive → (uncheck all) → None BulkActive → (click action) → Modal → (complete) → None ```