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