PauseFailedState.tsx 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. import { PermissionAction, SupportCategories } from '@supabase/shared-types/out/constants'
  2. import { useParams } from 'common'
  3. import { Download, MoreVertical, Trash } from 'lucide-react'
  4. import { useState } from 'react'
  5. import {
  6. Button,
  7. CriticalIcon,
  8. Dialog,
  9. DialogContent,
  10. DialogHeader,
  11. DialogSection,
  12. DialogTitle,
  13. DropdownMenu,
  14. DropdownMenuContent,
  15. DropdownMenuTrigger,
  16. } from 'ui'
  17. import { DeleteProjectModal } from '@/components/interfaces/Settings/General/DeleteProjectPanel/DeleteProjectModal'
  18. import { SupportLink } from '@/components/interfaces/Support/SupportLink'
  19. import { LogicalBackupCliInstructions } from '@/components/layouts/ProjectLayout/LogicalBackupCliInstructions'
  20. import { ButtonTooltip } from '@/components/ui/ButtonTooltip'
  21. import { DropdownMenuItemTooltip } from '@/components/ui/DropdownMenuItemTooltip'
  22. import { InlineLink } from '@/components/ui/InlineLink'
  23. import { useBackupDownloadMutation } from '@/data/database/backup-download-mutation'
  24. import { useDownloadableBackupQuery } from '@/data/database/backup-query'
  25. import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions'
  26. import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
  27. export const PauseFailedState = () => {
  28. const { ref } = useParams()
  29. const { data: project } = useSelectedProjectQuery()
  30. const [visible, setVisible] = useState(false)
  31. const [showCliBackup, setShowCliBackup] = useState(false)
  32. const { can: canDeleteProject } = useAsyncCheckPermissions(PermissionAction.UPDATE, 'projects', {
  33. resource: { project_id: project?.id },
  34. })
  35. const { data, isPending: isLoadingBackups } = useDownloadableBackupQuery({ projectRef: ref })
  36. const backups = data?.backups ?? []
  37. const { mutate: downloadBackup, isPending: isDownloading } = useBackupDownloadMutation({
  38. onSuccess: (res) => {
  39. const { fileUrl } = res
  40. // Trigger browser download by create,trigger and remove tempLink
  41. const tempLink = document.createElement('a')
  42. tempLink.href = fileUrl
  43. document.body.appendChild(tempLink)
  44. tempLink.click()
  45. document.body.removeChild(tempLink)
  46. },
  47. })
  48. const onClickDownloadBackup = () => {
  49. if (!ref) return console.error('Project ref is required')
  50. if (backups.length === 0 || data?.status === 'physical-backups-enabled')
  51. return setShowCliBackup(true)
  52. downloadBackup({ ref, backup: backups[0] })
  53. }
  54. const downloadBackupTooltipText = isLoadingBackups
  55. ? undefined
  56. : data?.status === 'physical-backups-enabled'
  57. ? 'Project uses physical backups — click to see CLI backup instructions'
  58. : backups.length === 0
  59. ? 'No downloadable backup available — click to see CLI backup instructions'
  60. : undefined
  61. return (
  62. <>
  63. <div className="flex items-center justify-center h-full">
  64. <div className="bg-surface-100 border border-overlay rounded-md w-3/4 lg:w-1/2">
  65. <div className="space-y-6 pt-6">
  66. <div className="flex px-8 space-x-8">
  67. <div className="mt-1">
  68. <CriticalIcon className="w-5 h-5" />
  69. </div>
  70. <div className="space-y-1">
  71. <p>Something went wrong while pausing your project</p>
  72. <p className="text-sm text-foreground-light">
  73. Your project's data is intact, but your project is inaccessible due to the failure
  74. while pausing. Database backups for this project can still be accessed{' '}
  75. <InlineLink href={`/project/${ref}/database/backups/scheduled`}>here</InlineLink>.
  76. </p>
  77. <p className="text-sm text-foreground-light">
  78. Please contact support for assistance.
  79. </p>
  80. </div>
  81. </div>
  82. <div className="border-t border-overlay flex items-center justify-end gap-x-2 py-4 px-8">
  83. <Button asChild type="default">
  84. <SupportLink
  85. queryParams={{
  86. category: SupportCategories.DATABASE_UNRESPONSIVE,
  87. projectRef: project?.ref,
  88. subject: 'Pausing failed for project',
  89. }}
  90. >
  91. Contact support
  92. </SupportLink>
  93. </Button>
  94. <ButtonTooltip
  95. type="default"
  96. icon={<Download />}
  97. disabled={isLoadingBackups}
  98. loading={isDownloading || isLoadingBackups}
  99. tooltip={{
  100. content: {
  101. side: 'bottom',
  102. text: downloadBackupTooltipText,
  103. },
  104. }}
  105. onClick={onClickDownloadBackup}
  106. >
  107. Download backup
  108. </ButtonTooltip>
  109. <DropdownMenu>
  110. <DropdownMenuTrigger>
  111. <Button type="default" className="px-1.5" icon={<MoreVertical />} />
  112. </DropdownMenuTrigger>
  113. <DropdownMenuContent className="w-72" align="end">
  114. <DropdownMenuItemTooltip
  115. onClick={() => setVisible(true)}
  116. className="items-start gap-x-2"
  117. disabled={!canDeleteProject}
  118. tooltip={{
  119. content: {
  120. side: 'right',
  121. text: !canDeleteProject
  122. ? 'You need additional permissions to delete this project'
  123. : undefined,
  124. },
  125. }}
  126. >
  127. <div className="translate-y-0.5">
  128. <Trash size={14} />
  129. </div>
  130. <div className="">
  131. <p>Delete project</p>
  132. <p className="text-foreground-lighter">
  133. Project cannot be restored once it is deleted
  134. </p>
  135. </div>
  136. </DropdownMenuItemTooltip>
  137. </DropdownMenuContent>
  138. </DropdownMenu>
  139. </div>
  140. </div>
  141. </div>
  142. </div>
  143. <Dialog open={showCliBackup} onOpenChange={setShowCliBackup}>
  144. <DialogContent size="medium">
  145. <DialogHeader>
  146. <DialogTitle>Back up your database</DialogTitle>
  147. </DialogHeader>
  148. <DialogSection>
  149. <LogicalBackupCliInstructions showResetPassword={false} />
  150. </DialogSection>
  151. </DialogContent>
  152. </Dialog>
  153. <DeleteProjectModal visible={visible} onClose={() => setVisible(false)} />
  154. </>
  155. )
  156. }