Files
Gildas Garcia 243e079a2c chore: remove _Shadcn_ suffix from Command components (#46153)
## Problem

The `_Shadcn_` suffix isn't needed anymore on `Command` components

## Solution

- Remove the `_Shadcn_` suffix
- Simplify UI package exports
- Apply prettier

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Refactor**
* Simplified command component imports and exports across the UI library
by removing internal naming aliases and adopting direct component
references. Updated the public UI package barrel export to use wildcard
re-exports for cleaner API surface.

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/46153?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-05-20 15:45:32 +02:00

354 lines
13 KiB
TypeScript

import { PermissionAction } from '@supabase/shared-types/out/constants'
import { useParams } from 'common'
import dayjs from 'dayjs'
import { Check, ChevronDown } from 'lucide-react'
import Link from 'next/link'
import { useQueryState } from 'nuqs'
import { useMemo, useState } from 'react'
import { Button, cn, CommandGroup, CommandItem } from 'ui'
import { Admonition } from 'ui-patterns'
import { ShimmeringLoader } from 'ui-patterns/ShimmeringLoader'
import { Restriction } from '../BillingSettings/Restriction'
import ActiveCompute from './ActiveCompute'
import Activity from './Activity'
import Compute from './Compute'
import Egress from './Egress'
import OrgLogUsage from './OrgLogUsage'
import SizeAndCounts from './SizeAndCounts'
import { TotalUsage } from './TotalUsage'
import {
ScaffoldContainer,
ScaffoldHeader,
ScaffoldSection,
ScaffoldTitle,
} from '@/components/layouts/Scaffold'
import AlertError from '@/components/ui/AlertError'
import DateRangePicker from '@/components/ui/DateRangePicker'
import NoPermission from '@/components/ui/NoPermission'
import { OrganizationProjectSelector } from '@/components/ui/OrganizationProjectSelector'
import { useOrgDailyStatsQuery } from '@/data/analytics/org-daily-stats-query'
import { useProjectDetailQuery } from '@/data/projects/project-detail-query'
import { useOrgSubscriptionQuery } from '@/data/subscriptions/org-subscription-query'
import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions'
import { TIME_PERIODS_BILLING, TIME_PERIODS_REPORTS } from '@/lib/constants/metrics'
export const Usage = () => {
const { slug } = useParams()
const [dateRange, setDateRange] = useState<any>()
const [selectedProjectRef, setSelectedProjectRef] = useQueryState('projectRef')
const [openProjectSelector, setOpenProjectSelector] = useState(false)
const { can: canReadSubscriptions, isLoading: isLoadingPermissions } = useAsyncCheckPermissions(
PermissionAction.BILLING_READ,
'stripe.subscriptions'
)
const {
data: subscription,
error: subscriptionError,
isPending: isLoadingSubscription,
isError: isErrorSubscription,
isSuccess: isSuccessSubscription,
} = useOrgSubscriptionQuery({ orgSlug: slug })
const { data: selectedProject } = useProjectDetailQuery({
ref: selectedProjectRef ?? undefined,
})
const billingCycleStart = useMemo(() => {
return dayjs.unix(subscription?.current_period_start ?? 0).utc()
}, [subscription])
const billingCycleEnd = useMemo(() => {
return dayjs.unix(subscription?.current_period_end ?? 0).utc()
}, [subscription])
const currentBillingCycleSelected = useMemo(() => {
// Selected by default
if (!dateRange?.period_start || !dateRange?.period_end) return true
return (
dayjs(dateRange.period_start.date).isSame(billingCycleStart) &&
dayjs(dateRange.period_end.date).isSame(billingCycleEnd)
)
}, [dateRange, billingCycleStart, billingCycleEnd])
const startDate = useMemo(() => {
// If end date is in future, set end date to now
if (!dateRange?.period_start?.date) {
return undefined
} else {
// LF seems to have an issue with the milliseconds, causes infinite loading sometimes
return new Date(dateRange?.period_start?.date).toISOString().slice(0, -5) + 'Z'
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [dateRange, subscription])
const endDate = useMemo(() => {
// If end date is in future, set end date to end of current day
if (dateRange?.period_end?.date && dayjs(dateRange.period_end.date).isAfter(dayjs())) {
// LF seems to have an issue with the milliseconds, causes infinite loading sometimes
// In order to have full days from Prometheus metrics when using 1d interval,
// the time needs to be greater or equal than the time of the start date
return dayjs().endOf('day').toISOString().slice(0, -5) + 'Z'
} else if (dateRange?.period_end?.date) {
// LF seems to have an issue with the milliseconds, causes infinite loading sometimes
return new Date(dateRange.period_end.date).toISOString().slice(0, -5) + 'Z'
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [dateRange, subscription])
const {
data: orgDailyStats,
error: orgDailyStatsError,
isPending: isLoadingOrgDailyStats,
isError: isErrorOrgDailyStats,
} = useOrgDailyStatsQuery({
orgSlug: slug,
projectRef: selectedProjectRef ?? undefined,
startDate,
endDate,
})
return (
<>
<ScaffoldContainer>
<ScaffoldHeader className="pt-8">
<ScaffoldTitle>Usage</ScaffoldTitle>
</ScaffoldHeader>
</ScaffoldContainer>
<div className="sticky top-0 border-b bg-sidebar z-1">
<ScaffoldContainer>
<div className="py-4 flex items-center space-x-4">
{isLoadingSubscription || isLoadingPermissions ? (
<div className="flex lg:items-center items-start gap-3 flex-col lg:flex-row lg:justify-between w-full">
<div className="flex items-center gap-2">
<ShimmeringLoader className="w-48" />
<ShimmeringLoader className="w-48" />
</div>
<ShimmeringLoader className="w-[280px]" />
</div>
) : !canReadSubscriptions ? (
<NoPermission resourceText="view organization usage" />
) : null}
{isErrorSubscription && (
<AlertError
className="w-full"
subject="Failed to retrieve usage data"
error={subscriptionError}
/>
)}
{isSuccessSubscription && (
<div className="flex lg:items-center items-start gap-3 flex-col lg:flex-row lg:justify-between w-full">
<div className="flex items-center gap-2">
<DateRangePicker
onChange={setDateRange}
value={TIME_PERIODS_BILLING[0].key}
options={[...TIME_PERIODS_BILLING, ...TIME_PERIODS_REPORTS]}
loading={isLoadingSubscription}
currentBillingPeriodStart={subscription?.current_period_start}
currentBillingPeriodEnd={subscription?.current_period_end}
className="w-48!"
/>
<OrganizationProjectSelector
open={openProjectSelector}
setOpen={setOpenProjectSelector}
selectedRef={selectedProjectRef}
onSelect={(project) => {
setSelectedProjectRef(project.ref)
}}
renderTrigger={({ listboxId, open }) => {
return (
<Button
block
type="default"
role="combobox"
size="tiny"
aria-expanded={open}
aria-controls={listboxId}
className="justify-between w-[180px]"
iconRight={<ChevronDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />}
>
{!selectedProject ? 'All projects' : selectedProject?.name}
</Button>
)
}}
renderRow={(project) => {
const isSelected = selectedProjectRef === project.ref
return (
<div className="w-full flex items-center justify-between">
<span className={cn('truncate', isSelected ? 'max-w-60' : 'max-w-64')}>
{project.name}
</span>
{isSelected && <Check size={16} />}
</div>
)
}}
renderActions={() => (
<CommandGroup>
<CommandItem
className="cursor-pointer flex items-center justify-between w-full"
onSelect={() => {
setOpenProjectSelector(false)
setSelectedProjectRef(null)
}}
onClick={() => {
setOpenProjectSelector(false)
setSelectedProjectRef(null)
}}
>
All projects
{!selectedProjectRef && <Check size={16} />}
</CommandItem>
</CommandGroup>
)}
/>
</div>
<div className="flex items-center gap-2">
<p className={cn('text-sm transition', isLoadingSubscription && 'opacity-50')}>
Organization is on the{' '}
<span className="font-medium text-brand">{subscription.plan.name} Plan</span>
</p>
<span className="text-border-stronger">
<svg
viewBox="0 0 24 24"
width="16"
height="16"
stroke="currentColor"
strokeWidth="1"
strokeLinecap="round"
strokeLinejoin="round"
fill="none"
shapeRendering="geometricPrecision"
>
<path d="M16 3.549L7.12 20.600" />
</svg>
</span>
<p className="text-sm text-foreground-light">
{billingCycleStart.format('DD MMM YYYY')} -{' '}
{billingCycleEnd.format('DD MMM YYYY')}
</p>
</div>
</div>
)}
</div>
</ScaffoldContainer>
</div>
{isErrorOrgDailyStats && (
<ScaffoldContainer>
<ScaffoldSection isFullWidth className="pb-0">
<AlertError
error={orgDailyStatsError}
subject="Failed to retrieve usage statistics for organization"
/>
</ScaffoldSection>
</ScaffoldContainer>
)}
{selectedProject ? (
<ScaffoldContainer className="mt-5">
<Admonition
type="default"
title="Usage filtered by project"
description={
<div>
You are currently viewing usage for the{' '}
<span className="font-medium text-foreground">
{selectedProject?.name || selectedProjectRef}
</span>{' '}
project. Supabase uses{' '}
<Link
href="/docs/guides/platform/billing-on-supabase#organization-based-billing"
target="_blank"
>
organization-level billing
</Link>{' '}
and quotas. For billing purposes, we sum up usage from all your projects. To view
your usage quota, set the project filter above back to "All Projects".
</div>
}
/>
</ScaffoldContainer>
) : (
<ScaffoldContainer id="restriction" className="mt-5">
<Restriction />
</ScaffoldContainer>
)}
<TotalUsage
orgSlug={slug as string}
projectRef={selectedProjectRef}
subscription={subscription}
startDate={startDate}
endDate={endDate}
currentBillingCycleSelected={currentBillingCycleSelected}
/>
{subscription?.plan.id !== 'free' && (
<Compute orgDailyStats={orgDailyStats} isLoadingOrgDailyStats={isLoadingOrgDailyStats} />
)}
{subscription?.plan.id === 'platform' && (
<ActiveCompute
orgDailyStats={orgDailyStats}
isLoadingOrgDailyStats={isLoadingOrgDailyStats}
/>
)}
<Egress
orgSlug={slug as string}
projectRef={selectedProjectRef}
subscription={subscription}
currentBillingCycleSelected={currentBillingCycleSelected}
orgDailyStats={orgDailyStats}
isLoadingOrgDailyStats={isLoadingOrgDailyStats}
startDate={startDate}
endDate={endDate}
/>
<SizeAndCounts
orgSlug={slug as string}
projectRef={selectedProjectRef}
subscription={subscription}
currentBillingCycleSelected={currentBillingCycleSelected}
orgDailyStats={orgDailyStats}
isLoadingOrgDailyStats={isLoadingOrgDailyStats}
startDate={startDate}
endDate={endDate}
/>
<Activity
orgSlug={slug as string}
projectRef={selectedProjectRef}
subscription={subscription}
startDate={startDate}
endDate={endDate}
currentBillingCycleSelected={currentBillingCycleSelected}
orgDailyStats={orgDailyStats}
isLoadingOrgDailyStats={isLoadingOrgDailyStats}
/>
{subscription?.plan.id === 'platform' && (
<OrgLogUsage
orgSlug={slug as string}
projectRef={selectedProjectRef}
subscription={subscription}
startDate={startDate}
endDate={endDate}
currentBillingCycleSelected={currentBillingCycleSelected}
orgDailyStats={orgDailyStats}
isLoadingOrgDailyStats={isLoadingOrgDailyStats}
/>
)}
</>
)
}