Files
headquarter/apps/web/src/pages/dashboard.tsx
T
Fusion 6f41fa7cbe feat: implement universal icon system with Phosphor Icons
- Install @phosphor-icons/react package
- Create centralized Icon component with size/weight/color variants
- Create icon registry with 34 icons across 5 categories
- Replace all raw Unicode symbols with proper icon components
- Add icons to navigation, buttons, status indicators, git operations
- Add icon CSS with consistent sizing and spacing
- Fix type definitions for Phosphor icon compatibility

Quality gates: typecheck ✓, lint ✓, build ✓ (375KB bundle)
2026-05-19 19:33:06 +02:00

84 lines
2.4 KiB
TypeScript

import { useCallback, useEffect, useMemo, useState } from "react";
import { getDashboardSummary, type DashboardSummary } from "../api/dashboard";
import { Icon } from "../components/icon";
const CARDS = [
{ label: "Projects", key: "projects" },
{ label: "Repositories", key: "repositories" },
{ label: "SSH Keys", key: "sshKeys" }
] as const;
type DashboardStatus = "loading" | "ready" | "error";
export const DashboardPage = () => {
const [status, setStatus] = useState<DashboardStatus>("loading");
const [summary, setSummary] = useState<DashboardSummary | null>(null);
const loadSummary = useCallback(async () => {
setStatus("loading");
try {
const data = await getDashboardSummary();
setSummary(data);
setStatus("ready");
} catch {
setSummary(null);
setStatus("error");
}
}, []);
useEffect(() => {
void loadSummary();
}, [loadSummary]);
const cards = useMemo(() => CARDS, []);
const isEmpty =
status === "ready" &&
summary !== null &&
summary.projects === 0 &&
summary.repositories === 0 &&
summary.sshKeys === 0 &&
summary.recentActivity.length === 0;
return (
<section className="stack">
<h1>Dashboard</h1>
<p className="muted">Your workspace overview will appear here.</p>
{status === "loading" && <p className="muted">Loading dashboard...</p>}
{status === "error" && (
<div className="card stack">
<p>Dashboard is unavailable</p>
<button className="secondary-button" onClick={() => void loadSummary()} type="button">
<Icon name="refresh" size="sm" />
Retry
</button>
</div>
)}
<div className="card-grid">
{cards.map((card) => (
<article className="card" key={card.label}>
<p className="card-label">{card.label}</p>
<p className="card-value">{summary ? String(summary[card.key]) : "-"}</p>
</article>
))}
</div>
{isEmpty && <p className="muted">No activity yet</p>}
<div className="quick-actions">
<button className="primary-button" type="button">
<Icon name="add" size="sm" />
New Project
</button>
<button className="secondary-button" type="button">
<Icon name="add" size="sm" />
Add Repository
</button>
</div>
</section>
);
};