phase 16: React dashboard with full platform control and analytics

This commit is contained in:
Celes Renata
2026-04-11 16:19:46 -07:00
parent 25e0e386b7
commit faccb0b8db
53 changed files with 7924 additions and 13 deletions
+79
View File
@@ -0,0 +1,79 @@
/**
* Shared API client targeting Query API, Symbol Registry, and Risk Engine.
* Base URLs are configurable via env vars (VITE_*).
*/
const QUERY_API_BASE = import.meta.env.VITE_QUERY_API_URL ?? '';
const SYMBOL_REGISTRY_BASE = import.meta.env.VITE_SYMBOL_REGISTRY_URL ?? '';
const RISK_ENGINE_BASE = import.meta.env.VITE_RISK_ENGINE_URL ?? '';
export type ApiBase = 'query' | 'registry' | 'risk';
function baseUrl(api: ApiBase): string {
switch (api) {
case 'query':
return QUERY_API_BASE;
case 'registry':
return SYMBOL_REGISTRY_BASE;
case 'risk':
return RISK_ENGINE_BASE;
}
}
export class ApiError extends Error {
status: number;
body: unknown;
constructor(status: number, body: unknown) {
super(`API error ${status}`);
this.name = 'ApiError';
this.status = status;
this.body = body;
}
}
export async function apiFetch<T>(
api: ApiBase,
path: string,
init?: RequestInit,
): Promise<T> {
const url = `${baseUrl(api)}${path}`;
const res = await fetch(url, {
...init,
headers: {
'Content-Type': 'application/json',
...init?.headers,
},
});
if (!res.ok) {
const body = await res.json().catch(() => null);
throw new ApiError(res.status, body);
}
return res.json() as Promise<T>;
}
/** Convenience GET */
export function apiGet<T>(api: ApiBase, path: string): Promise<T> {
return apiFetch<T>(api, path);
}
/** Convenience POST */
export function apiPost<T>(api: ApiBase, path: string, body: unknown): Promise<T> {
return apiFetch<T>(api, path, {
method: 'POST',
body: JSON.stringify(body),
});
}
/** Convenience PUT */
export function apiPut<T>(api: ApiBase, path: string, body: unknown): Promise<T> {
return apiFetch<T>(api, path, {
method: 'PUT',
body: JSON.stringify(body),
});
}
/** Convenience DELETE */
export function apiDelete<T>(api: ApiBase, path: string): Promise<T> {
return apiFetch<T>(api, path, { method: 'DELETE' });
}