Compute.tsx152 lines · main
| 1 | import { BarChart2 } from 'lucide-react' |
| 2 | import { useMemo } from 'react' |
| 3 | import { GenericSkeletonLoader } from 'ui-patterns/ShimmeringLoader' |
| 4 | |
| 5 | import { SectionContent } from './SectionContent' |
| 6 | import { Attribute, AttributeColor } from './Usage.constants' |
| 7 | import { dailyUsageToDataPoints } from './Usage.utils' |
| 8 | import UsageBarChart from './UsageBarChart' |
| 9 | import Panel from '@/components/ui/Panel' |
| 10 | import { DataPoint } from '@/data/analytics/constants' |
| 11 | import { |
| 12 | ComputeUsageMetric, |
| 13 | computeUsageMetricLabel, |
| 14 | type OrgDailyUsageResponse, |
| 15 | } from '@/data/analytics/org-daily-stats-query' |
| 16 | import { useIsFeatureEnabled } from '@/hooks/misc/useIsFeatureEnabled' |
| 17 | import { DOCS_URL } from '@/lib/constants' |
| 18 | |
| 19 | export interface ComputeProps { |
| 20 | orgDailyStats: OrgDailyUsageResponse | undefined |
| 21 | isLoadingOrgDailyStats: boolean |
| 22 | } |
| 23 | |
| 24 | const 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 | |
| 152 | export default Compute |