PauseProjectButton.tsx 4.3 KB

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