feat(web): migrate dashboard to WidgetGrid with layout persistence
Replace the hardcoded dashboard layout with the WidgetGrid system. The dashboard now loads/saves user layouts via the UserLayout API and creates a default layout on first visit with all 7 widgets. Changes: - Add layout API client (fetchDefaultLayout, createLayout, updateLayout) - Add default layout constant with 7-widget arrangement (12-col grid) - Update BaseWidget + WidgetGrid to use CSS variables for theme compat - Add responsive container width measurement via ResizeObserver - Rewrite dashboard page to use WidgetGrid with edit mode toggle - Update all tests for the new dashboard architecture Ref: #488 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
54
apps/web/src/lib/api/layouts.ts
Normal file
54
apps/web/src/lib/api/layouts.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Layout API client — CRUD for user dashboard layouts
|
||||
*/
|
||||
|
||||
import type { UserLayout, WidgetPlacement } from "@mosaic/shared";
|
||||
import { apiGet, apiPost, apiPatch } from "./client";
|
||||
|
||||
export interface CreateLayoutPayload {
|
||||
name: string;
|
||||
isDefault?: boolean;
|
||||
layout: WidgetPlacement[];
|
||||
metadata?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export interface UpdateLayoutPayload {
|
||||
name?: string;
|
||||
isDefault?: boolean;
|
||||
layout?: WidgetPlacement[];
|
||||
metadata?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the user's default layout for the active workspace.
|
||||
* Returns null if no layout exists (404).
|
||||
*/
|
||||
export async function fetchDefaultLayout(workspaceId: string): Promise<UserLayout | null> {
|
||||
try {
|
||||
return await apiGet<UserLayout>("/api/layouts/default", workspaceId);
|
||||
} catch {
|
||||
// 404 = no layout yet — not an error
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new layout.
|
||||
*/
|
||||
export async function createLayout(
|
||||
workspaceId: string,
|
||||
payload: CreateLayoutPayload
|
||||
): Promise<UserLayout> {
|
||||
return apiPost<UserLayout>("/api/layouts", payload, workspaceId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an existing layout (partial patch).
|
||||
*/
|
||||
export async function updateLayout(
|
||||
workspaceId: string,
|
||||
layoutId: string,
|
||||
payload: UpdateLayoutPayload
|
||||
): Promise<UserLayout> {
|
||||
return apiPatch<UserLayout>(`/api/layouts/${layoutId}`, payload, workspaceId);
|
||||
}
|
||||
Reference in New Issue
Block a user