Chore/update confirmation modal (#22328)

* Update confirmation modal

* update all props

* Update ExtensionCard.tsx

* clen up
This commit is contained in:
Jonathan Summers-Muir
2024-04-02 13:39:27 +08:00
committed by GitHub
parent ab39435862
commit 645273cb45
43 changed files with 766 additions and 891 deletions

View File

@@ -190,34 +190,30 @@ const Pagination = ({ isLoading: isLoadingRows = false }: PaginationProps) => {
<ConfirmationModal
visible={isConfirmPreviousModalOpen}
header="Confirm moving to previous page"
buttonLabel="Confirm"
onSelectCancel={() => setIsConfirmPreviousModalOpen(false)}
onSelectConfirm={() => {
title="Confirm moving to previous page"
confirmLabel="Confirm"
onCancel={() => setIsConfirmPreviousModalOpen(false)}
onConfirm={() => {
onConfirmPreviousPage()
}}
>
<Modal.Content>
<p className="py-4 text-sm text-foreground-light">
The currently selected lines will be deselected, do you want to proceed?
</p>
</Modal.Content>
<p className="py-4 text-sm text-foreground-light">
The currently selected lines will be deselected, do you want to proceed?
</p>
</ConfirmationModal>
<ConfirmationModal
visible={isConfirmNextModalOpen}
header="Confirm moving to next page"
buttonLabel="Confirm"
onSelectCancel={() => setIsConfirmNextModalOpen(false)}
onSelectConfirm={() => {
title="Confirm moving to next page"
confirmLabel="Confirm"
onCancel={() => setIsConfirmNextModalOpen(false)}
onConfirm={() => {
onConfirmNextPage()
}}
>
<Modal.Content>
<p className="py-4 text-sm text-foreground-light">
The currently selected lines will be deselected, do you want to proceed?
</p>
</Modal.Content>
<p className="py-4 text-sm text-foreground-light">
The currently selected lines will be deselected, do you want to proceed?
</p>
</ConfirmationModal>
</>
)}

View File

@@ -77,20 +77,18 @@ const AccessTokenList = () => {
</div>
<ConfirmationModal
visible={isOpen}
danger={true}
header="Confirm to delete"
buttonLabel="Delete"
buttonLoadingLabel="Deleting"
onSelectCancel={() => setIsOpen(false)}
onSelectConfirm={() => {
variant={'destructive'}
title="Confirm to delete"
confirmLabel="Delete"
confirmLabelLoading="Deleting"
onCancel={() => setIsOpen(false)}
onConfirm={() => {
if (token) onDeleteToken(token.id)
}}
>
<Modal.Content>
<p className="py-4 text-sm text-foreground-light">
{`This action cannot be undone. Are you sure you want to delete "${token?.name}" token?`}
</p>
</Modal.Content>
<p className="py-4 text-sm text-foreground-light">
{`This action cannot be undone. Are you sure you want to delete "${token?.name}" token?`}
</p>
</ConfirmationModal>
</>
)

View File

@@ -64,29 +64,27 @@ const FirstStep = ({ visible, name, enroll, setName, isEnrolling, onClose }: Fir
<ConfirmationModal
size="medium"
visible={visible}
header="Add a new authenticator app as a factor"
buttonLabel="Generate QR"
buttonLoadingLabel="Generating QR"
buttonDisabled={name.length === 0}
title="Add a new authenticator app as a factor"
confirmLabel="Generate QR"
confirmLabelLoading="Generating QR"
disabled={name.length === 0}
loading={isEnrolling}
onSelectCancel={onClose}
onSelectConfirm={() => {
onCancel={onClose}
onConfirm={() => {
enroll({
factorType: 'totp',
friendlyName: name,
})
}}
>
<Modal.Content>
<div className="pt-6 pb-5">
<Input
label="Provide a name to identify this app"
descriptionText="A string will be randomly generated if a name is not provided"
value={name}
onChange={(e) => setName(e.target.value)}
/>
</div>
</Modal.Content>
<div className="pt-6 pb-5">
<Input
label="Provide a name to identify this app"
descriptionText="A string will be randomly generated if a name is not provided"
value={name}
onChange={(e) => setName(e.target.value)}
/>
</div>
</ConfirmationModal>
)
}
@@ -152,65 +150,63 @@ const SecondStep = ({
<ConfirmationModal
size="medium"
visible={visible}
header={`Verify new factor ${factorName}`}
buttonLabel="Confirm"
buttonLoadingLabel="Confirming"
title={`Verify new factor ${factorName}`}
confirmLabel="Confirm"
confirmLabelLoading="Confirming"
loading={isVerifying}
onSelectCancel={() => {
onCancel={() => {
// If a factor has been created (but not verified), unenroll it. This will be run as a
// side effect so that it's not confusing to the user why the modal stays open while
// unenrolling.
if (factor) unenroll({ factorId: factor.id })
}}
onSelectConfirm={() => factor && challengeAndVerify({ factorId: factor.id, code })}
onConfirm={() => factor && challengeAndVerify({ factorId: factor.id, code })}
>
<Modal.Content>
<>
<div className="py-4 pb-0 text-sm">
<span>
Use an authenticator app to scan the following QR code, and provide the code from the
app to complete the enrolment.
</span>
<>
<div className="py-4 pb-0 text-sm">
<span>
Use an authenticator app to scan the following QR code, and provide the code from the
app to complete the enrolment.
</span>
</div>
{isLoading && (
<div className="pb-4 px-4">
<GenericSkeletonLoader />
</div>
{isLoading && (
<div className="pb-4 px-4">
<GenericSkeletonLoader />
)}
{factor && (
<>
<div className="flex justify-center py-6">
<div className="h-48 w-48 bg-white rounded">
<img width={190} height={190} src={factor.totp.qr_code} alt={factor.totp.uri} />
</div>
</div>
)}
{factor && (
<>
<div className="flex justify-center py-6">
<div className="h-48 w-48 bg-white rounded">
<img width={190} height={190} src={factor.totp.qr_code} alt={factor.totp.uri} />
</div>
</div>
<div>
<InformationBox
title="Unable to scan?"
description={
<Input
copy
disabled
id="ref"
size="small"
label="You can also enter this secret key into your authenticator app"
value={factor.totp.secret}
/>
}
/>
</div>
<div className="pt-2 pb-4">
<Input
label="Authentication code"
value={code}
placeholder="XXXXXX"
onChange={(e) => setCode(e.target.value)}
/>
</div>
</>
)}
</>
</Modal.Content>
<div>
<InformationBox
title="Unable to scan?"
description={
<Input
copy
disabled
id="ref"
size="small"
label="You can also enter this secret key into your authenticator app"
value={factor.totp.secret}
/>
}
/>
</div>
<div className="pt-2 pb-4">
<Input
label="Authentication code"
value={code}
placeholder="XXXXXX"
onChange={(e) => setCode(e.target.value)}
/>
</div>
</>
)}
</>
</ConfirmationModal>
)
}

View File

@@ -34,45 +34,36 @@ const DeleteFactorModal = ({
<ConfirmationModal
size="medium"
visible={visible}
danger
header="Confirm to delete factor"
buttonLabel="Delete"
buttonLoadingLabel="Deleting"
variant={'destructive'}
title="Confirm to delete factor"
confirmLabel="Delete"
confirmLabelLoading="Deleting"
loading={isLoading}
onSelectCancel={onClose}
onSelectConfirm={() => factorId && unenroll({ factorId })}
onCancel={onClose}
onConfirm={() => factorId && unenroll({ factorId })}
alert={{
title: lastFactorToBeDeleted
? 'Multi-factor authentication will be disabled'
: 'This action cannot be undone',
description: lastFactorToBeDeleted
? 'There are no other factors that are set up once you delete this factor, as such your account will no longer be guarded by multi-factor authentication'
: 'You will no longer be able to use this authenticator app for multi-factor authentication when signing in to the dashboard',
}}
>
<Modal.Content className="py-6">
<Alert_Shadcn_ variant="warning">
<IconAlertTriangle strokeWidth={2} />
<AlertTitle_Shadcn_>
{lastFactorToBeDeleted
? 'Multi-factor authentication will be disabled'
: 'This action cannot be undone'}
</AlertTitle_Shadcn_>
<AlertDescription_Shadcn_>
{lastFactorToBeDeleted
? 'There are no other factors that are set up once you delete this factor, as such your account will no longer be guarded by multi-factor authentication'
: 'You will no longer be able to use this authenticator app for multi-factor authentication when signing in to the dashboard'}
</AlertDescription_Shadcn_>
</Alert_Shadcn_>
<div className="text-sm px-1 pt-4">
<p>Before deleting this factor, consider:</p>
<ul className="text-foreground-light py-1 list-disc mx-4 space-y-1">
{lastFactorToBeDeleted ? (
<>
<li>Adding another authenticator app as a factor prior to deleting</li>
<li>Ensure that your account does not need multi-factor authentication</li>
</>
) : (
<>
<li>Your backup authenticator app is still available to use</li>
<li>Adding another authenticator app thereafter as a backup</li>
</>
)}
</ul>
</div>
</Modal.Content>
<p className="text-sm">Before deleting this factor, consider:</p>
<ul className="text-sm text-foreground-light py-1 list-disc mx-4 space-y-1">
{lastFactorToBeDeleted ? (
<>
<li>Adding another authenticator app as a factor prior to deleting</li>
<li>Ensure that your account does not need multi-factor authentication</li>
</>
) : (
<>
<li>Your backup authenticator app is still available to use</li>
<li>Adding another authenticator app thereafter as a backup</li>
</>
)}
</ul>
</ConfirmationModal>
)
}

View File

@@ -843,22 +843,20 @@ export const AIPolicyEditorPanel = memo(function ({
<ConfirmationModal
visible={isClosingPolicyEditorPanel}
header="Discard changes"
buttonLabel="Discard"
onSelectCancel={() => {
title="Discard changes"
confirmLabel="Discard"
onCancel={() => {
setIsClosingPolicyEditorPanel(false)
}}
onSelectConfirm={() => {
onConfirm={() => {
onSelectCancel()
setIsClosingPolicyEditorPanel(false)
}}
>
<Modal.Content>
<p className="py-4 text-sm text-foreground-light">
Are you sure you want to close the editor? Any unsaved changes on your policy and
conversations with the Assistant will be lost.
</p>
</Modal.Content>
<p className="text-sm text-foreground-light">
Are you sure you want to close the editor? Any unsaved changes on your policy and
conversations with the Assistant will be lost.
</p>
</ConfirmationModal>
</>
)

View File

@@ -187,21 +187,19 @@ const PolicyEditorModal = ({
<div>
<ConfirmationModal
visible={isClosingPolicyEditorModal}
header="Discard changes"
buttonLabel="Discard"
onSelectCancel={() => setIsClosingPolicyEditorModal(false)}
onSelectConfirm={() => {
title="Discard changes"
confirmLabel="Discard"
onCancel={() => setIsClosingPolicyEditorModal(false)}
onConfirm={() => {
onSelectCancel()
setIsClosingPolicyEditorModal(false)
setIsDirty(false)
}}
>
<Modal.Content>
<p className="py-4 text-sm text-foreground-light">
There are unsaved changes. Are you sure you want to close the editor? Your changes
will be lost.
</p>
</Modal.Content>
<p className="text-sm text-foreground-light">
There are unsaved changes. Are you sure you want to close the editor? Your changes will
be lost.
</p>
</ConfirmationModal>
{view === POLICY_MODAL_VIEWS.SELECTION ? (
<PolicySelection

View File

@@ -224,35 +224,30 @@ const UserDropdown = ({
<ConfirmationModal
visible={isDeleteModalOpen}
header="Confirm to delete"
buttonLabel="Delete"
buttonLoadingLabel="Delete"
onSelectCancel={() => setIsDeleteModalOpen(false)}
onSelectConfirm={() => {
title="Confirm to delete"
confirmLabel="Delete"
onCancel={() => setIsDeleteModalOpen(false)}
onConfirm={() => {
handleDelete()
}}
>
<Modal.Content>
<p className="py-4 text-sm text-foreground-light">
This is permanent! Are you sure you want to delete user {user.email}?
</p>
</Modal.Content>
<p className="text-sm text-foreground-light">
This is permanent! Are you sure you want to delete user {user.email}?
</p>
</ConfirmationModal>
<ConfirmationModal
visible={isDeleteFactorsModalOpen}
header="Confirm to delete"
buttonLabel="Delete"
onSelectCancel={() => setIsDeleteFactorsModalOpen(false)}
onSelectConfirm={() => {
title="Confirm to delete"
confirmLabel="Delete"
onCancel={() => setIsDeleteFactorsModalOpen(false)}
onConfirm={() => {
handleDeleteFactors()
}}
>
<Modal.Content>
<p className="py-4 text-sm text-foreground-light">
This is permanent! Are you sure you want to delete the user's MFA factors?
</p>
</Modal.Content>
<p className="text-sm text-foreground-light">
This is permanent! Are you sure you want to delete the user's MFA factors?
</p>
</ConfirmationModal>
</>
)

View File

@@ -344,44 +344,28 @@ const BranchManagement = () => {
/>
<ConfirmationModal
danger
variant={'destructive'}
size="medium"
loading={isDisabling}
visible={showDisableBranching}
header="Confirm disable branching for project"
buttonLabel="Confirm disable branching"
buttonLoadingLabel="Disabling branching..."
onSelectConfirm={() => onConfirmDisableBranching()}
onSelectCancel={() => setShowDisableBranching(false)}
title="Confirm disable branching for project"
confirmLabel="Confirm disable branching"
confirmLabelLoading="Disabling branching..."
onConfirm={() => onConfirmDisableBranching()}
onCancel={() => setShowDisableBranching(false)}
alert={{
title: 'This action cannot be undone',
description:
'All database preview branches will be removed upon disabling branching. You may still re-enable branching again thereafter, but your existing preview branches will not be restored.',
}}
>
<Modal.Content>
<div className="py-6">
<Alert_Shadcn_ variant="warning">
<IconAlertTriangle strokeWidth={2} />
<AlertTitle_Shadcn_>This action cannot be undone</AlertTitle_Shadcn_>
<AlertDescription_Shadcn_>
All database preview branches will be removed upon disabling branching. You may
still re-enable branching again thereafter, but your existing preview branches will
not be restored.
</AlertDescription_Shadcn_>
</Alert_Shadcn_>
<ul className="mt-4 space-y-5">
<li className="flex gap-3">
<div>
<strong className="text-sm">Before you disable branching, consider:</strong>
<ul className="space-y-2 mt-2 text-sm text-foreground-light">
<li className="list-disc ml-6">
Your project no longer requires database previews.
</li>
<li className="list-disc ml-6">
None of your database previews are currently being used in any app.
</li>
</ul>
</div>
</li>
</ul>
</div>
</Modal.Content>
<p className="text-sm">Before you disable branching, consider:</p>
<ul className="space-y-2 mt-2 text-sm text-foreground-light">
<li className="list-disc ml-6">Your project no longer requires database previews.</li>
<li className="list-disc ml-6">
None of your database previews are currently being used in any app.
</li>
</ul>
</ConfirmationModal>
<CreateBranchModal visible={showCreateBranch} onClose={() => setShowCreateBranch(false)} />

View File

@@ -244,22 +244,19 @@ export const BranchRow = ({
</DropdownMenu>
<ConfirmationModal
danger
variant={'destructive'}
visible={showConfirmResetModal}
buttonLabel="Reset branch"
header="Confirm branch reset"
confirmLabel="Reset branch"
title="Confirm branch reset"
loading={isResetting}
onSelectCancel={() => {
onCancel={() => {
setShowConfirmResetModal(false)
}}
onSelectConfirm={onConfirmReset}
onConfirm={onConfirmReset}
>
<Modal.Content>
<p className="py-4 text-sm text-foreground-light">
Are you sure you want to reset the "{branch.name}" branch? All data will be
deleted.
</p>
</Modal.Content>
<p className="text-sm text-foreground-light">
Are you sure you want to reset the "{branch.name}" branch? All data will be deleted.
</p>
</ConfirmationModal>
</div>
)}

View File

@@ -107,26 +107,22 @@ const BackupsList = () => {
</div>
<ConfirmationModal
size="medium"
buttonLabel="Confirm restore"
buttonLoadingLabel="Restoring"
confirmLabel="Confirm restore"
confirmLabelLoading="Restoring"
visible={selectedBackup !== undefined}
header="Confirm to restore from backup"
title="Confirm to restore from backup"
loading={isRestoring || isSuccessBackup}
onSelectCancel={() => setSelectedBackup(undefined)}
onSelectConfirm={() => {
onCancel={() => setSelectedBackup(undefined)}
onConfirm={() => {
if (selectedBackup === undefined) return console.error('Backup required')
restoreFromBackup({ ref: projectRef, backup: selectedBackup })
}}
>
<Modal.Content>
<div className="pt-6 pb-5">
<p>
Are you sure you want to restore from
{dayjs(selectedBackup?.inserted_at).format('DD MMM YYYY')}? This will destroy any new
data written since this backup was made.
</p>
</div>
</Modal.Content>
<p>
Are you sure you want to restore from
{dayjs(selectedBackup?.inserted_at).format('DD MMM YYYY')}? This will destroy any new data
written since this backup was made.
</p>
</ConfirmationModal>
</>
)

View File

@@ -46,40 +46,32 @@ const DeleteEnumeratedTypeModal = ({
return (
<ConfirmationModal
danger
variant={'destructive'}
size="medium"
loading={isDeleting}
visible={visible}
header={
title={
<>
Confirm to delete enumerated type{' '}
<code className="text-sm">{selectedEnumeratedType?.name}</code>
</>
}
buttonLabel="Confirm delete"
buttonLoadingLabel="Deleting..."
onSelectConfirm={() => onConfirmDeleteType()}
onSelectCancel={onClose}
confirmLabel="Confirm delete"
confirmLabelLoading="Deleting..."
onCancel={onClose}
onConfirm={() => onConfirmDeleteType()}
alert={{
title: 'This action cannot be undone',
description:
'You will need to re-create the enumerated type if you want to revert the deletion.',
}}
>
<Modal.Content>
<div className="py-6">
<Alert_Shadcn_ variant="warning">
<IconAlertTriangle strokeWidth={2} />
<AlertTitle_Shadcn_>This action cannot be undone</AlertTitle_Shadcn_>
<AlertDescription_Shadcn_>
You will need to re-create the enumerated type if you want to revert the deletion.
</AlertDescription_Shadcn_>
</Alert_Shadcn_>
<div className="mt-4">
<strong className="text-sm">Before deleting this enumerated type, consider:</strong>
<ul className="space-y-2 mt-2 text-sm text-foreground-light">
<li className="list-disc ml-6">
This enumerated type is no longer in use in any tables or functions
</li>
</ul>
</div>
</div>
</Modal.Content>
<p className="text-sm">Before deleting this enumerated type, consider:</p>
<ul className="space-y-2 mt-2 text-sm text-foreground-light">
<li className="list-disc ml-6">
This enumerated type is no longer in use in any tables or functions
</li>
</ul>
</ConfirmationModal>
)
}

View File

@@ -139,17 +139,15 @@ const ExtensionCard = ({ extension }: ExtensionCardProps) => {
/>
<ConfirmationModal
visible={isDisableModalOpen}
header="Confirm to disable extension"
buttonLabel="Disable"
buttonLoadingLabel="Disabling"
onSelectCancel={() => setIsDisableModalOpen(false)}
onSelectConfirm={() => onConfirmDisable()}
title="Confirm to disable extension"
confirmLabel="Disable"
confirmLabelLoading="Disabling"
onCancel={() => setIsDisableModalOpen(false)}
onConfirm={() => onConfirmDisable()}
>
<Modal.Content>
<p className="py-4 text-sm text-foreground-light">
Are you sure you want to turn OFF the "{extension.name}" extension?
</p>
</Modal.Content>
<p className="text-sm text-foreground-light">
Are you sure you want to turn OFF the "{extension.name}" extension?
</p>
</ConfirmationModal>
</>
)

View File

@@ -405,20 +405,18 @@ const CreateFunction = ({ func, visible, setVisible }: CreateFunctionProps) => {
) : null}
<ConfirmationModal
visible={isClosingPanel}
header="Discard changes"
buttonLabel="Discard"
onSelectCancel={() => setIsClosingPanel(false)}
onSelectConfirm={() => {
title="Discard changes"
confirmLabel="Discard"
onCancel={() => setIsClosingPanel(false)}
onConfirm={() => {
setIsClosingPanel(false)
setVisible(!visible)
}}
>
<Modal.Content>
<p className="py-4 text-sm text-foreground-light">
There are unsaved changes. Are you sure you want to close the panel? Your changes will
be lost.
</p>
</Modal.Content>
<p className="text-sm text-foreground-light">
There are unsaved changes. Are you sure you want to close the panel? Your changes will
be lost.
</p>
</ConfirmationModal>
</SheetContent>
</Sheet>

View File

@@ -310,21 +310,19 @@ const EditHookPanel = ({ visible, selectedHook, onClose }: EditHookPanelProps) =
</SidePanel>
<ConfirmationModal
visible={isClosingPanel}
header="Discard changes"
buttonLabel="Discard"
onSelectCancel={() => setIsClosingPanel(false)}
onSelectConfirm={() => {
title="Discard changes"
confirmLabel="Discard"
onCancel={() => setIsClosingPanel(false)}
onConfirm={() => {
setIsClosingPanel(false)
setIsEdited(false)
onClose()
}}
>
<Modal.Content>
<p className="py-4 text-sm text-foreground-light">
There are unsaved changes. Are you sure you want to close the panel? Your changes will
be lost.
</p>
</Modal.Content>
<p className="text-sm text-foreground-light">
There are unsaved changes. Are you sure you want to close the panel? Your changes will be
lost.
</p>
</ConfirmationModal>
</>
)

View File

@@ -278,48 +278,41 @@ const Indexes = () => {
<CreateIndexSidePanel visible={showCreateIndex} onClose={() => setShowCreateIndex(false)} />
<ConfirmationModal
danger
variant="warning"
size="medium"
loading={isExecuting}
visible={selectedIndexToDelete !== undefined}
header={
title={
<>
Confirm to delete index <code className="text-sm">{selectedIndexToDelete?.name}</code>
</>
}
buttonLabel="Confirm delete"
buttonLoadingLabel="Deleting..."
onSelectConfirm={() =>
confirmLabel="Confirm delete"
confirmLabelLoading="Deleting..."
onConfirm={() =>
selectedIndexToDelete !== undefined ? onConfirmDeleteIndex(selectedIndexToDelete) : {}
}
onSelectCancel={() => setSelectedIndexToDelete(undefined)}
onCancel={() => setSelectedIndexToDelete(undefined)}
alert={{
title: 'This action cannot be undone',
description:
'Deleting an index that is still in use will cause queries to slow down, and in some cases causing significant performance issues.',
}}
>
<Modal.Content>
<div className="py-6">
<Alert_Shadcn_ variant="warning">
<IconAlertTriangle strokeWidth={2} />
<AlertTitle_Shadcn_>This action cannot be undone</AlertTitle_Shadcn_>
<AlertDescription_Shadcn_>
Deleting an index that is still in use will cause queries to slow down, and in some
cases causing significant performance issues.
</AlertDescription_Shadcn_>
</Alert_Shadcn_>
<ul className="mt-4 space-y-5">
<li className="flex gap-3">
<div>
<strong className="text-sm">Before deleting this index, consider:</strong>
<ul className="space-y-2 mt-2 text-sm text-foreground-light">
<li className="list-disc ml-6">This index is no longer in use</li>
<li className="list-disc ml-6">
The table which the index is on is not currently in use, as dropping an index
requires a short exclusive access lock on the table.
</li>
</ul>
</div>
</li>
</ul>
</div>
</Modal.Content>
<ul className="mt-4 space-y-5">
<li className="flex gap-3">
<div>
<strong className="text-sm">Before deleting this index, consider:</strong>
<ul className="space-y-2 mt-2 text-sm text-foreground-light">
<li className="list-disc ml-6">This index is no longer in use</li>
<li className="list-disc ml-6">
The table which the index is on is not currently in use, as dropping an index
requires a short exclusive access lock on the table.
</li>
</ul>
</div>
</li>
</ul>
</ConfirmationModal>
</>
)

View File

@@ -173,21 +173,19 @@ const PublicationsList = ({ onSelectPublication = noop }: PublicationsListProps)
<ConfirmationModal
visible={toggleListenEventValue !== null}
header={`Confirm to toggle sending ${toggleListenEventValue?.event.event.toLowerCase()} events`}
buttonLabel="Confirm"
buttonLoadingLabel="Updating"
onSelectCancel={() => setToggleListenEventValue(null)}
onSelectConfirm={() => {
title={`Confirm to toggle sending ${toggleListenEventValue?.event.event.toLowerCase()} events`}
confirmLabel="Confirm"
confirmLabelLoading="Updating"
onCancel={() => setToggleListenEventValue(null)}
onConfirm={() => {
toggleListenEvent()
}}
>
<Modal.Content>
<p className="py-4 text-sm text-foreground-light">
Are you sure you want to {toggleListenEventValue?.currentStatus ? 'stop' : 'start'}{' '}
sending {toggleListenEventValue?.event.event.toLowerCase()} events for{' '}
{toggleListenEventValue?.publication.name}?
</p>
</Modal.Content>
<p className="text-sm text-foreground-light">
Are you sure you want to {toggleListenEventValue?.currentStatus ? 'stop' : 'start'}{' '}
sending {toggleListenEventValue?.event.event.toLowerCase()} events for{' '}
{toggleListenEventValue?.publication.name}?
</p>
</ConfirmationModal>
</>
)

View File

@@ -383,20 +383,18 @@ const CreateTrigger = ({ trigger, visible, setVisible }: CreateTriggerProps) =>
</CreateTriggerContext.Provider>
<ConfirmationModal
visible={isClosingPanel}
header="Discard changes"
buttonLabel="Discard"
onSelectCancel={() => setIsClosingPanel(false)}
onSelectConfirm={() => {
title="Discard changes"
confirmLabel="Discard"
onCancel={() => setIsClosingPanel(false)}
onConfirm={() => {
setIsClosingPanel(false)
setVisible(!visible)
}}
>
<Modal.Content>
<p className="py-4 text-sm text-foreground-light">
There are unsaved changes. Are you sure you want to close the panel? Your changes
will be lost.
</p>
</Modal.Content>
<p className="text-sm text-foreground-light">
There are unsaved changes. Are you sure you want to close the panel? Your changes
will be lost.
</p>
</ConfirmationModal>
</div>
) : (

View File

@@ -121,22 +121,20 @@ const EdgeFunctionSecrets = () => {
<ConfirmationModal
loading={isDeleting}
visible={selectedSecret !== undefined}
buttonLabel="Delete secret"
buttonLoadingLabel="Deleting secret"
header={`Confirm to delete secret "${selectedSecret?.name}"`}
onSelectCancel={() => setSelectedSecret(undefined)}
onSelectConfirm={() => {
confirmLabel="Delete secret"
confirmLabelLoading="Deleting secret"
title={`Confirm to delete secret "${selectedSecret?.name}"`}
onCancel={() => setSelectedSecret(undefined)}
onConfirm={() => {
if (selectedSecret !== undefined) {
deleteSecret({ projectRef, secrets: [selectedSecret.name] })
}
}}
>
<Modal.Content className="py-4">
<p className="text-sm">
Before removing this secret, do ensure that none of your edge functions are currently
actively using this secret. This action cannot be undone.
</p>
</Modal.Content>
<p className="text-sm">
Before removing this secret, do ensure that none of your edge functions are currently
actively using this secret. This action cannot be undone.
</p>
</ConfirmationModal>
</>
)

View File

@@ -137,29 +137,26 @@ const IntegrationConnectionItem = forwardRef<HTMLLIElement, IntegrationConnectio
/>
<ConfirmationModal
danger
variant="destructive"
size={type === 'GitHub' && isBranchingEnabled ? 'medium' : 'small'}
visible={isOpen}
header={`Confirm to delete ${type} connection`}
buttonLabel="Delete connection"
onSelectCancel={onCancel}
onSelectConfirm={onConfirm}
title={`Confirm to delete ${type} connection`}
confirmLabel="Delete connection"
onCancel={onCancel}
onConfirm={onConfirm}
alert={
type === 'GitHub' && isBranchingEnabled
? {
title: 'Branching will be disabled for this project',
description: ` Deleting this GitHub connection will remove all preview branches on this project,
and also disable branching for ${project.name}`,
}
: {}
}
>
<Modal.Content className="py-4 flex flex-col gap-y-4">
{type === 'GitHub' && isBranchingEnabled && (
<Alert_Shadcn_ variant="warning">
<WarningIcon />
<AlertTitle_Shadcn_>Branching will be disabled for this project</AlertTitle_Shadcn_>
<AlertDescription_Shadcn_>
Deleting this GitHub connection will remove all preview branches on this project,
and also disable branching for {project.name}
</AlertDescription_Shadcn_>
</Alert_Shadcn_>
)}
<p className="text-sm text-foreground-light">
This action cannot be undone. Are you sure you want to delete this {type} connection?
</p>
</Modal.Content>
<p className="text-sm text-foreground-light">
This action cannot be undone. Are you sure you want to delete this {type} connection?
</p>
</ConfirmationModal>
</>
)

View File

@@ -73,53 +73,46 @@ const SOC2 = () => {
)}
<ConfirmationModal
visible={isOpen}
header="Non-Disclosure Agreement to access Supabase's SOC2 Report"
buttonLabel="I agree"
buttonLoadingLabel="Downloading"
onSelectCancel={() => setIsOpen(false)}
size="large"
onSelectConfirm={() => {
title="Non-Disclosure Agreement to access Supabase's SOC2 Report"
confirmLabel="I agree"
confirmLabelLoading="Downloading"
onCancel={() => setIsOpen(false)}
onConfirm={() => {
if (slug) fetchSOC2(slug)
}}
>
<Modal.Content>
<div className="py-4 text-sm text-foreground-light pl-30">
<ol className="list-decimal list-inside">
<li>The information that you are about to access is confidential.</li>
<ol className="list-decimal list-inside text-sm text-foreground-light pl-30">
<li>The information that you are about to access is confidential.</li>
<li>
Your access to our SOC 2 materials is governed by confidentiality obligations
contained in the agreement between Supabase, Inc ("Supabase", "we", "our" or "us") and
the Supabase customer that has authorized you to access our platform to obtain this
information (our "Customer").
</li>
<li>
You must ensure that you treat the information in our SOC 2 materials in accordance
with those confidentiality obligations, as communicated to you by the Customer.
</li>
<li>
By clicking "I agree" below or otherwise accessing our SOC 2 materials, you:
<ol className="list-[lower-roman] list-inside pl-4">
<li>acknowledge that you have read and understood this Confidentiality Notice;</li>
<li>
Your access to our SOC 2 materials is governed by confidentiality obligations
contained in the agreement between Supabase, Inc ("Supabase", "we", "our" or "us")
and the Supabase customer that has authorized you to access our platform to obtain
this information (our "Customer").
</li>
<li>
You must ensure that you treat the information in our SOC 2 materials in
accordance with those confidentiality obligations, as communicated to you by the
Customer.
</li>
<li>
By clicking "I agree" below or otherwise accessing our SOC 2 materials, you:
<ol className="list-[lower-roman] list-inside pl-4">
<li>
acknowledge that you have read and understood this Confidentiality Notice;
</li>
<li>
confirm that you have been authorized by the Customer to access this
information, and your use of our SOC 2 materials is subject to the
confidentiality obligations owed by the Customer to us.
</li>
</ol>
</li>
<li>
This Confidentiality Notice does not substitute or supersede any agreement between
us and the Customer, or any internal rules or policies that the Customer requires
you to comply with in your access to and use of confidential information. However,
your failure to comply with this Confidentiality Notice may be used to determine
whether the Customer has complied with its confidentiality obligations to us.
confirm that you have been authorized by the Customer to access this information,
and your use of our SOC 2 materials is subject to the confidentiality obligations
owed by the Customer to us.
</li>
</ol>
</div>
</Modal.Content>
</li>
<li>
This Confidentiality Notice does not substitute or supersede any agreement between us
and the Customer, or any internal rules or policies that the Customer requires you to
comply with in your access to and use of confidential information. However, your
failure to comply with this Confidentiality Notice may be used to determine whether
the Customer has complied with its confidentiality obligations to us.
</li>
</ol>
</ConfirmationModal>
</ScaffoldSectionContent>
</ScaffoldSection>

View File

@@ -190,18 +190,16 @@ const MemberActions = ({ member, roles }: MemberActionsProps) => {
<ConfirmationModal
visible={isDeleteModalOpen}
header="Confirm to remove"
buttonLabel="Remove"
onSelectCancel={() => setIsDeleteModalOpen(false)}
onSelectConfirm={() => {
title="Confirm to remove"
confirmLabel="Remove"
onCancel={() => setIsDeleteModalOpen(false)}
onConfirm={() => {
handleMemberDelete()
}}
>
<Modal.Content>
<p className="py-4 text-sm text-foreground-light">
This is permanent! Are you sure you want to remove {member.primary_email}
</p>
</Modal.Content>
<p className="text-sm text-foreground-light">
This is permanent! Are you sure you want to remove {member.primary_email}
</p>
</ConfirmationModal>
</>
)

View File

@@ -143,18 +143,16 @@ const TeamSettings = () => {
<ConfirmationModal
visible={isLeaveTeamModalOpen}
header="Are you sure?"
buttonLabel="Leave"
onSelectCancel={() => setIsLeaveTeamModalOpen(false)}
onSelectConfirm={() => {
title="Are you sure?"
confirmLabel="Leave"
onCancel={() => setIsLeaveTeamModalOpen(false)}
onConfirm={() => {
leaveTeam()
}}
>
<Modal.Content>
<p className="py-4 text-sm text-foreground-light">
Are you sure you want to leave this organization? This is permanent.
</p>
</Modal.Content>
<p className="text-sm text-foreground-light">
Are you sure you want to leave this organization? This is permanent.
</p>
</ConfirmationModal>
</>
)

View File

@@ -1,31 +0,0 @@
import { Modal } from 'ui'
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
interface ApplyConfigModalProps {
visible: boolean
onSelectCancel: () => void
onSelectConfirm: () => void
}
export const ApplyConfigModal = (props: ApplyConfigModalProps) => {
return (
<ConfirmationModal
header="Previously found messages will be lost"
danger
buttonLabel="Confirm"
size="small"
{...props}
>
<Modal.Content>
<div className="py-4">
<p className="text-sm text-foreground"></p>
<p className="text-sm text-foreground-light">
The realtime inspector will clear currently collected messages and start listening for
new messages matching the updated filters.
</p>
</div>
</Modal.Content>
</ConfirmationModal>
)
}

View File

@@ -18,10 +18,10 @@ import {
} from 'ui'
import Telemetry from 'lib/telemetry'
import { ApplyConfigModal } from '../ApplyConfigModal'
import { RealtimeConfig } from '../useRealtimeMessages'
import { FilterSchema } from './FilterSchema'
import { FilterTable } from './FilterTable'
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
interface RealtimeFilterPopoverProps {
config: RealtimeConfig
@@ -194,10 +194,14 @@ export const RealtimeFilterPopover = ({ config, onChangeConfig }: RealtimeFilter
</div>
</PopoverContent_Shadcn_>
</Popover_Shadcn_>
<ApplyConfigModal
<ConfirmationModal
title="Previously found messages will be lost"
variant="destructive"
confirmLabel="Confirm"
size="small"
visible={applyConfigOpen}
onSelectCancel={() => setApplyConfigOpen(false)}
onSelectConfirm={() => {
onCancel={() => setApplyConfigOpen(false)}
onConfirm={() => {
Telemetry.sendEvent(
{
category: 'realtime_inspector',
@@ -211,7 +215,12 @@ export const RealtimeFilterPopover = ({ config, onChangeConfig }: RealtimeFilter
setApplyConfigOpen(false)
setOpen(false)
}}
/>
>
<p className="text-sm text-foreground-light">
The realtime inspector will clear currently collected messages and start listening for new
messages matching the updated filters.
</p>
</ConfirmationModal>
</>
)
}

View File

@@ -213,32 +213,31 @@ const JWTSettings = () => {
</Panel>
<ConfirmationModal
danger
variant={'destructive'}
size="medium"
visible={isRegeneratingKey}
header="Confirm to generate a new JWT secret"
buttonLabel="Generate new secret"
buttonLoadingLabel="Generating"
onSelectCancel={() => setIsGeneratingKey(false)}
onSelectConfirm={() => handleJwtSecretUpdate('ROLL', setIsGeneratingKey)}
>
<Modal.Content className="py-4 space-y-4">
<Alert_Shadcn_ variant="warning">
<IconAlertTriangle />
<AlertTitle_Shadcn_>This will invalidate all existing API keys</AlertTitle_Shadcn_>
<AlertDescription_Shadcn_>
title="Confirm to generate a new JWT secret"
confirmLabel="Generate new secret"
confirmLabelLoading="Generating"
onCancel={() => setIsGeneratingKey(false)}
onConfirm={() => handleJwtSecretUpdate('ROLL', setIsGeneratingKey)}
alert={{
title: 'This will invalidate all existing API keys',
description: (
<>
Generating a new JWT secret will invalidate <u className="text-foreground">all</u> of
your API keys, including your <code className="text-xs">service_role</code> and{' '}
<code className="text-xs">anon</code> keys. Your project will also be restarted during
this process, which will terminate any existing connections. You may receive API
errors for up to 2 minutes while the new secret is deployed.
</AlertDescription_Shadcn_>
</Alert_Shadcn_>
<p className="text-foreground text-sm">
This action cannot be undone and the old JWT secret will be lost. All existing API keys
will be invalidated, and any open connections will be terminated.
</p>
</Modal.Content>
</>
),
}}
>
<p className="text-foreground text-sm">
This action cannot be undone and the old JWT secret will be lost. All existing API keys
will be invalidated, and any open connections will be terminated.
</p>
</ConfirmationModal>
<Modal

View File

@@ -106,28 +106,20 @@ const BannedIPs = () => {
</FormPanel>
<ConfirmationModal
danger
variant="destructive"
size="medium"
loading={isUnbanning}
visible={showUnban}
header="Confirm Unban IP"
buttonLabel="Confirm Unban"
buttonLoadingLabel="Unbanning..."
onSelectConfirm={onConfirmUnbanIP}
onSelectCancel={() => setShowUnban(false)}
>
<Modal.Content>
<div className="py-6">
<Alert_Shadcn_ variant="warning">
<IconAlertTriangle strokeWidth={2} />
<AlertTitle_Shadcn_>This action cannot be undone</AlertTitle_Shadcn_>
<AlertDescription_Shadcn_>
Are you sure you want to unban this IP address {selectedIPToUnban}?
</AlertDescription_Shadcn_>
</Alert_Shadcn_>
</div>
</Modal.Content>
</ConfirmationModal>
title="Confirm Unban IP"
confirmLabel="Confirm Unban"
confirmLabelLoading="Unbanning..."
onCancel={() => setShowUnban(false)}
onConfirm={onConfirmUnbanIP}
alert={{
title: 'This action cannot be undone',
description: `Are you sure you want to unban this IP address ${selectedIPToUnban}?`,
}}
/>
</div>
)
}

View File

@@ -147,20 +147,18 @@ const CustomDomainActivate = ({ projectRef, customDomain }: CustomDomainActivate
size="small"
loading={isCheckingRecord || isActivating}
visible={isActivateConfirmModalVisible}
header={
<div>
title={
<>
Are you sure you want to activate the custom domain{' '}
<code className="text-sm">{customDomain.hostname}</code> for the project?
</div>
</>
}
buttonLabel="Activate"
buttonLoadingLabel="Activating"
onSelectCancel={() => setIsActivateConfirmModalVisible(false)}
onSelectConfirm={onActivateCustomDomain}
confirmLabel="Activate"
confirmLabelLoading="Activating"
onCancel={() => setIsActivateConfirmModalVisible(false)}
onConfirm={onActivateCustomDomain}
>
<Modal.Content className="py-3">
<p className="text-sm">The existing Supabase subdomain will be deactivated.</p>
</Modal.Content>
<p className="text-sm">The existing Supabase subdomain will be deactivated.</p>
</ConfirmationModal>
</>
)

View File

@@ -94,22 +94,19 @@ const PauseProjectButton = () => {
) : null}
</Tooltip.Root>
<ConfirmationModal
danger
variant={'destructive'}
visible={isModalOpen}
loading={isPausing}
header="Pause this project?"
description=""
buttonLabel="Pause project"
buttonLoadingLabel="Pausing project"
onSelectCancel={() => setIsModalOpen(false)}
onSelectConfirm={requestPauseProject}
title="Pause this project?"
confirmLabel="Pause project"
confirmLabelLoading="Pausing project"
onCancel={() => setIsModalOpen(false)}
onConfirm={requestPauseProject}
>
<Modal.Content className="py-4">
<p className="text-foreground-light text-sm">
Are you sure you want to pause this project? It will not be accessible until you unpause
it.
</p>
</Modal.Content>
<p className="text-foreground-light text-sm">
Are you sure you want to pause this project? It will not be accessible until you unpause
it.
</p>
</ConfirmationModal>
</>
)

View File

@@ -59,31 +59,24 @@ const DropAllReplicasConfirmationModal = ({
return (
<ConfirmationModal
danger
variant={'destructive'}
size="medium"
loading={isRemoving}
visible={visible}
header="Confirm to drop all read replicas?"
buttonLabel="Drop all replicas"
buttonLoadingLabel="Dropping all replicas"
onSelectCancel={() => onCancel()}
onSelectConfirm={() => onConfirmRemove()}
title="Confirm to drop all read replicas?"
confirmLabel="Drop all replicas"
confirmLabelLoading="Dropping all replicas"
onCancel={() => onCancel()}
onConfirm={() => onConfirmRemove()}
alert={{
title: 'This action cannot be undone',
description: 'You may still deploy new replicas in this region thereafter',
}}
>
<Modal.Content className="py-3">
<Alert_Shadcn_ variant="warning">
<IconAlertTriangle strokeWidth={2} />
<AlertTitle_Shadcn_>This action cannot be undone</AlertTitle_Shadcn_>
<AlertDescription_Shadcn_>
You may still deploy new replicas in this region thereafter
</AlertDescription_Shadcn_>
</Alert_Shadcn_>
<div className="text-sm px-1 pt-4">
<p>Before deleting all replicas, consider:</p>
<ul className="text-foreground-light py-1 list-disc mx-4 space-y-1">
<li>Network traffic from this region may slow down</li>
</ul>
</div>
</Modal.Content>
<p className="text-sm">Before deleting all replicas, consider:</p>
<ul className="text-sm text-foreground-light list-disc">
<li>Network traffic from this region may slow down</li>
</ul>
</ConfirmationModal>
)
}

View File

@@ -43,34 +43,27 @@ const DropReplicaConfirmationModal = ({
return (
<ConfirmationModal
danger
variant="destructive"
size="medium"
loading={isRemoving}
visible={selectedReplica !== undefined}
header={`Confirm to drop selected replica? (ID: ${formattedId})`}
buttonLabel="Drop replica"
buttonLoadingLabel="Dropping replica"
onSelectCancel={() => onCancel()}
onSelectConfirm={() => onConfirmRemove()}
title={`Confirm to drop selected replica? (ID: ${formattedId})`}
confirmLabel="Drop replica"
confirmLabelLoading="Dropping replica"
onCancel={() => onCancel()}
onConfirm={() => onConfirmRemove()}
alert={{
title: 'This action cannot be undone',
description: 'You may still deploy a new replica in this region thereafter',
}}
>
<Modal.Content className="py-3">
<Alert_Shadcn_ variant="warning">
<IconAlertTriangle strokeWidth={2} />
<AlertTitle_Shadcn_>This action cannot be undone</AlertTitle_Shadcn_>
<AlertDescription_Shadcn_>
You may still deploy a new replica in this region thereafter
</AlertDescription_Shadcn_>
</Alert_Shadcn_>
<div className="text-sm px-1 pt-4">
<p>Before deleting this replica, consider:</p>
<ul className="text-foreground-light py-1 list-disc mx-4 space-y-1">
<li>
Network traffic from this region may slow down, especially if you have no other
replicas in this region
</li>
</ul>
</div>
</Modal.Content>
<p className="text-sm">Before deleting this replica, consider:</p>
<ul className="text-sm text-foreground-light py-1 list-disc mx-4 space-y-1">
<li>
Network traffic from this region may slow down, especially if you have no other replicas
in this region
</li>
</ul>
</ConfirmationModal>
)
}

View File

@@ -285,24 +285,22 @@ const InstanceConfigurationUI = () => {
{/* <ConfirmationModal
size="medium"
visible={selectedReplicaToRestart !== undefined}
header="Confirm to restart selected replica?"
buttonLabel="Restart replica"
buttonLoadingLabel="Restarting replica"
onSelectCancel={() => setSelectedReplicaToRestart(undefined)}
onSelectConfirm={() => onConfirmRestartReplica()}
title="Confirm to restart selected replica?"
confirmLabel="Restart replica"
confirmLabelLoading="Restarting replica"
onCancel={() => setSelectedReplicaToRestart(undefined)}
onConfirm={() => onConfirmRestartReplica()}
>
<Modal.Content className="py-3">
<p className="text-sm">Before restarting the replica, consider:</p>
<ul className="text-sm text-foreground-light py-1 list-disc mx-4 space-y-1">
<li>
Network traffic from this region may slow down while the replica is restarting,
especially if you have no other replicas in this region
</li>
</ul>
<p className="text-sm mt-2">
Are you sure you want to restart this replica (ID: {selectedReplicaToRestart?.id}) now?{' '}
</p>
</Modal.Content>
<p className="text-sm">Before restarting the replica, consider:</p>
<ul className="text-sm text-foreground-light py-1 list-disc mx-4 space-y-1">
<li>
Network traffic from this region may slow down while the replica is restarting,
especially if you have no other replicas in this region
</li>
</ul>
<p className="text-sm mt-2">
Are you sure you want to restart this replica (ID: {selectedReplicaToRestart?.id}) now?{' '}
</p>
</ConfirmationModal> */}
</>
)

View File

@@ -132,20 +132,18 @@ const SavedQueriesItem = ({ item }: SavedQueriesItemProps) => {
</DropdownMenuContent>
</DropdownMenu>
<ConfirmationModal
danger
variant="destructive"
visible={showConfirmModal}
buttonLabel="Delete query"
header="Confirm to delete saved query"
onSelectCancel={() => {
confirmLabel="Delete query"
title="Confirm to delete saved query"
onCancel={() => {
setShowConfirmModal(false)
}}
onSelectConfirm={onConfirmDelete}
onConfirm={onConfirmDelete}
>
<Modal.Content>
<p className="py-4 text-sm text-foreground-light">
Are you sure you want to delete {item.name}?
</p>
</Modal.Content>
<p className="text-sm text-foreground-light">
Are you sure you want to delete {item.name}?
</p>
</ConfirmationModal>
<UpdateSavedQueryModal
visible={showUpdateModal}

View File

@@ -1,6 +1,15 @@
import Link from 'next/link'
import toast from 'react-hot-toast'
import { Alert, Button, Checkbox, IconExternalLink, Modal } from 'ui'
import {
Alert,
AlertDescription_Shadcn_,
AlertTitle_Shadcn_,
Alert_Shadcn_,
Button,
Checkbox,
IconExternalLink,
Modal,
} from 'ui'
import type { SupaRow } from 'components/grid'
import { formatFilterURLParams } from 'components/grid/SupabaseGrid.utils'
@@ -220,40 +229,39 @@ const DeleteConfirmationDialogs = ({
return (
<>
<ConfirmationModal
danger
variant="destructive"
size="small"
visible={snap.confirmationDialog?.type === 'column'}
header={`Confirm deletion of column "${
title={`Confirm deletion of column "${
snap.confirmationDialog?.type === 'column' && snap.confirmationDialog.column.name
}"`}
buttonLabel="Delete"
buttonLoadingLabel="Deleting"
onSelectCancel={() => {
confirmLabel="Delete"
confirmLabelLoading="Deleting"
onCancel={() => {
snap.closeConfirmationDialog()
}}
onSelectConfirm={onConfirmDeleteColumn}
onConfirm={onConfirmDeleteColumn}
>
<Modal.Content>
<div className="py-4 space-y-4">
<p className="text-sm text-foreground-light">
Are you sure you want to delete the selected column? This action cannot be undone.
</p>
<Checkbox
label="Drop column with cascade?"
description="Deletes the column and its dependent objects"
checked={isDeleteWithCascade}
onChange={() => snap.toggleConfirmationIsWithCascade()}
/>
{isDeleteWithCascade && (
<Alert
withIcon
variant="warning"
title="Warning: Dropping with cascade may result in unintended consequences"
>
<p className="mb-4">
All dependent objects will be removed, as will any objects that depend on them,
recursively.
</p>
<div className="space-y-4">
<p className="text-sm text-foreground-light">
Are you sure you want to delete the selected column? This action cannot be undone.
</p>
<Checkbox
label="Drop column with cascade?"
description="Deletes the column and its dependent objects"
checked={isDeleteWithCascade}
onChange={() => snap.toggleConfirmationIsWithCascade()}
/>
{isDeleteWithCascade && (
<Alert_Shadcn_
variant="warning"
title="Warning: Dropping with cascade may result in unintended consequences"
>
<AlertTitle_Shadcn_>
All dependent objects will be removed, as will any objects that depend on them,
recursively.
</AlertTitle_Shadcn_>
<AlertDescription_Shadcn_>
<Button asChild size="tiny" type="default" icon={<IconExternalLink />}>
<Link
href="https://www.postgresql.org/docs/current/ddl-depend.html"
@@ -263,85 +271,81 @@ const DeleteConfirmationDialogs = ({
About dependency tracking
</Link>
</Button>
</Alert>
)}
</div>
</Modal.Content>
</AlertDescription_Shadcn_>
</Alert_Shadcn_>
)}
</div>
</ConfirmationModal>
<ConfirmationModal
danger
variant={'destructive'}
size="small"
visible={snap.confirmationDialog?.type === 'table'}
header={
title={
<span className="break-words">{`Confirm deletion of table "${selectedTable?.name}"`}</span>
}
buttonLabel="Delete"
buttonLoadingLabel="Deleting"
onSelectCancel={() => {
confirmLabel="Delete"
confirmLabelLoading="Deleting"
onCancel={() => {
snap.closeConfirmationDialog()
}}
onSelectConfirm={onConfirmDeleteTable}
onConfirm={onConfirmDeleteTable}
>
<Modal.Content>
<div className="py-4 space-y-4">
<p className="text-sm text-foreground-light">
Are you sure you want to delete the selected table? This action cannot be undone.
</p>
<Checkbox
label="Drop table with cascade?"
description="Deletes the table and its dependent objects"
checked={isDeleteWithCascade}
onChange={() => snap.toggleConfirmationIsWithCascade(!isDeleteWithCascade)}
/>
{isDeleteWithCascade && (
<Alert
withIcon
variant="warning"
title="Warning: Dropping with cascade may result in unintended consequences"
>
<p className="mb-4">
All dependent objects will be removed, as will any objects that depend on them,
recursively.
</p>
<Button asChild size="tiny" type="default" icon={<IconExternalLink />}>
<Link
href="https://www.postgresql.org/docs/current/ddl-depend.html"
target="_blank"
rel="noreferrer"
>
About dependency tracking
</Link>
</Button>
</Alert>
)}
</div>
</Modal.Content>
<div className="space-y-4">
<p className="text-sm text-foreground-light">
Are you sure you want to delete the selected table? This action cannot be undone.
</p>
<Checkbox
label="Drop table with cascade?"
description="Deletes the table and its dependent objects"
checked={isDeleteWithCascade}
onChange={() => snap.toggleConfirmationIsWithCascade(!isDeleteWithCascade)}
/>
{isDeleteWithCascade && (
<Alert
withIcon
variant="warning"
title="Warning: Dropping with cascade may result in unintended consequences"
>
<p className="mb-4">
All dependent objects will be removed, as will any objects that depend on them,
recursively.
</p>
<Button asChild size="tiny" type="default" icon={<IconExternalLink />}>
<Link
href="https://www.postgresql.org/docs/current/ddl-depend.html"
target="_blank"
rel="noreferrer"
>
About dependency tracking
</Link>
</Button>
</Alert>
)}
</div>
</ConfirmationModal>
<ConfirmationModal
danger
variant={'destructive'}
size="small"
visible={snap.confirmationDialog?.type === 'row'}
header={
title={
<span className="break-words">
Confirm to delete the selected row{numRows > 1 && 's'}
</span>
}
buttonLabel="Delete"
buttonLoadingLabel="Deleting"
onSelectCancel={() => snap.closeConfirmationDialog()}
onSelectConfirm={() => onConfirmDeleteRow()}
confirmLabel="Delete"
confirmLabelLoading="Deleting"
onCancel={() => snap.closeConfirmationDialog()}
onConfirm={() => onConfirmDeleteRow()}
>
<Modal.Content>
<div className="py-4 space-y-4">
<p className="text-sm text-foreground-light">
Are you sure you want to delete {isAllRowsSelected ? 'all' : 'the selected'}{' '}
{numRows > 1 && `${numRows} `}row
{numRows > 1 && 's'}? This action cannot be undone.
</p>
</div>
</Modal.Content>
<div className="space-y-4">
<p className="text-sm text-foreground-light">
Are you sure you want to delete {isAllRowsSelected ? 'all' : 'the selected'}{' '}
{numRows > 1 && `${numRows} `}row
{numRows > 1 && 's'}? This action cannot be undone.
</p>
</div>
</ConfirmationModal>
</>
)

View File

@@ -267,13 +267,13 @@ const GridHeaderActions = ({ table, isViewSelected, isTableSelected }: GridHeade
<ConfirmationModal
visible={showEnableRealtime}
loading={isTogglingRealtime}
header={`${isRealtimeEnabled ? 'Disable' : 'Enable'} realtime for ${table.name}`}
buttonLabel={`${isRealtimeEnabled ? 'Disable' : 'Enable'} realtime`}
buttonLoadingLabel={`${isRealtimeEnabled ? 'Disabling' : 'Enabling'} realtime`}
onSelectCancel={() => setShowEnableRealtime(false)}
onSelectConfirm={() => toggleRealtime()}
title={`${isRealtimeEnabled ? 'Disable' : 'Enable'} realtime for ${table.name}`}
confirmLabel={`${isRealtimeEnabled ? 'Disable' : 'Enable'} realtime`}
confirmLabelLoading={`${isRealtimeEnabled ? 'Disabling' : 'Enabling'} realtime`}
onCancel={() => setShowEnableRealtime(false)}
onConfirm={() => toggleRealtime()}
>
<Modal.Content className="py-4 space-y-2">
<div className="space-y-2">
<p className="text-sm">
Once realtime has been {isRealtimeEnabled ? 'disabled' : 'enabled'}, the table will{' '}
{isRealtimeEnabled ? 'no longer ' : ''}broadcast any changes to authorized subscribers.
@@ -287,7 +287,7 @@ const GridHeaderActions = ({ table, isViewSelected, isTableSelected }: GridHeade
settings.
</p>
)}
</Modal.Content>
</div>
</ConfirmationModal>
<ConfirmModal

View File

@@ -642,21 +642,19 @@ const SidePanelEditor = ({
/>
<ConfirmationModal
visible={isClosingPanel}
header="Discard changes"
buttonLabel="Discard"
onSelectCancel={() => setIsClosingPanel(false)}
onSelectConfirm={() => {
title="Discard changes"
confirmLabel="Discard"
onCancel={() => setIsClosingPanel(false)}
onConfirm={() => {
setIsClosingPanel(false)
setIsEdited(false)
snap.closeSidePanel()
}}
>
<Modal.Content>
<p className="py-4 text-sm text-foreground-light">
There are unsaved changes. Are you sure you want to close the panel? Your changes will
be lost.
</p>
</Modal.Content>
<p className="text-sm text-foreground-light">
There are unsaved changes. Are you sure you want to close the panel? Your changes will be
lost.
</p>
</ConfirmationModal>
</>
)

View File

@@ -402,18 +402,16 @@ const TableEditor = ({
<ConfirmationModal
visible={rlsConfirmVisible}
header="Turn off Row Level Security"
buttonLabel="Confirm"
title="Turn off Row Level Security"
confirmLabel="Confirm"
size="medium"
onSelectCancel={() => setRlsConfirmVisible(false)}
onSelectConfirm={() => {
onCancel={() => setRlsConfirmVisible(false)}
onConfirm={() => {
onUpdateField({ isRLSEnabled: !tableFields.isRLSEnabled })
setRlsConfirmVisible(false)
}}
>
<Modal.Content>
<RLSDisableModalContent />
</Modal.Content>
<RLSDisableModalContent />
</ConfirmationModal>
</SidePanel.Content>

View File

@@ -287,13 +287,14 @@ const ReportsMenu = () => {
}}
/>
<ConfirmationModal
header="Delete custom report"
buttonLabel="Delete report"
buttonLoadingLabel="Deleting report"
title="Delete custom report"
confirmLabel="Delete report"
confirmLabelLoading="Deleting report"
size="medium"
loading={deleteReport.isLoading}
visible={deleteModalOpen}
onSelectConfirm={async () => {
onCancel={() => setDeleteModalOpen(false)}
onConfirm={async () => {
if (selectedReport) {
if (!selectedReport.id) return
await deleteReport.mutateAsync({
@@ -305,17 +306,12 @@ const ReportsMenu = () => {
}
setDeleteModalOpen(false)
}}
onSelectCancel={() => setDeleteModalOpen(false)}
>
<Modal.Content>
<div className="my-6">
<div className="text-sm text-foreground-light grid gap-4">
<div className="grid gap-1">
<p>Are you sure you want to delete '{selectedReport?.name}'?</p>
</div>
</div>
<div className="text-sm text-foreground-light grid gap-4">
<div className="grid gap-1">
<p>Are you sure you want to delete '{selectedReport?.name}'?</p>
</div>
</Modal.Content>
</div>
</ConfirmationModal>
<CreateReportModal
visible={showNewReportModal}

View File

@@ -28,6 +28,7 @@ import type { SqlSnippet } from 'data/content/sql-snippets-query'
import { useSqlEditorStateSnapshot } from 'state/sql-editor'
import { CriticalIcon, WarningIcon } from 'ui-patterns/Icons/StatusIcons'
import { QueryItemActions } from './QueryItemActions'
import { Eye, Unlock } from 'lucide-react'
export interface QueryItemProps {
tabInfo: SqlSnippet
@@ -188,58 +189,50 @@ const QueryItem = ({
onComplete={() => setRenameModalOpen(false)}
/>
<ConfirmationModal
header="Confirm to delete query"
buttonLabel="Delete query"
buttonLoadingLabel="Deleting query"
title="Confirm to delete query"
confirmLabel="Delete query"
confirmLabelLoading="Deleting query"
size="medium"
loading={isDeleting}
visible={deleteModalOpen}
onSelectConfirm={onConfirmDelete}
onSelectCancel={() => setDeleteModalOpen(false)}
variant={'destructive'}
onCancel={() => setDeleteModalOpen(false)}
onConfirm={onConfirmDelete}
alert={
visibility === 'project'
? {
title: 'This SQL snippet will be lost forever',
description:
'Deleting this query will remove it for all members of the project team.',
}
: {}
}
>
<Modal.Content className="my-6 grid gap-1 text-sm text-foreground-light grid gap-4">
{visibility === 'project' && (
<Alert_Shadcn_ variant="destructive">
<CriticalIcon />
<AlertTitle_Shadcn_>This SQL snippet will be lost forever</AlertTitle_Shadcn_>
<AlertDescription_Shadcn_>
Deleting this query will remove it for all members of the project team.
</AlertDescription_Shadcn_>
</Alert_Shadcn_>
)}
<p>Are you sure you want to delete '{name}'?</p>
</Modal.Content>
<p className="text-sm">Are you sure you want to delete '{name}'?</p>
</ConfirmationModal>
<ConfirmationModal
header="Confirm sharing query"
title="Confirm sharing query"
size="medium"
buttonLabel="Share query"
buttonLoadingLabel="Sharing query"
confirmLabel="Share query"
confirmLabelLoading="Sharing query"
visible={shareModalOpen}
onSelectConfirm={onConfirmShare}
onSelectCancel={() => setShareModalOpen(false)}
onCancel={() => setShareModalOpen(false)}
onConfirm={onConfirmShare}
alert={{
title: 'This SQL query will become public to all team members',
description: 'Anyone with access to the project can view it',
}}
>
<Modal.Content className="my-6 text-sm text-foreground-light grid gap-4 grid gap-1">
<Alert_Shadcn_ variant="warning">
<WarningIcon />
<AlertTitle_Shadcn_>
This SQL query will become public to all team members
</AlertTitle_Shadcn_>
<AlertDescription_Shadcn_>
Anyone with access to the project can view it
</AlertDescription_Shadcn_>
</Alert_Shadcn_>
<ul className="mt-4 space-y-5">
<li className="flex gap-3">
<IconEye />
<span>Project members will have read-only access to this query.</span>
</li>
<li className="flex gap-3">
<IconUnlock />
<span>Anyone will be able to duplicate it to their personal snippets.</span>
</li>
</ul>
</Modal.Content>
<ul className="text-sm text-foreground-light space-y-5">
<li className="flex gap-3">
<Eye />
<span>Project members will have read-only access to this query.</span>
</li>
<li className="flex gap-3">
<Unlock />
<span>Anyone will be able to duplicate it to their personal snippets.</span>
</li>
</ul>
</ConfirmationModal>
<DownloadSnippetModal
id={id as string}

View File

@@ -349,35 +349,24 @@ const SideBarContent = () => {
</div>
<ConfirmationModal
header="Confirm to delete query"
buttonLabel={`Delete ${selectedQueries.length.toLocaleString()} quer${selectedQueries.length > 1 ? 'ies' : 'y'}`}
buttonLoadingLabel="Deleting query"
title="Confirm to delete query"
confirmLabel={`Delete ${selectedQueries.length.toLocaleString()} quer${selectedQueries.length > 1 ? 'ies' : 'y'}`}
confirmLabelLoading="Deleting query"
size="medium"
loading={isDeleting}
visible={showDeleteModal}
onSelectConfirm={onConfirmDelete}
onSelectCancel={() => setShowDeleteModal(false)}
danger
onConfirm={onConfirmDelete}
onCancel={() => setShowDeleteModal(false)}
variant={'destructive'}
alert={{
title: 'This action cannot be undone',
description: 'The selected SQL snippets cannot be recovered once deleted',
}}
>
<Modal.Content>
<div className="my-6">
<div className="text-sm text-foreground-light grid gap-4">
<div className="grid gap-y-4">
<Alert_Shadcn_ variant="destructive">
<WarningIcon />
<AlertTitle_Shadcn_>This action cannot be undone</AlertTitle_Shadcn_>
<AlertDescription_Shadcn_>
The selected SQL snippets cannot be recovered once deleted
</AlertDescription_Shadcn_>
</Alert_Shadcn_>
<p>
Are you sure you want to delete the selected {selectedQueries.length} quer
{selectedQueries.length > 1 ? 'ies' : 'y'}?
</p>
</div>
</div>
</div>
</Modal.Content>
<p className="text-sm text-foreground-light">
Are you sure you want to delete the selected {selectedQueries.length} quer
{selectedQueries.length > 1 ? 'ies' : 'y'}?
</p>
</ConfirmationModal>
</>
)

View File

@@ -40,24 +40,19 @@ export const EmptyBucketModal = ({ visible = false, bucket, onClose }: EmptyBuck
return (
<ConfirmationModal
danger
variant={'destructive'}
size="small"
title={`Confirm to delete all contents from ${bucket?.name}`}
confirmLabel="Empty bucket"
visible={visible}
onSelectCancel={() => onClose()}
onSelectConfirm={onEmptyBucket}
header={`Confirm to delete all contents from ${bucket?.name}`}
buttonLabel="Empty bucket"
onCancel={() => onClose()}
onConfirm={onEmptyBucket}
alert={{
title: 'This action cannot be undone',
description: 'The contents of your bucket cannot be recovered once deleted',
}}
>
<Modal.Content className="py-4 space-y-2">
<Alert_Shadcn_ variant="warning">
<IconAlertTriangle strokeWidth={2} />
<AlertTitle_Shadcn_>This action cannot be undone</AlertTitle_Shadcn_>
<AlertDescription_Shadcn_>
The contents of your bucket cannot be recovered once deleted
</AlertDescription_Shadcn_>
</Alert_Shadcn_>
<p className="text-sm">Are you sure you want to empty the bucket "{bucket?.name}"?</p>
</Modal.Content>
<p className="text-sm">Are you sure you want to empty the bucket "{bucket?.name}"?</p>
</ConfirmationModal>
)
}

View File

@@ -1,80 +1,135 @@
import { MouseEventHandler, PropsWithChildren, useEffect, useState } from 'react'
import { Button, Modal } from 'ui'
'use client'
import { MouseEventHandler, forwardRef, useEffect, useState } from 'react'
import {
Admonition,
Alert_Shadcn_,
Button,
Dialog,
DialogContent,
DialogSection,
DialogSectionSeparator,
DialogTitle,
cn,
} from 'ui'
import { DialogHeader } from 'ui/src/components/shadcn/ui/dialog'
export interface ConfirmationModalProps {
visible: boolean
danger?: boolean
loading?: boolean
header: string | JSX.Element
description?: string
size?: 'small' | 'tiny' | 'medium' | 'large'
buttonLabel: string
buttonLoadingLabel?: string
buttonDisabled?: boolean
onSelectCancel: () => void
onSelectConfirm: () => void
}
const ConfirmationModal = ({
visible = false,
loading: loading_ = false,
danger = false,
header = '',
description = '',
size = 'small',
buttonLabel = '',
buttonLoadingLabel = '',
buttonDisabled = false,
onSelectCancel = () => {},
onSelectConfirm = () => {},
children,
}: PropsWithChildren<ConfirmationModalProps>) => {
useEffect(() => {
if (visible) {
setLoading(false)
}
}, [visible])
useEffect(() => {
setLoading(loading_)
}, [loading_])
const [loading, setLoading] = useState(false)
const onConfirm: MouseEventHandler<HTMLButtonElement> = (e) => {
e.preventDefault()
e.stopPropagation()
setLoading(true)
onSelectConfirm()
visible: boolean
title: string | React.ReactNode
size?: React.ComponentProps<typeof DialogContent>['size']
confirmLabel?: string
confirmLabelLoading?: string
cancelLabel?: string
onConfirm: () => void
onCancel: () => void
disabled?: boolean
variant?: React.ComponentProps<typeof Alert_Shadcn_>['variant']
alert?: {
base?: React.ComponentProps<typeof Alert_Shadcn_>
title?: string
description?: string | React.ReactNode
}
return (
<Modal
layout="vertical"
visible={visible}
header={header}
description={description}
size={size}
onCancel={onSelectCancel}
customFooter={
<div className="flex justify-end w-full items-center space-x-3">
<Button type="default" disabled={loading} onClick={onSelectCancel}>
Cancel
</Button>
<Button
type={danger ? 'danger' : 'primary'}
loading={loading}
disabled={loading || buttonDisabled}
onClick={onConfirm}
>
{(loading && buttonLoadingLabel) || buttonLabel}
</Button>
</div>
}
>
{children}
</Modal>
)
}
const ConfirmationModal = forwardRef<
React.ElementRef<typeof DialogContent>,
React.ComponentPropsWithoutRef<typeof Dialog> & ConfirmationModalProps
>(
(
{
title,
size = 'small',
visible,
onCancel,
onConfirm,
loading: loading_ = false,
cancelLabel = 'Cancel',
confirmLabel = 'Submit',
confirmLabelLoading,
alert,
children,
variant = 'default',
disabled,
...props
},
ref
) => {
useEffect(() => {
if (visible) {
setLoading(false)
}
}, [visible])
useEffect(() => {
setLoading(loading_)
}, [loading_])
const [loading, setLoading] = useState(false)
const onSubmit: MouseEventHandler<HTMLButtonElement> = (e) => {
e.preventDefault()
e.stopPropagation()
setLoading(true)
onConfirm()
}
return (
<Dialog
open={visible}
{...props}
onOpenChange={() => {
if (visible) {
onCancel()
}
}}
>
<DialogContent ref={ref} className="p-0 gap-0 pb-5 !block" size={size}>
<DialogHeader className={cn('border-b')} padding={'small'}>
<DialogTitle className="">{title}</DialogTitle>
</DialogHeader>
{alert && (
<Admonition
type={variant as 'default' | 'destructive' | 'warning'}
label={alert.title}
description={alert.description}
className="border-r-0 border-l-0 rounded-none -mt-px [&_svg]:ml-0.5 mb-0"
{...alert?.base}
/>
)}
{children && (
<>
<DialogSection padding={'small'}>{children}</DialogSection>
<DialogSectionSeparator />
</>
)}
<div className="flex gap-2 px-5 pt-5">
<Button size="medium" block type="default" disabled={loading}>
{cancelLabel}
</Button>
<Button
block
size="medium"
type={
variant === 'destructive' ? 'danger' : variant === 'warning' ? 'warning' : 'primary'
}
htmlType="submit"
loading={loading}
disabled={loading || disabled}
onClick={onSubmit}
className="truncate"
>
{confirmLabel}
</Button>
</div>
</DialogContent>
</Dialog>
)
}
)
ConfirmationModal.displayName = 'ConfirmationModal'
export default ConfirmationModal

View File

@@ -5,15 +5,13 @@ import { ReactNode, forwardRef } from 'react'
import { useForm } from 'react-hook-form'
import {
Admonition,
AlertDescription_Shadcn_,
AlertTitle_Shadcn_,
Alert_Shadcn_,
Button,
DialogContent,
DialogSectionSeparator,
DialogSection,
DialogTitle,
Dialog,
DialogContent,
DialogSection,
DialogSectionSeparator,
DialogTitle,
FormControl_Shadcn_,
FormDescription_Shadcn_,
FormField_Shadcn_,
@@ -21,15 +19,10 @@ import {
FormLabel_Shadcn_,
FormMessage_Shadcn_,
Form_Shadcn_,
IconAlertCircle,
Input_Shadcn_,
cn,
} from 'ui'
import {
DIALOG_PADDING_X_SMALL,
DIALOG_PADDING_Y_SMALL,
DialogHeader,
} from 'ui/src/components/shadcn/ui/dialog'
import { DialogHeader } from 'ui/src/components/shadcn/ui/dialog'
import { z } from 'zod'
export interface TextConfirmModalProps {

View File

@@ -1,5 +1,5 @@
import { cva } from 'class-variance-authority'
import { forwardRef } from 'react'
import React, { forwardRef } from 'react'
import { cn } from '../../lib/utils/cn'
import { Alert, AlertDescription, AlertTitle } from './../shadcn/ui/alert'
@@ -15,7 +15,7 @@ export interface AdmonitionProps {
| 'warning'
label?: string
title?: string
description?: string
description?: string | React.ReactNode
}
const admonitionToAlertMapping: Record<