import { PermissionAction } from '@supabase/shared-types/out/constants' import { useQueryClient } from '@tanstack/react-query' import dayjs from 'dayjs' import { Download, PauseCircle } from 'lucide-react' import Link from 'next/link' import { useState } from 'react' import toast from 'react-hot-toast' import { useParams } from 'common' import AlertError from 'components/ui/AlertError' import { ButtonTooltip } from 'components/ui/ButtonTooltip' import { useBackupDownloadMutation } from 'data/database/backup-download-mutation' import { useFreeProjectLimitCheckQuery } from 'data/organizations/free-project-limit-check-query' import { useProjectPauseStatusQuery } from 'data/projects/project-pause-status-query' import { useProjectRestoreMutation } from 'data/projects/project-restore-mutation' import { setProjectStatus } from 'data/projects/projects-query' import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-query' import { useCheckPermissions } from 'hooks/misc/useCheckPermissions' import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization' import { useFlag } from 'hooks/ui/useFlag' import { PROJECT_STATUS } from 'lib/constants' import { AlertDescription_Shadcn_, AlertTitle_Shadcn_, Alert_Shadcn_, Button, Modal, TooltipContent_Shadcn_, TooltipTrigger_Shadcn_, Tooltip_Shadcn_, } from 'ui' import { GenericSkeletonLoader } from 'ui-patterns' import ConfirmModal from 'ui-patterns/Dialogs/ConfirmDialog' import { WarningIcon } from 'ui-patterns/Icons/StatusIcons' import { useProjectContext } from './ProjectContext' import { RestorePaidPlanProjectNotice } from './RestorePaidPlanProjectNotice' export interface ProjectPausedStateProps { product?: string } const ProjectPausedState = ({ product }: ProjectPausedStateProps) => { const { ref } = useParams() const queryClient = useQueryClient() const { project } = useProjectContext() const selectedOrganization = useSelectedOrganization() const enforceNinetyDayUnpauseExpiry = useFlag('enforceNinetyDayUnpauseExpiry') const orgSlug = selectedOrganization?.slug const { data: subscription } = useOrgSubscriptionQuery({ orgSlug }) const { data: pauseStatus, error: pauseStatusError, isError, isSuccess, isLoading, } = useProjectPauseStatusQuery( { ref }, { enabled: project?.status === PROJECT_STATUS.INACTIVE && enforceNinetyDayUnpauseExpiry, } ) const finalDaysRemainingBeforeRestoreDisabled = pauseStatus?.remaining_days_till_restore_disabled ?? pauseStatus?.max_days_till_restore_disabled ?? 0 const isFreePlan = subscription?.plan?.id === 'free' const isRestoreDisabled = enforceNinetyDayUnpauseExpiry && isSuccess && !pauseStatus.can_restore const latestBackup = pauseStatus?.latest_downloadable_backup_id const { data: membersExceededLimit } = useFreeProjectLimitCheckQuery( { slug: orgSlug }, { enabled: isFreePlan } ) const hasMembersExceedingFreeTierLimit = (membersExceededLimit || []).length > 0 const [showConfirmRestore, setShowConfirmRestore] = useState(false) const [showFreeProjectLimitWarning, setShowFreeProjectLimitWarning] = useState(false) const { mutate: restoreProject } = useProjectRestoreMutation({ onSuccess: (_, variables) => { setProjectStatus(queryClient, variables.ref, PROJECT_STATUS.RESTORING) toast.success('Restoring project') }, }) const { mutate: downloadBackup, isLoading: isDownloading } = useBackupDownloadMutation({ onSuccess: (res) => { const { fileUrl } = res // Trigger browser download by create,trigger and remove tempLink const tempLink = document.createElement('a') tempLink.href = fileUrl document.body.appendChild(tempLink) tempLink.click() document.body.removeChild(tempLink) }, }) const canResumeProject = useCheckPermissions( PermissionAction.INFRA_EXECUTE, 'queue_jobs.projects.initialize_or_resume' ) const onSelectRestore = () => { if (!canResumeProject) { toast.error('You do not have the required permissions to restore this project') } else if (hasMembersExceedingFreeTierLimit) setShowFreeProjectLimitWarning(true) else setShowConfirmRestore(true) } const onConfirmRestore = () => { if (!project) { return toast.error('Unable to restore: project is required') } restoreProject({ ref: project.ref }) } const onSelectDownloadBackup = () => { if (ref === undefined) return console.error('Project ref is required') if (!latestBackup) return toast.error('No backups available for download') downloadBackup({ ref, backup: { id: latestBackup, // [Joshen] Just FYI these params aren't required for the download backup request // API types need to be updated project_id: -1, inserted_at: '', isPhysicalBackup: false, status: {}, }, }) } return ( <>
The project "{project?.name ?? ''}" is currently paused.
All of your project's data is still intact, but your project is inaccessible while paused.{' '} {product !== undefined ? ( <> Restore this project to access the{' '} {product} page > ) : ( 'Restore this project and get back to building!' )}
To prevent future pauses, consider upgrading to Pro.
The following members have reached their maximum limits for the number of active free plan projects within organizations where they are an administrator or owner:
These members will need to either delete, pause, or upgrade one or more of these projects before you're able to unpause this project.