-
Projects
-
Manage your prompt tuning projects.
+
+ );
+}
+
+// ---------------------------------------------------------------------------
+// Project Card
+// ---------------------------------------------------------------------------
+
+function ProjectCard({
+ project,
+ onClick,
+}: {
+ project: ProjectResponse;
+ onClick: () => void;
+}) {
+ const updatedDate = new Date(project.updated_at);
+ const timeAgo = formatTimeAgo(updatedDate);
+
+ return (
+
+ );
+}
+
+// ---------------------------------------------------------------------------
+// Helper
+// ---------------------------------------------------------------------------
+
+function formatTimeAgo(date: Date): string {
+ const now = Date.now();
+ const diffMs = now - date.getTime();
+ const diffSec = Math.floor(diffMs / 1000);
+
+ if (diffSec < 60) return "just now";
+ const diffMin = Math.floor(diffSec / 60);
+ if (diffMin < 60) return `${diffMin}m ago`;
+ const diffHr = Math.floor(diffMin / 60);
+ if (diffHr < 24) return `${diffHr}h ago`;
+ const diffDay = Math.floor(diffHr / 24);
+ if (diffDay < 30) return `${diffDay}d ago`;
+ return date.toLocaleDateString();
+}
+
+// ---------------------------------------------------------------------------
+// Projects Page
+// ---------------------------------------------------------------------------
+
+export default function ProjectsPage() {
+ const navigate = useNavigate();
+ const [projectList, setProjectList] = useState
([]);
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState(null);
+ const [modalOpen, setModalOpen] = useState(false);
+
+ const loadProjects = useCallback(async () => {
+ setLoading(true);
+ setError(null);
+ try {
+ const resp = await projects.list();
+ setProjectList(resp.items);
+ } catch (err: unknown) {
+ if (err instanceof ApiError) {
+ setError(`Failed to load projects (${err.status}).`);
+ } else {
+ setError("Network error. Is the server running?");
+ }
+ } finally {
+ setLoading(false);
+ }
+ }, []);
+
+ useEffect(() => {
+ loadProjects();
+ }, [loadProjects]);
+
+ function handleProjectCreated(p: ProjectResponse) {
+ setProjectList((prev) => [p, ...prev]);
+ setModalOpen(false);
+ }
+
+ return (
+
+
+ {/* Header */}
+
+
+
+ Projects
+
+
+ Manage your prompt tuning projects.
+
+
+
+
+
+ {/* Loading state */}
+ {loading && (
+
+ Loading projects…
+
+ )}
+
+ {/* Error state */}
+ {!loading && error && (
+
+ )}
+
+ {/* Empty state */}
+ {!loading && !error && projectList.length === 0 && (
+
+
+
+ No projects yet
+
+
+ Create your first project to start tuning prompts.
+
+
+
+ )}
+
+ {/* Project grid */}
+ {!loading && !error && projectList.length > 0 && (
+
+ {projectList.map((project) => (
+
navigate(`/experiments/${project.id}`)}
+ />
+ ))}
+
+ )}
+
+
+ {/* New Project Modal */}
+
setModalOpen(false)}
+ onCreated={handleProjectCreated}
+ />
);
}