fix(projects): surface API errors instead of silently rendering empty list

Made-with: Cursor
This commit is contained in:
2026-04-28 17:47:34 -07:00
parent 6fca78dca9
commit ba69a78a5f

View File

@@ -73,6 +73,7 @@ export default function ProjectsPage() {
const [projects, setProjects] = useState<ProjectWithStats[]>([]);
const [loading, setLoading] = useState(true);
const [loadError, setLoadError] = useState<string | null>(null);
const [showNew, setShowNew] = useState(false);
const [projectToDelete, setProjectToDelete] = useState<ProjectWithStats | null>(null);
const [isDeleting, setIsDeleting] = useState(false);
@@ -81,12 +82,20 @@ export default function ProjectsPage() {
const fetchProjects = async () => {
try {
setLoading(true);
const res = await fetch("/api/projects");
if (!res.ok) throw new Error("Failed to fetch projects");
setLoadError(null);
const res = await fetch("/api/projects", { credentials: "include" });
if (res.status === 401) {
throw new Error("Your session expired — please log in again.");
}
if (!res.ok) {
let body: { error?: string; details?: string } = {};
try { body = await res.json(); } catch { /* keep {} */ }
throw new Error(body.error || `HTTP ${res.status} ${res.statusText}`.trim());
}
const data = await res.json();
setProjects(data.projects ?? []);
} catch {
/* silent */
} catch (err) {
setLoadError(err instanceof Error ? err.message : "Failed to load projects");
} finally {
setLoading(false);
}
@@ -174,6 +183,29 @@ export default function ProjectsPage() {
</div>
)}
{/* Error */}
{!loading && loadError && (
<div style={{
marginBottom: 24, padding: "14px 18px",
background: "#fdecea", border: "1px solid #f4c4be",
borderRadius: 10, color: "#8b2a1f", fontSize: "0.85rem",
display: "flex", alignItems: "center", gap: 12,
}}>
<span style={{ flex: 1 }}>{loadError}</span>
<button
onClick={fetchProjects}
style={{
padding: "5px 12px", borderRadius: 6,
background: "#8b2a1f", color: "#fff", border: "none",
fontSize: "0.78rem", fontWeight: 600, cursor: "pointer",
fontFamily: "var(--font-inter), ui-sans-serif, sans-serif",
}}
>
Retry
</button>
</div>
)}
{/* Project list */}
{!loading && (
<div style={{ display: "flex", flexDirection: "column", gap: 8 }}>