feat: add sidebar

This commit is contained in:
Austin Pickett 2026-04-22 23:25:17 -04:00
parent 7db2703b33
commit e5d2815b41
41 changed files with 2469 additions and 1391 deletions

View file

@ -0,0 +1,41 @@
import { useCallback, useState } from "react";
export function useConfirmDelete<TId>({
onDelete,
}: {
onDelete: (id: TId) => Promise<void>;
}) {
const [pendingId, setPendingId] = useState<TId | null>(null);
const [isDeleting, setIsDeleting] = useState(false);
const requestDelete = useCallback((id: TId) => {
setPendingId(id);
}, []);
const cancel = useCallback(() => {
if (!isDeleting) setPendingId(null);
}, [isDeleting]);
const confirm = useCallback(async () => {
if (pendingId === null) return;
const id = pendingId;
setIsDeleting(true);
try {
await onDelete(id);
setPendingId(null);
} catch {
// Dialog stays open; caller can surface errors in onDelete before rethrowing
} finally {
setIsDeleting(false);
}
}, [pendingId, onDelete]);
return {
cancel,
confirm,
isDeleting,
isOpen: pendingId !== null,
pendingId,
requestDelete,
} as const;
}

View file

@ -0,0 +1,27 @@
import { useEffect, useState } from "react";
import { api } from "@/lib/api";
import type { StatusResponse } from "@/lib/api";
const POLL_MS = 10_000;
/**
* Light-weight status poll for the app shell (sidebar). The Status page uses
* its own faster interval; we keep this slower to avoid duplicate load.
*/
export function useSidebarStatus() {
const [status, setStatus] = useState<StatusResponse | null>(null);
useEffect(() => {
const load = () => {
api
.getStatus()
.then(setStatus)
.catch(() => {});
};
load();
const id = setInterval(load, POLL_MS);
return () => clearInterval(id);
}, []);
return status;
}