Co-Authored-By: Claude Haiku 4.5 <[email protected]>
71 lines
2.2 KiB
TypeScript
71 lines
2.2 KiB
TypeScript
import { useEffect, useState, type ReactElement } from 'react';
|
|
import { useNavigate } from 'react-router-dom';
|
|
import { ProjectCard } from '@/components/projects/project-card';
|
|
import { api } from '@/lib/api';
|
|
import type { Project } from '@/lib/types';
|
|
import { getErrorMessage } from './page-errors';
|
|
|
|
export function ProjectsPage(): ReactElement {
|
|
const navigate = useNavigate();
|
|
const [projects, setProjects] = useState<Project[]>([]);
|
|
const [loading, setLoading] = useState(true);
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
useEffect(() => {
|
|
let cancelled = false;
|
|
|
|
void api<Project[]>('/api/projects')
|
|
.then((response) => {
|
|
if (cancelled) return;
|
|
setProjects(response);
|
|
})
|
|
.catch((caught: unknown) => {
|
|
if (cancelled) return;
|
|
setError(getErrorMessage(caught, 'Failed to load projects.'));
|
|
})
|
|
.finally(() => {
|
|
if (cancelled) return;
|
|
setLoading(false);
|
|
});
|
|
|
|
return () => {
|
|
cancelled = true;
|
|
};
|
|
}, []);
|
|
|
|
return (
|
|
<div className="flex min-h-screen flex-col px-4 py-6 sm:px-6">
|
|
<header className="mb-6 border-b px-1 pb-3">
|
|
<h1 className="text-2xl font-semibold">Projects</h1>
|
|
</header>
|
|
|
|
{error ? (
|
|
<div role="alert" className="mb-6 rounded-lg border border-error/40 px-4 py-3 text-sm">
|
|
{error}
|
|
</div>
|
|
) : null}
|
|
|
|
{loading ? (
|
|
<p className="py-8 text-center text-sm text-text-muted">Loading projects...</p>
|
|
) : projects.length === 0 ? (
|
|
<div className="py-12 text-center">
|
|
<h2 className="text-lg font-medium text-text-secondary">No projects yet</h2>
|
|
<p className="mt-1 text-sm text-text-muted">
|
|
Projects will appear here when created via the gateway API
|
|
</p>
|
|
</div>
|
|
) : (
|
|
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
|
{projects.map((project) => (
|
|
<ProjectCard
|
|
key={project.id}
|
|
project={project}
|
|
onClick={(selectedProject) => navigate(`/projects/${selectedProject.id}`)}
|
|
/>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|