Compute.tsx 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. import { BarChart2 } from 'lucide-react'
  2. import { useMemo } from 'react'
  3. import { GenericSkeletonLoader } from 'ui-patterns/ShimmeringLoader'
  4. import { SectionContent } from './SectionContent'
  5. import { Attribute, AttributeColor } from './Usage.constants'
  6. import { dailyUsageToDataPoints } from './Usage.utils'
  7. import UsageBarChart from './UsageBarChart'
  8. import Panel from '@/components/ui/Panel'
  9. import { DataPoint } from '@/data/analytics/constants'
  10. import {
  11. ComputeUsageMetric,
  12. computeUsageMetricLabel,
  13. type OrgDailyUsageResponse,
  14. } from '@/data/analytics/org-daily-stats-query'
  15. import { useIsFeatureEnabled } from '@/hooks/misc/useIsFeatureEnabled'
  16. import { DOCS_URL } from '@/lib/constants'
  17. export interface ComputeProps {
  18. orgDailyStats: OrgDailyUsageResponse | undefined
  19. isLoadingOrgDailyStats: boolean
  20. }
  21. const Compute = ({ orgDailyStats, isLoadingOrgDailyStats }: ComputeProps) => {
  22. const allAttributeKeys = Object.values(ComputeUsageMetric).map((it) => it.toLowerCase())
  23. const { billingAll } = useIsFeatureEnabled(['billing:all'])
  24. const chartData: DataPoint[] = dailyUsageToDataPoints(orgDailyStats, (metric) =>
  25. metric.toString().startsWith('COMPUTE')
  26. )
  27. const COMPUTE_TO_COLOR: Record<ComputeUsageMetric, AttributeColor> = {
  28. [ComputeUsageMetric.COMPUTE_HOURS_BRANCH]: 'blue',
  29. [ComputeUsageMetric.COMPUTE_HOURS_XS]: 'white',
  30. [ComputeUsageMetric.COMPUTE_HOURS_SM]: 'green',
  31. [ComputeUsageMetric.COMPUTE_HOURS_MD]: 'dark-green',
  32. [ComputeUsageMetric.COMPUTE_HOURS_L]: 'yellow',
  33. [ComputeUsageMetric.COMPUTE_HOURS_XL]: 'dark-yellow',
  34. [ComputeUsageMetric.COMPUTE_HOURS_2XL]: 'orange',
  35. [ComputeUsageMetric.COMPUTE_HOURS_4XL]: 'dark-orange',
  36. [ComputeUsageMetric.COMPUTE_HOURS_8XL]: 'red',
  37. [ComputeUsageMetric.COMPUTE_HOURS_12XL]: 'dark-red',
  38. [ComputeUsageMetric.COMPUTE_HOURS_16XL]: 'purple',
  39. [ComputeUsageMetric.COMPUTE_HOURS_24XL]: 'purple',
  40. [ComputeUsageMetric.COMPUTE_HOURS_24XL_OPTIMIZED_CPU]: 'purple',
  41. [ComputeUsageMetric.COMPUTE_HOURS_24XL_OPTIMIZED_MEMORY]: 'purple',
  42. [ComputeUsageMetric.COMPUTE_HOURS_24XL_HIGH_MEMORY]: 'purple',
  43. [ComputeUsageMetric.COMPUTE_HOURS_48XL]: 'purple',
  44. [ComputeUsageMetric.COMPUTE_HOURS_48XL_OPTIMIZED_CPU]: 'purple',
  45. [ComputeUsageMetric.COMPUTE_HOURS_48XL_OPTIMIZED_MEMORY]: 'purple',
  46. [ComputeUsageMetric.COMPUTE_HOURS_48XL_HIGH_MEMORY]: 'purple',
  47. }
  48. const attributes: Attribute[] = Object.keys(ComputeUsageMetric).map((it) => ({
  49. key: it.toLowerCase(),
  50. color: COMPUTE_TO_COLOR[it as ComputeUsageMetric] || 'white',
  51. name: computeUsageMetricLabel(it as ComputeUsageMetric),
  52. }))
  53. const attributeKeysWithData = useMemo(() => {
  54. return allAttributeKeys.filter((attributeKey) => chartData.some((data) => data[attributeKey]))
  55. }, [chartData])
  56. const notAllValuesZero = useMemo(() => {
  57. return attributeKeysWithData.length > 0
  58. }, [attributeKeysWithData])
  59. return (
  60. <div id="compute" className="scroll-my-12">
  61. <SectionContent
  62. section={{
  63. name: 'Compute Hours',
  64. description:
  65. 'Amount of hours your projects were active. Each project is a dedicated server and database.\nPaid plans come with $10 in Compute Credits to cover one project running on Micro Compute or parts of any compute add-on.\nBilling is based on the sum of Compute Hours used. Paused projects do not count towards usage.',
  66. links: billingAll
  67. ? [
  68. {
  69. name: 'Compute Add-ons',
  70. url: `${DOCS_URL}/guides/platform/compute-add-ons`,
  71. },
  72. {
  73. name: 'Usage-billing for Compute',
  74. url: `${DOCS_URL}/guides/platform/manage-your-usage/compute`,
  75. },
  76. ]
  77. : [],
  78. }}
  79. >
  80. {isLoadingOrgDailyStats && <GenericSkeletonLoader />}
  81. {!isLoadingOrgDailyStats && (
  82. <>
  83. <div className="space-y-1">
  84. {chartData.length > 0 && (
  85. <div className="flex items-center justify-between">
  86. <div className="flex items-center space-x-4">
  87. <p className="text-sm">Compute Hours usage</p>
  88. </div>
  89. </div>
  90. )}
  91. {attributeKeysWithData.map((key) => (
  92. <div
  93. key={key}
  94. className="flex items-center justify-between border-b last:border-b-0 py-1 last:py-0"
  95. >
  96. <p className="text-sm text-foreground-light">
  97. <span className="font-medium">
  98. {computeUsageMetricLabel(key.toUpperCase() as ComputeUsageMetric)}
  99. </span>{' '}
  100. Compute Hours usage in period
  101. </p>
  102. <p className="text-sm">
  103. {chartData.reduce((prev, cur) => prev + ((cur[key] as number) ?? 0), 0)} hours
  104. </p>
  105. </div>
  106. ))}
  107. </div>
  108. <div className="space-y-1">
  109. <p className="text-sm">Compute Hours usage per day</p>
  110. <p className="text-sm text-foreground-light">The data refreshes every hour.</p>
  111. </div>
  112. {chartData.length > 0 && notAllValuesZero ? (
  113. <UsageBarChart
  114. name={`Compute Hours usage`}
  115. unit={'hours'}
  116. attributes={attributes}
  117. data={chartData}
  118. yMin={24}
  119. />
  120. ) : (
  121. <Panel>
  122. <Panel.Content>
  123. <div className="flex flex-col items-center justify-center">
  124. <BarChart2 className="text-foreground-light mb-2" />
  125. <p className="text-sm">No data in period</p>
  126. <p className="text-sm text-foreground-light">May take up to one hour to show</p>
  127. </div>
  128. </Panel.Content>
  129. </Panel>
  130. )}
  131. </>
  132. )}
  133. </SectionContent>
  134. </div>
  135. )
  136. }
  137. export default Compute