UnhealthyState.tsx 3.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. import { PermissionAction } from '@supabase/shared-types/out/constants'
  2. import { useParams } from 'common'
  3. import { AlertTriangle } from 'lucide-react'
  4. import { useRouter } from 'next/router'
  5. import { useState } from 'react'
  6. import { toast } from 'sonner'
  7. import { Button } from 'ui'
  8. import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
  9. import { useProjectDetailQuery, useSetProjectStatus } from '@/data/projects/project-detail-query'
  10. import { useProjectRestartMutation } from '@/data/projects/project-restart-mutation'
  11. import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions'
  12. import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
  13. import { PROJECT_STATUS } from '@/lib/constants'
  14. export const UnhealthyState = () => {
  15. const router = useRouter()
  16. const { ref } = useParams()
  17. const { data: project } = useSelectedProjectQuery()
  18. const { setProjectStatus } = useSetProjectStatus()
  19. const [showConfirm, setShowConfirm] = useState(false)
  20. const { can: canRestartProject } = useAsyncCheckPermissions(
  21. PermissionAction.INFRA_EXECUTE,
  22. 'reboot'
  23. )
  24. useProjectDetailQuery(
  25. { ref },
  26. {
  27. // Poll until the project recovers, which will dismiss this guard automatically
  28. refetchInterval: (query) => {
  29. const data = query.state.data
  30. return data?.status === PROJECT_STATUS.ACTIVE_UNHEALTHY ? 4000 : false
  31. },
  32. }
  33. )
  34. const { mutate: restartProject, isPending: isRestarting } = useProjectRestartMutation({
  35. onSuccess: () => {
  36. setProjectStatus({ ref: project?.ref ?? '', status: PROJECT_STATUS.RESTARTING })
  37. toast.success('Restarting project...')
  38. router.push(`/project/${ref}`)
  39. },
  40. onError: (error) => {
  41. toast.error(`Failed to restart project: ${error.message}`)
  42. },
  43. })
  44. return (
  45. <>
  46. <div className="flex items-center justify-center h-full">
  47. <div className="bg-surface-100 border border-overlay rounded-md w-3/4 lg:w-1/2">
  48. <div className="space-y-6 py-6">
  49. <div className="flex px-8 space-x-8">
  50. <div className="mt-1">
  51. <AlertTriangle className="text-warning" size={18} strokeWidth={1.5} />
  52. </div>
  53. <div className="flex flex-col gap-3">
  54. <div className="space-y-1">
  55. <p>Project {project?.name} is unhealthy</p>
  56. <p className="text-sm text-foreground-light">
  57. Your project is experiencing health issues and is not fully operational.
  58. Restarting the project will attempt to restore normal operation.
  59. </p>
  60. </div>
  61. <div>
  62. <Button
  63. type="default"
  64. size="tiny"
  65. disabled={!canRestartProject}
  66. loading={isRestarting}
  67. onClick={() => setShowConfirm(true)}
  68. >
  69. Restart project
  70. </Button>
  71. </div>
  72. </div>
  73. </div>
  74. </div>
  75. </div>
  76. </div>
  77. <ConfirmationModal
  78. visible={showConfirm}
  79. variant="destructive"
  80. title="Restart project"
  81. description="Are you sure you want to restart your project? There will be a few minutes of downtime."
  82. confirmLabel="Restart"
  83. confirmLabelLoading="Restarting"
  84. loading={isRestarting}
  85. onCancel={() => setShowConfirm(false)}
  86. onConfirm={() => restartProject({ ref: ref ?? '' })}
  87. />
  88. </>
  89. )
  90. }