feat: add ComponentGrid and ComponentTable components for displaying components in grid and table formats

feat: implement HealthBadge and RoleBadge components for displaying health status and roles

feat: create LogViewer component for streaming logs of services

feat: develop SecretsEditor for managing secrets with CRUD operations

feat: introduce ToolCard component for displaying tool information

style: add global CSS variables and styles for consistent theming

feat: set up API client for handling requests to the backend

feat: implement hooks for fetching components, statuses, and tools

feat: create routes for dashboard, component details, and tools

feat: add service management endpoints for starting, stopping, and restarting services

feat: implement event bus for handling real-time updates via SSE

feat: create health check and logs endpoints for monitoring services

feat: add tests for health and tools endpoints to ensure functionality

chore: update project configuration files and dependencies for better development experience
This commit is contained in:
2026-02-21 01:23:37 -08:00
parent f99c2dad86
commit 930bc601b7
63 changed files with 154 additions and 157 deletions

View File

@@ -0,0 +1,64 @@
const BASE_URL = import.meta.env.VITE_API_BASE_URL ?? "/api"
class ApiError extends Error {
status: number
constructor(status: number, message: string) {
super(message)
this.status = status
}
}
class ApiClient {
private baseUrl: string
constructor(baseUrl = BASE_URL) {
this.baseUrl = baseUrl
}
async get<T>(path: string): Promise<T> {
const resp = await fetch(`${this.baseUrl}${path}`)
if (!resp.ok) {
throw new ApiError(resp.status, await resp.text())
}
return resp.json()
}
async post<T>(path: string, body?: unknown): Promise<T> {
const resp = await fetch(`${this.baseUrl}${path}`, {
method: "POST",
headers: body ? { "Content-Type": "application/json" } : {},
body: body ? JSON.stringify(body) : undefined,
})
if (!resp.ok) {
throw new ApiError(resp.status, await resp.text())
}
return resp.json()
}
async put<T>(path: string, body?: unknown): Promise<T> {
const resp = await fetch(`${this.baseUrl}${path}`, {
method: "PUT",
headers: body ? { "Content-Type": "application/json" } : {},
body: body ? JSON.stringify(body) : undefined,
})
if (!resp.ok) {
throw new ApiError(resp.status, await resp.text())
}
return resp.json()
}
async delete<T>(path: string): Promise<T> {
const resp = await fetch(`${this.baseUrl}${path}`, { method: "DELETE" })
if (!resp.ok) {
throw new ApiError(resp.status, await resp.text())
}
return resp.json()
}
streamUrl(path: string): string {
return `${this.baseUrl}${path}`
}
}
export const apiClient = new ApiClient()
export { ApiError }