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
57 lines
1.6 KiB
TypeScript
57 lines
1.6 KiB
TypeScript
import type { ComponentSummary, HealthStatus } from "@/types"
|
|
import { ComponentCard } from "./ComponentCard"
|
|
|
|
const ROLE_ORDER = ["service", "tool", "worker", "job", "frontend", "remote", "containerized"]
|
|
const ROLE_LABELS: Record<string, string> = {
|
|
service: "Services",
|
|
tool: "Tools",
|
|
worker: "Workers",
|
|
job: "Jobs",
|
|
frontend: "Frontends",
|
|
remote: "Remote",
|
|
containerized: "Containers",
|
|
}
|
|
|
|
interface ComponentGridProps {
|
|
components: ComponentSummary[]
|
|
statuses: HealthStatus[]
|
|
}
|
|
|
|
export function ComponentGrid({ components, statuses }: ComponentGridProps) {
|
|
const statusMap = new Map(statuses.map((s) => [s.id, s]))
|
|
|
|
// Group by primary role
|
|
const groups = new Map<string, ComponentSummary[]>()
|
|
for (const comp of components) {
|
|
const primary = comp.roles[0] ?? "tool"
|
|
const list = groups.get(primary) ?? []
|
|
list.push(comp)
|
|
groups.set(primary, list)
|
|
}
|
|
|
|
return (
|
|
<div className="space-y-8">
|
|
{ROLE_ORDER.map((role) => {
|
|
const items = groups.get(role)
|
|
if (!items?.length) return null
|
|
return (
|
|
<section key={role}>
|
|
<h2 className="text-lg font-semibold mb-3 text-[var(--muted)]">
|
|
{ROLE_LABELS[role] ?? role}
|
|
</h2>
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
{items.map((comp) => (
|
|
<ComponentCard
|
|
key={comp.id}
|
|
component={comp}
|
|
health={statusMap.get(comp.id)}
|
|
/>
|
|
))}
|
|
</div>
|
|
</section>
|
|
)
|
|
})}
|
|
</div>
|
|
)
|
|
}
|