a37a3122f9
- Extract tool_types validation to shared module (validate_compose_yaml, check_port_exposed, validate_required_variables) - Extract _get_user and _get_owned_project to auth/dependencies.py - Create useAsyncData hook and apply to 6 pages - Create extractErrorMessage utility - TypeScript and build pass
43 lines
1.0 KiB
TypeScript
43 lines
1.0 KiB
TypeScript
import { useCallback, useEffect, useState } from "react";
|
|
|
|
type AsyncStatus = "idle" | "loading" | "ready" | "error";
|
|
|
|
interface UseAsyncDataResult<T> {
|
|
data: T | null;
|
|
status: AsyncStatus;
|
|
error: string | null;
|
|
reload: () => void;
|
|
}
|
|
|
|
export function useAsyncData<T>(
|
|
fetcher: () => Promise<T>,
|
|
deps: React.DependencyList = []
|
|
): UseAsyncDataResult<T> {
|
|
const [data, setData] = useState<T | null>(null);
|
|
const [status, setStatus] = useState<AsyncStatus>("idle");
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
const load = useCallback(async () => {
|
|
setStatus("loading");
|
|
setError(null);
|
|
try {
|
|
const result = await fetcher();
|
|
setData(result);
|
|
setStatus("ready");
|
|
} catch (err) {
|
|
setError(err instanceof Error ? err.message : "Failed to load data");
|
|
setStatus("error");
|
|
}
|
|
}, deps);
|
|
|
|
const reload = useCallback(() => {
|
|
void load();
|
|
}, [load]);
|
|
|
|
useEffect(() => {
|
|
void load();
|
|
}, [load]);
|
|
|
|
return { data, status, error, reload };
|
|
}
|