Files
supabase/apps/studio/components/interfaces/Settings/General/DeleteProjectPanel/DeleteProjectButton.tsx
Jonathan Summers-Muir 9104cbc359 Chore/add first pattern docs (#21741)
* remove un-needed stuff

* Update preview.js

* Update preview.js

* upgrade sb

* Update preview.js

* chore: main init of the confirmTextModal

* update modals

* dialog positioned fixed

* fix backdrop

* chore: overlay now wrapping DialogContent !!

* more modal updates

* fix props

* update size

* padding fixed

* Update Dialog.stories.tsx

* add padding to more Dialog stuff

* update text

* start using subcomponents in storybook

* make thinner because it's too wiiiiide

* Tiny fix

---------

Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
2024-03-05 18:08:28 +08:00

200 lines
7.2 KiB
TypeScript

import * as Tooltip from '@radix-ui/react-tooltip'
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { useRouter } from 'next/router'
import { useEffect, useState } from 'react'
import { Button, Input } from 'ui'
import toast from 'react-hot-toast'
import TextConfirmModal from 'ui-patterns/Dialogs/TextConfirmModal'
import { CANCELLATION_REASONS } from 'components/interfaces/Billing/Billing.constants'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import { useSendDowngradeFeedbackMutation } from 'data/feedback/exit-survey-send'
import { useProjectDeleteMutation } from 'data/projects/project-delete-mutation'
import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-query'
import { useCheckPermissions, useSelectedOrganization } from 'hooks'
export interface DeleteProjectButtonProps {
type?: 'danger' | 'default'
}
const DeleteProjectButton = ({ type = 'danger' }: DeleteProjectButtonProps) => {
const router = useRouter()
const { project } = useProjectContext()
const organization = useSelectedOrganization()
const projectRef = project?.ref
const { data: subscription } = useOrgSubscriptionQuery({ orgSlug: organization?.slug })
const projectPlan = subscription?.plan?.id ?? 'free'
const isFree = projectPlan === 'free'
const [isOpen, setIsOpen] = useState(false)
const [message, setMessage] = useState<string>('')
const [selectedReasons, setSelectedReasons] = useState<string[]>([])
const { mutateAsync: deleteProject, isLoading: isDeleting } = useProjectDeleteMutation()
const { mutateAsync: sendExitSurvey, isLoading: isSending } = useSendDowngradeFeedbackMutation()
const isSubmitting = isDeleting || isSending
useEffect(() => {
if (isOpen) {
setSelectedReasons([])
setMessage('')
}
}, [isOpen])
const canDeleteProject = useCheckPermissions(PermissionAction.UPDATE, 'projects', {
resource: {
project_id: project?.id,
},
})
const toggle = () => {
if (isSubmitting) return
setIsOpen(!isOpen)
}
const onSelectCancellationReason = (reason: string) => {
const existingSelection = selectedReasons.find((x) => x === reason)
const updatedSelection =
existingSelection === undefined
? selectedReasons.concat([reason])
: selectedReasons.filter((x) => x !== reason)
setSelectedReasons(updatedSelection)
}
async function handleDeleteProject() {
if (project === undefined) return
if (!isFree && selectedReasons.length === 0) {
return toast.error('Please select at least one reason for deleting your project')
}
try {
await deleteProject({ projectRef: project.ref })
if (!isFree) {
await sendExitSurvey({
projectRef,
message,
reasons: selectedReasons.reduce((a, b) => `${a}- ${b}\n`, ''),
exitAction: 'delete',
})
}
toast.success(`Successfully deleted ${project.name}`)
router.push(`/projects`)
} finally {
}
}
return (
<>
<Tooltip.Root delayDuration={0}>
<Tooltip.Trigger asChild>
<Button onClick={toggle} type={type} disabled={!canDeleteProject}>
Delete project
</Button>
</Tooltip.Trigger>
{!canDeleteProject && (
<Tooltip.Portal>
<Tooltip.Content side="bottom">
<Tooltip.Arrow className="radix-tooltip-arrow" />
<div
className={[
'rounded bg-alternative py-1 px-2 leading-none shadow', // background
'border border-background', //border
].join(' ')}
>
<span className="text-xs text-foreground">
You need additional permissions to delete this project
</span>
</div>
</Tooltip.Content>
</Tooltip.Portal>
)}
</Tooltip.Root>
<TextConfirmModal
visible={isOpen}
loading={isSubmitting}
size={isFree ? 'small' : 'medium'}
title={`Confirm deletion of ${project?.name}`}
variant="destructive"
alert={{
title: isFree
? 'This action cannot be undone.'
: `This will permanently delete the ${project?.name}`,
description: !isFree ? `All project data will be lost, and cannot be undone` : '',
}}
text={
isFree
? `This will permanently delete the ${project?.name} project and all of its data.`
: undefined
}
confirmPlaceholder="Type the project name in here"
confirmString={project?.name || ''}
confirmLabel="I understand, delete this project"
onConfirm={handleDeleteProject}
onCancel={toggle}
>
{/*
[Joshen] This is basically ExitSurvey.tsx, ideally we have one shared component but the one
in ExitSurvey has a Form wrapped around it already. Will probably need some effort to refactor
but leaving that for the future.
*/}
{!isFree && (
<>
<div className="space-y-1">
<h4 className="text-base">We're sad that you're leaving.</h4>
<p className="text-sm text-foreground-light">
We always strive to improve Supabase as much as we can. Please let us know the
reasons you are deleting your project so that we can improve in the future.
</p>
</div>
<div className="space-y-4 pt-4">
<div className="flex flex-wrap gap-2" data-toggle="buttons">
{CANCELLATION_REASONS.map((option) => {
const active = selectedReasons.find((x) => x === option)
return (
<label
key={option}
className={[
'flex cursor-pointer items-center space-x-2 rounded-md py-1',
'pl-2 pr-3 text-center text-sm shadow-sm transition-all duration-100',
`${
active
? ` bg-foreground text-background opacity-100 hover:bg-opacity-75`
: ` bg-border-strong text-foreground opacity-25 hover:opacity-50`
}`,
].join(' ')}
>
<input
type="checkbox"
name="options"
value={option}
className="hidden"
onClick={(event: any) => onSelectCancellationReason(event.target.value)}
/>
<div>{option}</div>
</label>
)
})}
</div>
<div className="text-area-text-sm">
<Input.TextArea
name="message"
label="Anything else that we can improve on?"
rows={3}
value={message}
onChange={(event) => setMessage(event.target.value)}
/>
</div>
</div>
</>
)}
</TextConfirmModal>
</>
)
}
export default DeleteProjectButton