feat(web): Phase 3 — Dashboard Page (#450) (#453)
Some checks failed
ci/woodpecker/push/web Pipeline failed

Co-authored-by: Jason Woltje <jason@diversecanvas.com>
Co-committed-by: Jason Woltje <jason@diversecanvas.com>
This commit was merged in pull request #453.
This commit is contained in:
2026-02-22 21:18:50 +00:00
committed by jason.woltje
parent 716f230f72
commit b43e860c40
7 changed files with 677 additions and 74 deletions

View File

@@ -1,78 +1,32 @@
"use client";
import { useState, useEffect } from "react";
import type { ReactElement } from "react";
import { RecentTasksWidget } from "@/components/dashboard/RecentTasksWidget";
import { UpcomingEventsWidget } from "@/components/dashboard/UpcomingEventsWidget";
import { QuickCaptureWidget } from "@/components/dashboard/QuickCaptureWidget";
import { DomainOverviewWidget } from "@/components/dashboard/DomainOverviewWidget";
import { mockTasks } from "@/lib/api/tasks";
import { mockEvents } from "@/lib/api/events";
import type { Task, Event } from "@mosaic/shared";
import { DashboardMetrics } from "@/components/dashboard/DashboardMetrics";
import { OrchestratorSessions } from "@/components/dashboard/OrchestratorSessions";
import { QuickActions } from "@/components/dashboard/QuickActions";
import { ActivityFeed } from "@/components/dashboard/ActivityFeed";
import { TokenBudget } from "@/components/dashboard/TokenBudget";
export default function DashboardPage(): ReactElement {
const [tasks, setTasks] = useState<Task[]>([]);
const [events, setEvents] = useState<Event[]>([]);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
void loadDashboardData();
}, []);
async function loadDashboardData(): Promise<void> {
setIsLoading(true);
setError(null);
try {
// TODO: Replace with real API calls when backend is ready
// const [tasksData, eventsData] = await Promise.all([fetchTasks(), fetchEvents()]);
await new Promise((resolve) => setTimeout(resolve, 300));
setTasks(mockTasks);
setEvents(mockEvents);
} catch (err) {
setError(
err instanceof Error
? err.message
: "We had trouble loading your dashboard. Please try again when you're ready."
);
} finally {
setIsLoading(false);
}
}
return (
<main className="container mx-auto px-4 py-8">
<div className="mb-8">
<h1 className="text-3xl font-bold text-gray-900">Dashboard</h1>
<p className="text-gray-600 mt-2">Welcome back! Here&apos;s your overview</p>
<div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
<DashboardMetrics />
<div
style={{
display: "grid",
gridTemplateColumns: "1fr 320px",
gap: 16,
}}
>
<div style={{ display: "flex", flexDirection: "column", gap: 16, minWidth: 0 }}>
<OrchestratorSessions />
<QuickActions />
</div>
<div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
<ActivityFeed />
<TokenBudget />
</div>
</div>
{error !== null ? (
<div className="rounded-lg border border-amber-200 bg-amber-50 p-6 text-center">
<p className="text-amber-800">{error}</p>
<button
onClick={() => void loadDashboardData()}
className="mt-4 rounded-md bg-amber-600 px-4 py-2 text-sm font-medium text-white hover:bg-amber-700 transition-colors"
>
Try again
</button>
</div>
) : (
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{/* Top row: Domain Overview and Quick Capture */}
<div className="lg:col-span-2">
<DomainOverviewWidget tasks={tasks} isLoading={isLoading} />
</div>
<RecentTasksWidget tasks={tasks} isLoading={isLoading} />
<UpcomingEventsWidget events={events} isLoading={isLoading} />
<div className="lg:col-span-2">
<QuickCaptureWidget />
</div>
</div>
)}
</main>
</div>
);
}