"""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}