PauseProjectButton.tsx124 lines · main
1import { PermissionAction } from '@supabase/shared-types/out/constants'
2import { CirclePause } from 'lucide-react'
3import { useRouter } from 'next/router'
4import { useState } from 'react'
5import { toast } from 'sonner'
6import {
7 AlertDialog,
8 AlertDialogAction,
9 AlertDialogCancel,
10 AlertDialogContent,
11 AlertDialogDescription,
12 AlertDialogFooter,
13 AlertDialogHeader,
14 AlertDialogTitle,
15} from 'ui'
16
17import { ButtonTooltip } from '@/components/ui/ButtonTooltip'
18import { useSetProjectStatus } from '@/data/projects/project-detail-query'
19import { useProjectPauseMutation } from '@/data/projects/project-pause-mutation'
20import { useCheckEntitlements } from '@/hooks/misc/useCheckEntitlements'
21import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions'
22import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization'
23import { useIsProjectActive, useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
24import { PROJECT_STATUS } from '@/lib/constants'
25
26const PauseProjectButton = () => {
27 const router = useRouter()
28 const { data: project } = useSelectedProjectQuery()
29 const { data: organization } = useSelectedOrganizationQuery()
30 const { setProjectStatus } = useSetProjectStatus()
31
32 const isProjectActive = useIsProjectActive()
33 const isProjectUnhealthy = project?.status === PROJECT_STATUS.ACTIVE_UNHEALTHY
34 const [isModalOpen, setIsModalOpen] = useState(false)
35
36 const projectRef = project?.ref ?? ''
37 const isPaused = project?.status === PROJECT_STATUS.INACTIVE
38 const { can: canPauseProject } = useAsyncCheckPermissions(
39 PermissionAction.INFRA_EXECUTE,
40 'queue_jobs.projects.pause'
41 )
42
43 const isFreePlan = organization?.plan.id === 'free'
44 const isBranch = Boolean(project?.parent_project_ref)
45 const { hasAccess: projectPausingAllowedInOrg } = useCheckEntitlements(
46 'project_pausing',
47 organization?.slug
48 )
49
50 const { mutate: pauseProject, isPending: isPausing } = useProjectPauseMutation({
51 onSuccess: (_, variables) => {
52 setProjectStatus({ ref: variables.ref, status: PROJECT_STATUS.PAUSING })
53 toast.success('Pausing project...')
54 router.push(`/project/${projectRef}`)
55 },
56 })
57
58 const requestPauseProject = () => {
59 if (!canPauseProject) {
60 return toast.error('You do not have the required permissions to pause this project')
61 }
62 pauseProject({ ref: projectRef })
63 }
64
65 const buttonDisabled =
66 isBranch ||
67 !projectPausingAllowedInOrg ||
68 project === undefined ||
69 isPaused ||
70 !canPauseProject ||
71 !isProjectActive
72
73 function getTooltipText() {
74 if (isPaused) return 'Your project is already paused'
75 if (!canPauseProject) return 'You need additional permissions to pause this project'
76 if (isProjectUnhealthy)
77 return 'Your project is unhealthy — restart it instead to restore normal operation'
78 if (!isProjectActive) return 'Unable to pause project as project is not active'
79 if (isBranch) return 'Branch projects cannot be paused'
80 if (!projectPausingAllowedInOrg && !isFreePlan)
81 return 'Projects on a paid plan will always be running'
82 return undefined
83 }
84
85 return (
86 <>
87 <ButtonTooltip
88 type="default"
89 icon={<CirclePause />}
90 onClick={() => setIsModalOpen(true)}
91 loading={isPausing}
92 disabled={buttonDisabled}
93 tooltip={{
94 content: {
95 side: 'bottom',
96 text: getTooltipText(),
97 },
98 }}
99 >
100 Pause project
101 </ButtonTooltip>
102
103 <AlertDialog open={isModalOpen} onOpenChange={setIsModalOpen}>
104 <AlertDialogContent>
105 <AlertDialogHeader>
106 <AlertDialogTitle>Pause project?</AlertDialogTitle>
107 <AlertDialogDescription>
108 This project will be unavailable while paused. Paused projects can be resumed for 90
109 days. After that, backups remain available to download.
110 </AlertDialogDescription>
111 </AlertDialogHeader>
112 <AlertDialogFooter>
113 <AlertDialogCancel disabled={isPausing}>Cancel</AlertDialogCancel>
114 <AlertDialogAction disabled={isPausing} onClick={requestPauseProject} variant="danger">
115 {isPausing ? 'Pausing project...' : 'Pause project'}
116 </AlertDialogAction>
117 </AlertDialogFooter>
118 </AlertDialogContent>
119 </AlertDialog>
120 </>
121 )
122}
123
124export default PauseProjectButton