Co-authored-by: Jason Woltje <jason@diversecanvas.com> Co-committed-by: Jason Woltje <jason@diversecanvas.com>
127 lines
3.6 KiB
TypeScript
127 lines
3.6 KiB
TypeScript
"use client";
|
|
|
|
import { useState, useEffect } from "react";
|
|
import type { ReactElement } from "react";
|
|
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";
|
|
import { fetchDashboardSummary } from "@/lib/api/dashboard";
|
|
import type { DashboardSummaryResponse } from "@/lib/api/dashboard";
|
|
import { useWorkspaceId } from "@/lib/hooks";
|
|
|
|
export default function DashboardPage(): ReactElement {
|
|
const workspaceId = useWorkspaceId();
|
|
const [data, setData] = useState<DashboardSummaryResponse | null>(null);
|
|
const [isLoading, setIsLoading] = useState(true);
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
useEffect(() => {
|
|
if (!workspaceId) {
|
|
setIsLoading(false);
|
|
return;
|
|
}
|
|
|
|
const wsId = workspaceId;
|
|
let cancelled = false;
|
|
setError(null);
|
|
setIsLoading(true);
|
|
|
|
async function loadSummary(): Promise<void> {
|
|
try {
|
|
const summary = await fetchDashboardSummary(wsId);
|
|
if (!cancelled) {
|
|
setData(summary);
|
|
}
|
|
} catch (err: unknown) {
|
|
console.error("[Dashboard] Failed to fetch summary:", err);
|
|
if (!cancelled) {
|
|
setError("Failed to load dashboard data");
|
|
}
|
|
} finally {
|
|
if (!cancelled) {
|
|
setIsLoading(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
void loadSummary();
|
|
|
|
return (): void => {
|
|
cancelled = true;
|
|
};
|
|
}, [workspaceId]);
|
|
|
|
useEffect(() => {
|
|
if (!workspaceId) return;
|
|
|
|
let cancelled = false;
|
|
const wsId = workspaceId;
|
|
|
|
const interval = setInterval(() => {
|
|
fetchDashboardSummary(wsId)
|
|
.then((summary) => {
|
|
if (!cancelled) setData(summary);
|
|
})
|
|
.catch((err: unknown) => {
|
|
console.error("[Dashboard] Refresh failed:", err);
|
|
});
|
|
}, 30_000);
|
|
|
|
return (): void => {
|
|
cancelled = true;
|
|
clearInterval(interval);
|
|
};
|
|
}, [workspaceId]);
|
|
|
|
if (isLoading) {
|
|
return (
|
|
<div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
|
|
<DashboardMetrics />
|
|
<div className="dash-grid">
|
|
<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>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
|
|
{error && (
|
|
<div
|
|
style={{
|
|
padding: "12px 16px",
|
|
marginBottom: 16,
|
|
background: "rgba(229,72,77,0.1)",
|
|
border: "1px solid var(--border)",
|
|
borderRadius: "var(--r)",
|
|
color: "var(--text)",
|
|
fontSize: "0.85rem",
|
|
}}
|
|
>
|
|
{error}
|
|
</div>
|
|
)}
|
|
<DashboardMetrics metrics={data?.metrics} />
|
|
<div className="dash-grid">
|
|
<div style={{ display: "flex", flexDirection: "column", gap: 16, minWidth: 0 }}>
|
|
<OrchestratorSessions jobs={data?.activeJobs} />
|
|
<QuickActions />
|
|
</div>
|
|
<div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
|
|
<ActivityFeed items={data?.recentActivity} />
|
|
<TokenBudget budgets={data?.tokenBudget} />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|