- Endpoint:
/api/v1/external-dependenciesviagetExternalDependencies()inservices/api.ts - Response Shape:
{ dependencies: ExternalDependency[], summary: { healthy: number, degraded: number, down: number, maintenance: number, unknown: number } }
- Status Values: "healthy" | "degraded" | "down" | "maintenance" | "unknown"
- Fetching Pattern: React Query
useQuerywith 60-second polling interval (matching existingExternalDependencyPanel)
- Framework: React 18 with TypeScript
- Styling: TailwindCSS with dark mode via
classstrategy - Theme Mechanism: CSS variables (
--stellar-*) defined inindex.css, accessed via Tailwind utility classes - Status Colors (following existing patterns):
- Healthy:
bg-green-500,text-green-400 - Degraded:
bg-yellow-500/bg-amber-500,text-yellow-400/text-amber-400 - Down:
bg-red-500,text-red-400 - Maintenance:
bg-blue-500,text-blue-400 - Unknown:
bg-gray-500,text-gray-400
- Healthy:
Following the established pattern in frontend/src/components/:
- File:
frontend/src/components/ServiceHealthPulse.tsx - Props Interface: Inline TypeScript interface with:
compact?: boolean(default: true)className?: string(for custom styling)
- Export: Default export of main component, named export of skeleton
- Compact Mode (default):
- Single pulse indicator (animated dot) showing overall status
- Brief status label ("All systems operational", "Degraded", "Service disruption", "Maintenance", "Unknown")
- Service count summary (e.g., "5 services")
- Last updated timestamp (relative time)
- Expand/collapse toggle button
- Detailed Mode (expanded):
- All compact mode content
- Per-service breakdown list with:
- Service name
- Individual status indicator (colored dot)
- Status label
- Smooth CSS transition on max-height for expansion animation
Following worst-case aggregation (matching existing patterns):
- If any service is "down" → overall status is "down"
- Else if any service is "degraded" → overall status is "degraded"
- Else if any service is "maintenance" → overall status is "maintenance"
- Else if all services are "healthy" → overall status is "healthy"
- Else → overall status is "unknown"
- ARIA Roles:
role="status"on overall pulse indicatorrole="list"androle="listitem"for service breakdown
- Live Regions:
aria-live="polite"on overall status for screen reader announcements - Color Independence:
- Every status indicator includes text label
- Status dots have
aria-hidden="true"with adjacent text
- Keyboard Navigation:
- Expand/collapse button is keyboard accessible
aria-expandedattribute on toggle buttonaria-controlslinking button to content region
- Focus Indicators: Tailwind
focus:ring-2 focus:ring-stellar-blueon interactive elements
frontend/src/components/ServiceHealthPulse.tsx— Main widget componentfrontend/src/components/ServiceHealthPulse.test.tsx— Component testsfrontend/src/hooks/useServiceHealth.ts— Data fetching hookfrontend/src/hooks/useServiceHealth.test.ts— Hook testsfrontend/docs/service-health-pulse-widget.md— Component documentation
frontend/src/test/mocks/handlers.ts— Add mock for/api/v1/external-dependenciesendpoint
- Create
useServiceHealthhook with React Query - Create
ServiceHealthPulsecomponent with compact/detailed modes - Write comprehensive tests for hook and component
- Add MSW mock handler for testing
- Create component documentation
- Run all CI checks locally before PR
-
npm run lint— Zero errors -
npm run build— Successful build -
npm run test— All tests pass - Type-check via build — Zero errors
- Accessibility — No vitest-axe violations