ServiceHealthCard.tsx 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. import Link from 'next/link'
  2. import { Button, Card, CardContent, CardFooter, CardHeader, CardTitle, cn, Loading } from 'ui'
  3. import { LogsBarChart } from 'ui-patterns/LogsBarChart'
  4. import type { LogsBarChartDatum } from '../ProjectHome/ProjectUsage.metrics'
  5. import { getHealthStatus, type ServiceKey } from './ObservabilityOverview.utils'
  6. import NoDataPlaceholder from '@/components/ui/Charts/NoDataPlaceholder'
  7. const colorClassMap: Record<string, string> = {
  8. muted: 'bg-muted',
  9. destructive: 'bg-destructive',
  10. warning: 'bg-warning',
  11. brand: 'bg-brand',
  12. }
  13. export type ServiceHealthCardProps = {
  14. serviceName: string
  15. serviceKey: ServiceKey
  16. total: number
  17. errorRate: number
  18. errorCount: number
  19. warningCount: number
  20. chartData: LogsBarChartDatum[]
  21. reportUrl?: string // undefined if feature flag disabled or no report available
  22. logsUrl: string
  23. isLoading: boolean
  24. onBarClick: (datum: any) => void
  25. datetimeFormat: string
  26. }
  27. export const ServiceHealthCard = ({
  28. serviceName,
  29. serviceKey: _serviceKey,
  30. total,
  31. errorRate,
  32. errorCount,
  33. warningCount,
  34. chartData,
  35. reportUrl,
  36. logsUrl,
  37. isLoading,
  38. onBarClick,
  39. datetimeFormat,
  40. }: ServiceHealthCardProps) => {
  41. const { color } = getHealthStatus(errorRate, total)
  42. return (
  43. <Card className="mb-0 md:mb-0 h-full flex flex-col">
  44. <CardHeader className="flex flex-row items-start justify-between gap-2 space-y-0 pb-0 border-b-0">
  45. <div className="flex flex-col">
  46. <CardTitle className="text-foreground-light flex items-center gap-2 text-xs font-medium">
  47. <div className={cn('w-1.5 h-1.5 rounded-full', colorClassMap[color] || 'bg-muted')} />
  48. {serviceName}
  49. </CardTitle>
  50. <div className="flex items-start gap-6 mt-2">
  51. <div className="flex flex-col">
  52. <span className="text-foreground text-xl">{total.toLocaleString()}</span>
  53. <span className="text-foreground-light text-xs">requests</span>
  54. </div>
  55. <div className="flex flex-col">
  56. <span className="text-foreground text-xl">{errorRate.toFixed(1)}%</span>
  57. <span className="text-foreground-light text-xs">error rate</span>
  58. </div>
  59. </div>
  60. </div>
  61. <div className="flex items-end gap-4 text-foreground-light">
  62. <div className="flex flex-col items-end">
  63. <div className="flex items-center gap-2">
  64. <div className="w-1.5 h-1.5 bg-warning rounded-full" />
  65. <span className="heading-meta">Warn</span>
  66. </div>
  67. <span className="text-foreground text-xl">{warningCount.toLocaleString()}</span>
  68. </div>
  69. <div className="flex flex-col items-end">
  70. <div className="flex items-center gap-2">
  71. <div className="w-1.5 h-1.5 bg-destructive rounded-full" />
  72. <span className="heading-meta">Err</span>
  73. </div>
  74. <span className="text-foreground text-xl">{errorCount.toLocaleString()}</span>
  75. </div>
  76. </div>
  77. </CardHeader>
  78. <CardContent className="p-card pt-4 flex-1 overflow-hidden max-h-[200px]">
  79. <Loading isFullHeight active={isLoading}>
  80. <LogsBarChart
  81. isFullHeight
  82. data={chartData}
  83. DateTimeFormat={datetimeFormat}
  84. onBarClick={onBarClick}
  85. EmptyState={
  86. <NoDataPlaceholder size="small" message="No data for selected period" isFullHeight />
  87. }
  88. />
  89. </Loading>
  90. </CardContent>
  91. <CardFooter className="border-t pt-3 pb-4 px-card flex gap-2">
  92. {reportUrl && (
  93. <Button type="default" size="tiny" asChild className="flex-1">
  94. <Link href={reportUrl}>View Report</Link>
  95. </Button>
  96. )}
  97. <Button type="default" size="tiny" asChild className="flex-1">
  98. <Link href={logsUrl}>View Logs</Link>
  99. </Button>
  100. </CardFooter>
  101. </Card>
  102. )
  103. }