mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 17:35:10 +03:00
Update delete-organization dialog to TextConfirmModal (#42670)
## What kind of change does this PR introduce? - ~Feat~ Chore - Resolves DEPR-33 ## What is the current behavior? The dialog used to delete an organization uses a ”type this org’s name in before you can press this button” interaction but: - A) Enables the button the whole time - B) We already have this componentized in TextConfirmModal ## What is the new behavior? - Refactored this dialog to use `TextConfirmModal` instead - Updated design docs to make this prop clearer | Before | After | | --- | --- | | <img width="1024" height="759" alt="Supabase" src="https://github.com/user-attachments/assets/85c965f6-29d4-4696-8c0c-a54ddcbe6167" /> | <img width="1024" height="759" alt="Supabase" src="https://github.com/user-attachments/assets/0a96ca65-15c3-4b1b-bb10-620526c059b8" /> | <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Documentation** * Updated Text Confirm Dialog documentation with clarifications: the confirm action remains disabled until input exactly matches the required string, and leading/trailing whitespace is trimmed from the input. * **Refactor** * Streamlined the organization deletion confirmation workflow for improved user experience. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
1 parent
b9a09d8612
commit
92bcdcd953
3 files changed
+26
-76
No files matched your search
@@ -53,7 +53,7 @@ export default function TextConfirmDialogDemo() {
|
||||
|
||||
## Props
|
||||
|
||||
- `confirmString`: The exact string the user must type to enable the confirm action
|
||||
- `confirmString`: The exact string the user must type to enable the confirm action (leading/trailing whitespace is trimmed)
|
||||
- `confirmPlaceholder`: Placeholder text shown in the confirmation input
|
||||
- `variant`: Visual intent of the dialog (`default`, `destructive`, or `warning`)
|
||||
- Other standard modal props inherited from the underlying [Dialog](../components/dialog) component
|
||||
|
||||
@@ -43,7 +43,7 @@ There are quite a few dialog components, each suited to a different task or cont
|
||||
|
||||
#### Text Confirm Dialog
|
||||
|
||||
[Text Confirm Dialog](../fragments/text-confirm-dialog) adds a deliberate speed bump for highly destructive actions by requiring the user to type an exact confirmation string before proceeding.
|
||||
[Text Confirm Dialog](../fragments/text-confirm-dialog) adds a deliberate speed bump for highly destructive actions by requiring the user to type an exact confirmation string before proceeding. The confirm action remains disabled until the input matches.
|
||||
|
||||
<ComponentPreview name="text-confirm-dialog-demo" />
|
||||
|
||||
|
||||
+24
-74
@@ -9,7 +9,7 @@ import { useOrganizationDeleteMutation } from 'data/organizations/organization-d
|
||||
import { useAsyncCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useLocalStorageQuery } from 'hooks/misc/useLocalStorage'
|
||||
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
|
||||
import { Button, Form, Input, Modal } from 'ui'
|
||||
import { TextConfirmModal } from 'components/ui/TextConfirmModalWrapper'
|
||||
|
||||
export const DeleteOrganizationButton = () => {
|
||||
const router = useRouter()
|
||||
@@ -17,7 +17,6 @@ export const DeleteOrganizationButton = () => {
|
||||
const { slug: orgSlug, name: orgName } = selectedOrganization ?? {}
|
||||
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
const [value, setValue] = useState('')
|
||||
|
||||
const [_, setLastVisitedOrganization] = useLocalStorageQuery(
|
||||
LOCAL_STORAGE_KEYS.LAST_VISITED_ORGANIZATION,
|
||||
@@ -37,23 +36,15 @@ export const DeleteOrganizationButton = () => {
|
||||
},
|
||||
})
|
||||
|
||||
const onValidate = (values: any) => {
|
||||
const errors: any = {}
|
||||
if (!values.orgName) {
|
||||
errors.orgName = 'Enter the name of the organization.'
|
||||
}
|
||||
if (values.orgName.trim() !== orgSlug?.trim()) {
|
||||
errors.orgName = 'Value entered does not match the value above.'
|
||||
}
|
||||
return errors
|
||||
}
|
||||
|
||||
const onConfirmDelete = async (values: any) => {
|
||||
const onConfirmDelete = () => {
|
||||
if (!canDeleteOrganization) {
|
||||
return toast.error('You do not have the required permissions to delete this organization')
|
||||
toast.error('You do not have permission to delete this organization')
|
||||
return
|
||||
}
|
||||
if (!orgSlug) {
|
||||
console.error('Org slug is required')
|
||||
return
|
||||
}
|
||||
if (!orgSlug) return console.error('Org slug is required')
|
||||
|
||||
deleteOrganization({ slug: orgSlug })
|
||||
}
|
||||
|
||||
@@ -62,7 +53,7 @@ export const DeleteOrganizationButton = () => {
|
||||
<div className="mt-2">
|
||||
<ButtonTooltip
|
||||
type="danger"
|
||||
disabled={!canDeleteOrganization}
|
||||
disabled={!canDeleteOrganization || !orgSlug}
|
||||
loading={!orgSlug}
|
||||
onClick={() => setIsOpen(true)}
|
||||
tooltip={{
|
||||
@@ -77,65 +68,24 @@ export const DeleteOrganizationButton = () => {
|
||||
Delete organization
|
||||
</ButtonTooltip>
|
||||
</div>
|
||||
<Modal
|
||||
hideFooter
|
||||
size="small"
|
||||
<TextConfirmModal
|
||||
visible={isOpen}
|
||||
size="small"
|
||||
variant="destructive"
|
||||
title="Delete organization"
|
||||
loading={isDeleting}
|
||||
confirmString={orgSlug ?? ''}
|
||||
confirmPlaceholder="Enter the string above"
|
||||
confirmLabel="I understand, delete this organization"
|
||||
onConfirm={onConfirmDelete}
|
||||
onCancel={() => setIsOpen(false)}
|
||||
header={
|
||||
<div className="flex items-baseline gap-2">
|
||||
<span>Delete organization</span>
|
||||
<span className="text-xs text-foreground-lighter">Are you sure?</span>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<Form
|
||||
validateOnBlur
|
||||
initialValues={{ orgName: '' }}
|
||||
onSubmit={onConfirmDelete}
|
||||
validate={onValidate}
|
||||
>
|
||||
{() => (
|
||||
<>
|
||||
<Modal.Content>
|
||||
<p className="text-sm text-foreground-lighter">
|
||||
This action <span className="text-foreground">cannot</span> be undone. This will
|
||||
permanently delete the <span className="text-foreground">{orgName}</span>{' '}
|
||||
organization and remove all of its projects.
|
||||
</p>
|
||||
</Modal.Content>
|
||||
<Modal.Separator />
|
||||
<Modal.Content>
|
||||
<Input
|
||||
id="orgName"
|
||||
label={
|
||||
<span>
|
||||
Please type <span className="font-bold">{orgSlug}</span> to confirm
|
||||
</span>
|
||||
}
|
||||
onChange={(e) => setValue(e.target.value)}
|
||||
value={value}
|
||||
placeholder="Enter the string above"
|
||||
className="w-full"
|
||||
/>
|
||||
</Modal.Content>
|
||||
<Modal.Separator />
|
||||
<Modal.Content>
|
||||
<Button
|
||||
block
|
||||
size="small"
|
||||
type="danger"
|
||||
htmlType="submit"
|
||||
loading={isDeleting}
|
||||
disabled={isDeleting}
|
||||
>
|
||||
I understand, delete this organization
|
||||
</Button>
|
||||
</Modal.Content>
|
||||
</>
|
||||
)}
|
||||
</Form>
|
||||
</Modal>
|
||||
<p className="text-sm text-foreground-lighter">
|
||||
This action <span className="text-foreground">cannot</span> be undone. This will
|
||||
permanently delete the <span className="text-foreground">{orgName}</span> organization and
|
||||
remove all of its projects.
|
||||
</p>
|
||||
</TextConfirmModal>
|
||||
</>
|
||||
)
|
||||
}
|
||||
Reference in new issue
Block a user