Files
supabase/apps/studio/components/layouts/ProjectLayout/PausedState/ProjectPausedState.tsx
Gildas Garcia 96d43099bb chore: refactor Button API so that it can be used a standard button (#46880)
## Problem

Our `<Button>` component breaks the default `button` contract by
redefining the `type` prop to set its variant (`primary`, `default`,
etc) instead of the button type (`submit`, `button`, etc).
This is confusing and forces to write more code when using it with
shadcn components that expect/inject the standard button props.

## Solution

- rename the `type` prop to `variant`
- rename the `htmlType` prop to `type`
- propagate the changes where necessary
- format code

## How to test

As this is just prop renaming, if it builds it's ok

---------

Co-authored-by: Ivan Vasilov <vasilov.ivan@gmail.com>
2026-06-16 23:59:58 +02:00

154 lines
6.1 KiB
TypeScript

import { useParams } from 'common'
import dayjs from 'dayjs'
import { PauseCircle } from 'lucide-react'
import Link from 'next/link'
import {
Button,
Card,
CardContent,
CardFooter,
cn,
Tooltip,
TooltipContent,
TooltipTrigger,
} from 'ui'
import { TimestampInfo } from 'ui-patterns'
import { GenericSkeletonLoader } from 'ui-patterns/ShimmeringLoader'
import { PauseDisabledState } from './PauseDisabledState'
import { ResumeProjectButton } from '@/components/interfaces/Project/ResumeProjectButton'
import { AlertError } from '@/components/ui/AlertError'
import { InlineLinkClassName } from '@/components/ui/InlineLink'
import { UpgradePlanButton } from '@/components/ui/UpgradePlanButton'
import { useProjectPauseStatusQuery } from '@/data/projects/project-pause-status-query'
import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization'
import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
import { usePHFlag } from '@/hooks/ui/useFlag'
import { PROJECT_STATUS } from '@/lib/constants'
export interface ProjectPausedStateProps {
product?: string
}
export const ProjectPausedState = ({ product }: ProjectPausedStateProps) => {
const { ref } = useParams()
const { data: project } = useSelectedProjectQuery()
const { data: selectedOrganization } = useSelectedOrganizationQuery()
const enableProBenefitWording = usePHFlag('proBenefitWording')
const {
data: pauseStatus,
error: pauseStatusError,
isError,
isSuccess: isPauseStatusSuccess,
isPending: isLoading,
} = useProjectPauseStatusQuery({ ref }, { enabled: project?.status === PROJECT_STATUS.INACTIVE })
const finalDaysRemainingBeforeRestoreDisabled =
pauseStatus?.remaining_days_till_restore_disabled ??
pauseStatus?.max_days_till_restore_disabled ??
0
const isFreePlan = selectedOrganization?.plan?.id === 'free'
const isRestoreDisabled = isPauseStatusSuccess && !pauseStatus.can_restore
return (
<Card className="w-full max-w-160 mx-auto">
<CardContent>
<PauseCircle size={48} strokeWidth={1} className="text-foreground-lighter shrink-0 mb-4" />
<div className="flex-1">
<div>
<h2 className="mb-4">The project "{project?.name}" is currently paused</h2>
<div className="text-foreground-light max-w-4xl">
{isLoading && <GenericSkeletonLoader className="mt-3" />}
{isPauseStatusSuccess && !isRestoreDisabled ? (
isFreePlan ? (
<>
<p className="text-sm">
All data, including backups and storage objects, remains safe. You can resume
this project from the dashboard within{' '}
<Tooltip>
<TooltipTrigger>
<span className={cn(InlineLinkClassName, 'text-foreground')}>
{finalDaysRemainingBeforeRestoreDisabled} day
{finalDaysRemainingBeforeRestoreDisabled > 1 ? 's' : ''}
</span>{' '}
</TooltipTrigger>
<TooltipContent side="bottom" className="w-80 text-center">
Free projects cannot be restored through the dashboard if they are paused
for more than {pauseStatus.max_days_till_restore_disabled} days
</TooltipContent>
</Tooltip>{' '}
(until{' '}
<TimestampInfo
displayAs="local"
utcTimestamp={dayjs()
.utc()
.add(pauseStatus.remaining_days_till_restore_disabled ?? 0, 'day')
.toISOString()}
className="text-sm text-foreground"
labelFormat="DD MMM YYYY"
/>
). After that, this project will not be resumable, but data will still be
available for download.
</p>
<p className="text-sm mt-4">
{enableProBenefitWording === 'variant-a'
? 'Upgrade to Pro to prevent pauses and unlock features like branching, compute upgrades, and daily backups.'
: 'To prevent future pauses, consider upgrading to Pro.'}
</p>
</>
) : (
<p className="text-sm">
Your project data is safe but inaccessible while paused. Once resumed, usage
will be billed by compute size and hours active.
</p>
)
) : !isLoading ? (
<p className="text-sm">
All of your project's data is still intact, but your project is inaccessible while
paused.{' '}
{product !== undefined ? (
<>
Resume this project to access the{' '}
<span className="text-brand">{product}</span> page.
</>
) : !isRestoreDisabled ? (
'Resume this project and get back to building!'
) : null}
</p>
) : null}
</div>
</div>
</div>
</CardContent>
{isError && (
<AlertError
className="rounded-none border-0"
error={pauseStatusError}
subject="Failed to retrieve pause status"
/>
)}
{isPauseStatusSuccess && !isRestoreDisabled && (
<CardFooter className="flex flex-wrap justify-end items-center gap-2">
<ResumeProjectButton size="tiny" variant="default" />
{isFreePlan ? (
<UpgradePlanButton source="projectPausedStateRestore" />
) : (
<Button asChild variant="default">
<Link href={`/project/${ref}/settings/general`}>View project settings</Link>
</Button>
)}
</CardFooter>
)}
{isPauseStatusSuccess && isRestoreDisabled && <PauseDisabledState />}
</Card>
)
}