b3e259fc25
Check Cross-Plugin Imports / check (push) Has been cancelled
- dashboards-Tabelle (Layout JSONB, Tabs, is_default, partial unique name index) - 6 CRUD-Endpoints /api/v1/dashboards, Owner-only (saved_views-Präzedenz), Audit - Lazy Default-Seed aus MiniApp-Registry (permission-gefiltert, 12-Spalten-Flow) - CORE_PERMISSIONS dashboard:read/write (fixt Phantom-Permission in dashboard.py) - Migration 0144: RLS crm_api+crm_worker + konvergenter Fix der 3 Phase-L-Policies - Tests: test_dashboards_backend.py 23/23 (TDD rot->grün); Regression 162/163
60 lines
1.7 KiB
Python
60 lines
1.7 KiB
Python
"""Schemas for personal dashboards (Phase M2).
|
|
|
|
Layout is validated structurally (12-column grid bounds) before it is
|
|
persisted; MiniApp existence and permissions are enforced at seed time and
|
|
when the frontend resolves widget components via /api/v1/miniapps.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
from pydantic import BaseModel, Field
|
|
|
|
|
|
class DashboardWidget(BaseModel):
|
|
"""A MiniApp instance placed on a dashboard tab."""
|
|
|
|
app_id: str = Field(..., min_length=1, max_length=80)
|
|
settings: dict[str, Any] = Field(default_factory=dict)
|
|
col: int = Field(default=0, ge=0)
|
|
row: int = Field(default=0, ge=0)
|
|
col_span: int = Field(default=1, ge=1, le=12)
|
|
row_span: int = Field(default=1, ge=1, le=12)
|
|
|
|
|
|
class DashboardTab(BaseModel):
|
|
"""A tab of a dashboard holding widget placements."""
|
|
|
|
id: str = Field(..., min_length=1, max_length=80)
|
|
name: str = Field(..., min_length=1, max_length=100)
|
|
widgets: list[DashboardWidget] = Field(default_factory=list)
|
|
|
|
|
|
class DashboardLayout(BaseModel):
|
|
"""Full persisted layout of a personal dashboard."""
|
|
|
|
version: int = Field(default=1, ge=1)
|
|
tabs: list[DashboardTab] = Field(default_factory=list)
|
|
|
|
|
|
class DashboardCreate(BaseModel):
|
|
name: str = Field(..., min_length=1, max_length=100)
|
|
|
|
|
|
class DashboardUpdate(BaseModel):
|
|
name: str | None = Field(default=None, min_length=1, max_length=100)
|
|
layout: DashboardLayout | None = None
|
|
|
|
|
|
class DashboardResponse(BaseModel):
|
|
id: str
|
|
name: str
|
|
layout: dict[str, Any]
|
|
is_default: bool
|
|
user_id: str
|
|
created_at: str | None = None
|
|
updated_at: str | None = None
|
|
|
|
model_config = {"from_attributes": True}
|