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