mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 17:35:10 +03:00
chore(studio): delete Confirm Modal component (#41295)
* docs * naming * docs * mini fix * update policies component * use destructured prop * rls policies dialogs * custom domain dialogs * restart server dialog * remove ConfirmDialog (aka ConfirmModal) * remove unrelated changes * delete unrelated file * language * Update apps/studio/components/interfaces/Storage/StoragePolicies/StoragePolicies.tsx Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * fixed broken test --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: Ali Waseem <waseema393@gmail.com>
This commit is contained in:
14 files changed
+161
-315
No files matched your search
@@ -38,17 +38,6 @@ export const Index: Record<string, any> = {
|
||||
subcategory: "undefined",
|
||||
chunks: []
|
||||
},
|
||||
"ConfirmDialog": {
|
||||
name: "ConfirmDialog",
|
||||
type: "components:fragment",
|
||||
registryDependencies: undefined,
|
||||
component: React.lazy(() => import("@/../../packages/ui-patterns/src/Dialogs/ConfirmDialog")),
|
||||
source: "",
|
||||
files: ["registry/default//Dialogs/ConfirmDialog.tsx"],
|
||||
category: "undefined",
|
||||
subcategory: "undefined",
|
||||
chunks: []
|
||||
},
|
||||
"PageContainer": {
|
||||
name: "PageContainer",
|
||||
type: "components:fragment",
|
||||
|
||||
@@ -19,12 +19,6 @@ export const fragments: Registry = [
|
||||
files: ['/Dialogs/TextConfirmModal.tsx'],
|
||||
optionalPath: '/Dialogs',
|
||||
},
|
||||
{
|
||||
name: 'ConfirmDialog',
|
||||
type: 'components:fragment',
|
||||
files: ['/Dialogs/ConfirmDialog.tsx'],
|
||||
optionalPath: '/Dialogs',
|
||||
},
|
||||
{
|
||||
name: 'PageContainer',
|
||||
type: 'components:fragment',
|
||||
|
||||
@@ -15,7 +15,7 @@ import { useDatabasePolicyDeleteMutation } from 'data/database-policies/database
|
||||
import { useTableUpdateMutation } from 'data/tables/table-update-mutation'
|
||||
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
|
||||
import { Button, Card, CardContent } from 'ui'
|
||||
import ConfirmModal from 'ui-patterns/Dialogs/ConfirmDialog'
|
||||
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
|
||||
|
||||
interface PoliciesProps {
|
||||
search?: string
|
||||
@@ -51,7 +51,7 @@ export const Policies = ({
|
||||
}>()
|
||||
const [selectedPolicyToDelete, setSelectedPolicyToDelete] = useState<any>({})
|
||||
|
||||
const { mutate: updateTable } = useTableUpdateMutation({
|
||||
const { mutate: updateTable, isPending: isUpdatingTable } = useTableUpdateMutation({
|
||||
onError: (error) => {
|
||||
toast.error(`Failed to toggle RLS: ${error.message}`)
|
||||
},
|
||||
@@ -60,14 +60,15 @@ export const Policies = ({
|
||||
},
|
||||
})
|
||||
|
||||
const { mutate: deleteDatabasePolicy } = useDatabasePolicyDeleteMutation({
|
||||
onSuccess: () => {
|
||||
toast.success('Successfully deleted policy!')
|
||||
},
|
||||
onSettled: () => {
|
||||
closeConfirmModal()
|
||||
},
|
||||
})
|
||||
const { mutate: deleteDatabasePolicy, isPending: isDeletingPolicy } =
|
||||
useDatabasePolicyDeleteMutation({
|
||||
onSuccess: () => {
|
||||
toast.success('Successfully deleted policy!')
|
||||
},
|
||||
onSettled: () => {
|
||||
closeConfirmModal()
|
||||
},
|
||||
})
|
||||
|
||||
const closeConfirmModal = useCallback(() => {
|
||||
setSelectedPolicyToDelete({})
|
||||
@@ -180,30 +181,30 @@ export const Policies = ({
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
<ConfirmModal
|
||||
danger
|
||||
<ConfirmationModal
|
||||
visible={!isEmpty(selectedPolicyToDelete)}
|
||||
title="Confirm to delete policy"
|
||||
description={`This is permanent! Are you sure you want to delete the policy "${selectedPolicyToDelete.name}"`}
|
||||
buttonLabel="Delete"
|
||||
buttonLoadingLabel="Deleting"
|
||||
onSelectCancel={closeConfirmModal}
|
||||
onSelectConfirm={onDeletePolicy}
|
||||
variant="destructive"
|
||||
title="Delete policy"
|
||||
description={`Are you sure you want to delete the policy “${selectedPolicyToDelete.name}”? This action cannot be undone.`}
|
||||
confirmLabel="Delete"
|
||||
confirmLabelLoading="Deleting"
|
||||
loading={isDeletingPolicy}
|
||||
onCancel={closeConfirmModal}
|
||||
onConfirm={onDeletePolicy}
|
||||
/>
|
||||
|
||||
<ConfirmModal
|
||||
danger={selectedTableToToggleRLS?.rls_enabled}
|
||||
<ConfirmationModal
|
||||
visible={selectedTableToToggleRLS !== undefined}
|
||||
title={`Confirm to ${
|
||||
selectedTableToToggleRLS?.rls_enabled ? 'disable' : 'enable'
|
||||
} Row Level Security`}
|
||||
variant={selectedTableToToggleRLS?.rls_enabled ? 'destructive' : 'default'}
|
||||
title={`${selectedTableToToggleRLS?.rls_enabled ? 'Disable' : 'Enable'} Row Level Security`}
|
||||
description={`Are you sure you want to ${
|
||||
selectedTableToToggleRLS?.rls_enabled ? 'disable' : 'enable'
|
||||
} Row Level Security for the table "${selectedTableToToggleRLS?.name}"?`}
|
||||
buttonLabel="Confirm"
|
||||
buttonLoadingLabel="Saving"
|
||||
onSelectCancel={closeConfirmModal}
|
||||
onSelectConfirm={onToggleRLS}
|
||||
} Row Level Security (RLS) for the table “${selectedTableToToggleRLS?.name}”?`}
|
||||
confirmLabel={`${selectedTableToToggleRLS?.rls_enabled ? 'Disable' : 'Enable'} RLS`}
|
||||
confirmLabelLoading={`${selectedTableToToggleRLS?.rls_enabled ? 'Disabling' : 'Enabling'} RLS`}
|
||||
loading={isUpdatingTable}
|
||||
onCancel={closeConfirmModal}
|
||||
onConfirm={onToggleRLS}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
|
||||
+29
-52
@@ -1,4 +1,3 @@
|
||||
import { AlertCircle } from 'lucide-react'
|
||||
import { useState } from 'react'
|
||||
import { toast } from 'sonner'
|
||||
|
||||
@@ -10,8 +9,9 @@ import { useCustomDomainActivateMutation } from 'data/custom-domains/custom-doma
|
||||
import { useCustomDomainDeleteMutation } from 'data/custom-domains/custom-domains-delete-mutation'
|
||||
import type { CustomDomainResponse } from 'data/custom-domains/custom-domains-query'
|
||||
import { DOCS_URL } from 'lib/constants'
|
||||
import { AlertDescription_Shadcn_, AlertTitle_Shadcn_, Alert_Shadcn_, Button } from 'ui'
|
||||
import { Button } from 'ui'
|
||||
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
|
||||
import { Admonition } from 'ui-patterns/admonition'
|
||||
|
||||
export type CustomDomainActivateProps = {
|
||||
projectRef?: string
|
||||
@@ -59,36 +59,33 @@ const CustomDomainActivate = ({ projectRef, customDomain }: CustomDomainActivate
|
||||
<div className="flex flex-col items-start">
|
||||
<Panel.Content>
|
||||
<div className="flex flex-col gap-2">
|
||||
<h4 className="text-foreground">
|
||||
Setup complete! Press activate to enable the custom domain{' '}
|
||||
<code className="text-sm">{customDomain.hostname}</code> for this project.
|
||||
</h4>
|
||||
<span className="text-sm text-foreground-light">
|
||||
<h4 className="text-foreground">Enable your custom domain</h4>
|
||||
<p className="text-sm text-foreground-light">
|
||||
Set up is almost complete. Press “Activate” below to enable{' '}
|
||||
<code className="text-code-inline">{customDomain.hostname}</code> for this project.
|
||||
</p>
|
||||
<p className="text-sm text-foreground-light">
|
||||
We recommend that you schedule a downtime window of 20 - 30 minutes for your
|
||||
application, as you will need to update any services that need to know about your
|
||||
custom domain (e.g client side code or OAuth providers)
|
||||
</span>
|
||||
custom domain (e.g client side code or OAuth providers).
|
||||
</p>
|
||||
</div>
|
||||
<div className="mt-4">
|
||||
<Alert_Shadcn_>
|
||||
<AlertCircle className="text-foreground-light" strokeWidth={1.5} />
|
||||
<AlertTitle_Shadcn_>
|
||||
Remember to retain your CNAME record for service continuity after activation
|
||||
</AlertTitle_Shadcn_>
|
||||
<AlertDescription_Shadcn_>
|
||||
<p className="col-span-12 text-sm lg:col-span-7 leading-6">
|
||||
Your custom domain CNAME record for{' '}
|
||||
<code className="text-code-inline">{customDomain.hostname}</code> should resolve
|
||||
to{' '}
|
||||
{endpoint ? (
|
||||
<code className="text-xs">{endpoint}</code>
|
||||
) : (
|
||||
"your project's API URL"
|
||||
)}
|
||||
. If you're using Cloudflare as your DNS provider, disable the proxy option.
|
||||
</p>
|
||||
</AlertDescription_Shadcn_>
|
||||
</Alert_Shadcn_>
|
||||
<Admonition
|
||||
type="note"
|
||||
title="Retain your CNAME record for service continuity after activation"
|
||||
>
|
||||
<p>
|
||||
Your custom domain CNAME record for{' '}
|
||||
<code className="text-code-inline">{customDomain.hostname}</code> should resolve to{' '}
|
||||
{endpoint ? (
|
||||
<code className="text-code-inline">{endpoint}</code>
|
||||
) : (
|
||||
"your project's API URL"
|
||||
)}
|
||||
. If you're using Cloudflare as your DNS provider, disable the proxy option.
|
||||
</p>
|
||||
</Admonition>
|
||||
</div>
|
||||
</Panel.Content>
|
||||
|
||||
@@ -107,22 +104,6 @@ const CustomDomainActivate = ({ projectRef, customDomain }: CustomDomainActivate
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
icon={
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
strokeWidth={1}
|
||||
stroke="currentColor"
|
||||
className="w-4 h-4"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M3.75 13.5l10.5-11.25L12 10.5h8.25L9.75 21.75 12 13.5H3.75z"
|
||||
/>
|
||||
</svg>
|
||||
}
|
||||
disabled={isDeleting}
|
||||
onClick={() => setIsActivateConfirmModalVisible(true)}
|
||||
className="self-end"
|
||||
@@ -138,20 +119,16 @@ const CustomDomainActivate = ({ projectRef, customDomain }: CustomDomainActivate
|
||||
size="small"
|
||||
loading={isCheckingRecord || isActivating}
|
||||
visible={isActivateConfirmModalVisible}
|
||||
title={
|
||||
<>
|
||||
Are you sure you want to activate the custom domain{' '}
|
||||
<code className="text-sm">{customDomain.hostname}</code> for the project?
|
||||
</>
|
||||
}
|
||||
title="Activate custom domain"
|
||||
confirmLabel="Activate"
|
||||
confirmLabelLoading="Activating"
|
||||
onCancel={() => setIsActivateConfirmModalVisible(false)}
|
||||
onConfirm={onActivateCustomDomain}
|
||||
>
|
||||
<p className="text-sm">
|
||||
This will activate the custom domain <code>{customDomain.hostname}</code>. Your project's
|
||||
Supabase domain will also remain active.
|
||||
Activating <code className="text-code-inline !break-normal">{customDomain.hostname}</code>{' '}
|
||||
will make it visible to users in place of your project’s Supabase domain. The Supabase
|
||||
domain will continue to work too.
|
||||
</p>
|
||||
</ConfirmationModal>
|
||||
</>
|
||||
|
||||
+29
-26
@@ -8,7 +8,7 @@ import type { CustomDomainResponse } from 'data/custom-domains/custom-domains-qu
|
||||
import { DOCS_URL } from 'lib/constants'
|
||||
import { Trash } from 'lucide-react'
|
||||
import { Button } from 'ui'
|
||||
import ConfirmModal from 'ui-patterns/Dialogs/ConfirmDialog'
|
||||
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
|
||||
|
||||
export type CustomDomainDeleteProps = {
|
||||
projectRef?: string
|
||||
@@ -17,14 +17,15 @@ export type CustomDomainDeleteProps = {
|
||||
|
||||
const CustomDomainDelete = ({ projectRef, customDomain }: CustomDomainDeleteProps) => {
|
||||
const [isDeleteConfirmModalVisible, setIsDeleteConfirmModalVisible] = useState(false)
|
||||
const { mutate: deleteCustomDomain } = useCustomDomainDeleteMutation({
|
||||
onSuccess: () => {
|
||||
toast.success(
|
||||
`Successfully deleted custom domain. It may take a few seconds before your custom domain is fully removed, hence you may need to refresh your browser.`
|
||||
)
|
||||
setIsDeleteConfirmModalVisible(false)
|
||||
},
|
||||
})
|
||||
const { mutate: deleteCustomDomain, isPending: isDeletingCustomDomain } =
|
||||
useCustomDomainDeleteMutation({
|
||||
onSuccess: () => {
|
||||
toast.success(
|
||||
`Successfully deleted custom domain. Refresh your browser to see the changes.`
|
||||
)
|
||||
setIsDeleteConfirmModalVisible(false)
|
||||
},
|
||||
})
|
||||
|
||||
const onDeleteCustomDomain = async () => {
|
||||
if (!projectRef) return console.error('Project ref is required')
|
||||
@@ -39,7 +40,9 @@ const CustomDomainDelete = ({ projectRef, customDomain }: CustomDomainDeleteProp
|
||||
<div className="flex items-center space-x-2">
|
||||
<code className="text-lg mx-0 flex items-center space-x-2">
|
||||
<div className="h-2 w-2 rounded-full bg-brand" />
|
||||
<span>{customDomain.hostname}</span>
|
||||
<span>
|
||||
<code className="text-code-inline">{customDomain.hostname}</code>
|
||||
</span>
|
||||
</code>
|
||||
</div>
|
||||
<p className="text-sm text-foreground-light">
|
||||
@@ -58,27 +61,27 @@ const CustomDomainDelete = ({ projectRef, customDomain }: CustomDomainDeleteProp
|
||||
icon={<Trash />}
|
||||
onClick={() => setIsDeleteConfirmModalVisible(true)}
|
||||
>
|
||||
Delete Custom Domain
|
||||
Delete custom domain
|
||||
</Button>
|
||||
</div>
|
||||
</Panel.Content>
|
||||
|
||||
<ConfirmModal
|
||||
danger
|
||||
<ConfirmationModal
|
||||
visible={isDeleteConfirmModalVisible}
|
||||
// @ts-ignore
|
||||
title={
|
||||
<div>
|
||||
Are you sure you want to delete the custom domain{' '}
|
||||
<code className="text-sm">{customDomain.hostname}</code> for the project?
|
||||
</div>
|
||||
}
|
||||
description="Your custom domain will be deactivated. You will need to re-verify your domain if you want to use it again."
|
||||
buttonLabel="Delete"
|
||||
buttonLoadingLabel="Deleting"
|
||||
onSelectCancel={() => setIsDeleteConfirmModalVisible(false)}
|
||||
onSelectConfirm={onDeleteCustomDomain}
|
||||
/>
|
||||
variant="destructive"
|
||||
title="Delete custom domain"
|
||||
confirmLabel="Delete"
|
||||
confirmLabelLoading="Deleting"
|
||||
loading={isDeletingCustomDomain}
|
||||
onCancel={() => setIsDeleteConfirmModalVisible(false)}
|
||||
onConfirm={onDeleteCustomDomain}
|
||||
>
|
||||
<p className="text-sm">
|
||||
Are you sure you want to delete the custom domain{' '}
|
||||
<code className="text-code-inline !break-normal">{customDomain.hostname}</code> for your
|
||||
project? You will need to re-verify this domain if you want to use it again.
|
||||
</p>
|
||||
</ConfirmationModal>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
+27
-43
@@ -1,9 +1,9 @@
|
||||
import { AlertCircle, HelpCircle, RefreshCw } from 'lucide-react'
|
||||
import Link from 'next/link'
|
||||
import { AlertCircle, RefreshCw } from 'lucide-react'
|
||||
import { toast } from 'sonner'
|
||||
|
||||
import { useParams } from 'common'
|
||||
import { DocsButton } from 'components/ui/DocsButton'
|
||||
import { InlineLink } from 'components/ui/InlineLink'
|
||||
import Panel from 'components/ui/Panel'
|
||||
import { useProjectSettingsV2Query } from 'data/config/project-settings-v2-query'
|
||||
import { useCustomDomainDeleteMutation } from 'data/custom-domains/custom-domains-delete-mutation'
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
Button,
|
||||
WarningIcon,
|
||||
} from 'ui'
|
||||
import { Admonition } from 'ui-patterns/admonition'
|
||||
import DNSRecord from './DNSRecord'
|
||||
import { DNSTableHeaders } from './DNSTableHeaders'
|
||||
|
||||
@@ -83,7 +84,7 @@ const CustomDomainVerify = () => {
|
||||
<div>
|
||||
<h4 className="text-foreground mb-2">
|
||||
Configure TXT verification for your custom domain{' '}
|
||||
<code className="text-sm">{customDomain?.hostname}</code>
|
||||
<code className="text-code-inline">{customDomain?.hostname}</code>
|
||||
</h4>
|
||||
<p className="text-sm text-foreground-light">
|
||||
Set the following TXT record(s) in your DNS provider, then click verify to confirm your
|
||||
@@ -94,47 +95,30 @@ const CustomDomainVerify = () => {
|
||||
</p>
|
||||
{!isValidating && (
|
||||
<div className="mt-4 mb-2">
|
||||
<Alert_Shadcn_ variant="default">
|
||||
{isNotVerifiedYet ? (
|
||||
<AlertCircle className="text-foreground-light" strokeWidth={1.5} />
|
||||
) : (
|
||||
<HelpCircle className="text-foreground-light" strokeWidth={1.5} />
|
||||
)}
|
||||
<AlertTitle_Shadcn_>
|
||||
{isNotVerifiedYet
|
||||
<Admonition
|
||||
type="note"
|
||||
title={
|
||||
isNotVerifiedYet
|
||||
? 'Unable to verify records from DNS provider yet.'
|
||||
: 'Please note that it may take up to 24 hours for the DNS records to propagate.'}
|
||||
</AlertTitle_Shadcn_>
|
||||
<AlertDescription_Shadcn_>
|
||||
<div>
|
||||
{isNotVerifiedYet && (
|
||||
<p>
|
||||
Please check again soon. Note that it may take up to 24 hours for changes in
|
||||
DNS records to propagate.
|
||||
</p>
|
||||
)}
|
||||
<p>
|
||||
You may also visit{' '}
|
||||
<Link
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
href={`https://whatsmydns.net/#TXT/${customDomain?.ssl.txt_name}`}
|
||||
className="text-brand"
|
||||
>
|
||||
here
|
||||
</Link>{' '}
|
||||
to check if your DNS has been propagated successfully before clicking verify.
|
||||
</p>
|
||||
{isNotVerifiedYet && (
|
||||
<p className="mt-1 text-foreground-lighter">
|
||||
Some registrars will require you to remove the domain name when creating DNS
|
||||
records. As an example, to create a record for `foo.app.example.com`, you
|
||||
would need to create an entry for `foo.app`.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</AlertDescription_Shadcn_>
|
||||
</Alert_Shadcn_>
|
||||
: 'Please note that it may take up to 24 hours for the DNS records to propagate.'
|
||||
}
|
||||
>
|
||||
<p>
|
||||
You may also visit{' '}
|
||||
<InlineLink href={`https://whatsmydns.net/#TXT/${customDomain?.ssl.txt_name}`}>
|
||||
here
|
||||
</InlineLink>{' '}
|
||||
to check if your DNS has been propagated successfully before clicking verify.
|
||||
</p>
|
||||
{isNotVerifiedYet && (
|
||||
<p className="mt-1">
|
||||
Some registrars will require you to remove the domain name when creating DNS
|
||||
records. As an example, to create a record for{' '}
|
||||
<code className="text-code-inline">foo.app.example.com</code>, you would need to
|
||||
create an entry for <code className="text-code-inline">foo.app</code>.
|
||||
</p>
|
||||
)}
|
||||
</Admonition>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
+10
-11
@@ -22,7 +22,7 @@ import {
|
||||
DropdownMenuTrigger,
|
||||
cn,
|
||||
} from 'ui'
|
||||
import ConfirmModal from 'ui-patterns/Dialogs/ConfirmDialog'
|
||||
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
|
||||
|
||||
const RestartServerButton = () => {
|
||||
const router = useRouter()
|
||||
@@ -169,22 +169,21 @@ const RestartServerButton = () => {
|
||||
</Button>
|
||||
)}
|
||||
|
||||
<ConfirmModal
|
||||
danger
|
||||
<ConfirmationModal
|
||||
visible={serviceToRestart !== undefined}
|
||||
variant="destructive"
|
||||
title={`Restart ${serviceToRestart}`}
|
||||
// @ts-ignore
|
||||
description={
|
||||
<>
|
||||
Are you sure you want to restart the{' '}
|
||||
<span className="text-foreground">{serviceToRestart}</span>? There will be a few minutes
|
||||
of downtime.
|
||||
Are you sure you want to restart your {serviceToRestart}? There will be a few minutes of
|
||||
downtime.
|
||||
</>
|
||||
}
|
||||
buttonLabel="Restart"
|
||||
buttonLoadingLabel="Restarting"
|
||||
onSelectCancel={() => setServiceToRestart(undefined)}
|
||||
onSelectConfirm={async () => {
|
||||
confirmLabel="Restart"
|
||||
confirmLabelLoading="Restarting"
|
||||
loading={isLoading}
|
||||
onCancel={() => setServiceToRestart(undefined)}
|
||||
onConfirm={async () => {
|
||||
if (serviceToRestart === 'project') {
|
||||
await requestProjectRestart()
|
||||
} else if (serviceToRestart === 'database') {
|
||||
|
||||
@@ -14,7 +14,7 @@ import { usePaginatedBucketsQuery } from 'data/storage/buckets-query'
|
||||
import { useDebouncedValue } from 'hooks/misc/useDebouncedValue'
|
||||
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
|
||||
import { GenericSkeletonLoader } from 'ui-patterns'
|
||||
import ConfirmModal from 'ui-patterns/Dialogs/ConfirmDialog'
|
||||
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
|
||||
import { PageContainer } from 'ui-patterns/PageContainer'
|
||||
import {
|
||||
PageSection,
|
||||
@@ -73,13 +73,17 @@ export const StoragePolicies = () => {
|
||||
onError: () => {},
|
||||
})
|
||||
const { mutateAsync: updateDatabasePolicy } = useDatabasePolicyUpdateMutation()
|
||||
const { mutate: deleteDatabasePolicy } = useDatabasePolicyDeleteMutation({
|
||||
onSuccess: async () => {
|
||||
await refetch()
|
||||
toast.success('Successfully deleted policy!')
|
||||
setSelectedPolicyToDelete(undefined)
|
||||
},
|
||||
})
|
||||
const { mutate: deleteDatabasePolicy, isPending: isDeletingPolicy } =
|
||||
useDatabasePolicyDeleteMutation({
|
||||
onSuccess: async () => {
|
||||
await refetch()
|
||||
toast.success('Successfully deleted policy!')
|
||||
setSelectedPolicyToDelete(undefined)
|
||||
},
|
||||
onError: (error: any) => {
|
||||
toast.error(`Failed to delete policy: ${error.message}`)
|
||||
},
|
||||
})
|
||||
|
||||
// Only use storage policy editor when creating new policies for buckets
|
||||
const showStoragePolicyEditor =
|
||||
@@ -318,15 +322,16 @@ export const StoragePolicies = () => {
|
||||
onSaveSuccess={onSavePolicySuccess}
|
||||
/>
|
||||
|
||||
<ConfirmModal
|
||||
danger
|
||||
<ConfirmationModal
|
||||
visible={!isEmpty(selectedPolicyToDelete)}
|
||||
title="Confirm to delete policy"
|
||||
description={`This is permanent! Are you sure you want to delete the policy "${selectedPolicyToDelete?.name}"`}
|
||||
buttonLabel="Delete"
|
||||
buttonLoadingLabel="Deleting"
|
||||
onSelectCancel={onCancelPolicyDelete}
|
||||
onSelectConfirm={onDeletePolicy}
|
||||
variant="destructive"
|
||||
title="Delete policy"
|
||||
description={`Are you sure you want to delete the policy “${selectedPolicyToDelete?.name}”? This action cannot be undone.`}
|
||||
confirmLabel="Delete"
|
||||
confirmLabelLoading="Deleting"
|
||||
loading={isDeletingPolicy}
|
||||
onCancel={onCancelPolicyDelete}
|
||||
onConfirm={onDeletePolicy}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
|
||||
@@ -43,7 +43,6 @@ import {
|
||||
TooltipTrigger,
|
||||
cn,
|
||||
} from 'ui'
|
||||
import ConfirmModal from 'ui-patterns/Dialogs/ConfirmDialog'
|
||||
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
|
||||
import { RoleImpersonationPopover } from '../RoleImpersonationSelector/RoleImpersonationPopover'
|
||||
import ViewEntityAutofixSecurityModal from './ViewEntityAutofixSecurityModal'
|
||||
@@ -78,7 +77,7 @@ export const GridHeaderActions = ({ table, isRefetching }: GridHeaderActionsProp
|
||||
const { realtimeAll: realtimeEnabled } = useIsFeatureEnabled(['realtime:all'])
|
||||
const { isSchemaLocked } = useIsProtectedSchema({ schema: table.schema })
|
||||
|
||||
const { mutate: updateTable } = useTableUpdateMutation({
|
||||
const { mutate: updateTable, isPending: isUpdatingTable } = useTableUpdateMutation({
|
||||
onError: (error) => {
|
||||
toast.error(`Failed to toggle RLS: ${error.message}`)
|
||||
},
|
||||
@@ -592,15 +591,18 @@ export const GridHeaderActions = ({ table, isRefetching }: GridHeaderActionsProp
|
||||
/>
|
||||
|
||||
{isTable && (
|
||||
<ConfirmModal
|
||||
danger={table.rls_enabled}
|
||||
<ConfirmationModal
|
||||
visible={rlsConfirmModalOpen}
|
||||
title="Enable Row Level Security"
|
||||
description="Are you sure you want to enable Row Level Security for this table?"
|
||||
buttonLabel="Enable RLS"
|
||||
buttonLoadingLabel="Updating"
|
||||
onSelectCancel={closeConfirmModal}
|
||||
onSelectConfirm={onToggleRLS}
|
||||
variant={table.rls_enabled ? 'destructive' : 'default'}
|
||||
title={`${table.rls_enabled ? 'Disable' : 'Enable'} Row Level Security`}
|
||||
description={`Are you sure you want to ${
|
||||
table.rls_enabled ? 'disable' : 'enable'
|
||||
} Row Level Security for this table?`}
|
||||
confirmLabel={`${table.rls_enabled ? 'Disable' : 'Enable'} RLS`}
|
||||
confirmLabelLoading={`${table.rls_enabled ? 'Disabling' : 'Enabling'} RLS`}
|
||||
loading={isUpdatingTable}
|
||||
onCancel={closeConfirmModal}
|
||||
onConfirm={onToggleRLS}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -108,11 +108,7 @@ const NavigationItem: React.FC<NavigationItemProps> = ({ item, onClick, ...props
|
||||
)}
|
||||
/>
|
||||
{item.title}
|
||||
{item.new && (
|
||||
<Badge variant="success" className="capitalize">
|
||||
New
|
||||
</Badge>
|
||||
)}
|
||||
{item.new && <Badge variant="success">New</Badge>}
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -200,12 +200,12 @@ test.describe.serial('RLS Policies', () => {
|
||||
|
||||
// A confirmation modal appears when toggling RLS from the policies page
|
||||
await expect(
|
||||
page.getByRole('heading', { name: 'Confirm to disable Row Level Security' }),
|
||||
page.getByRole('heading', { name: 'Disable Row Level Security' }),
|
||||
'RLS disable confirmation modal should appear'
|
||||
).toBeVisible({ timeout: 50000 })
|
||||
|
||||
// Confirm disabling RLS
|
||||
await page.getByRole('button', { name: 'Confirm' }).click()
|
||||
await page.getByRole('button', { name: 'Disable RLS' }).click()
|
||||
|
||||
// After confirming, the toggle button text should change to "Enable RLS"
|
||||
await expect(
|
||||
|
||||
@@ -278,10 +278,6 @@
|
||||
"import": "./src/DataInputs/Input.tsx",
|
||||
"types": "./src/DataInputs/Input.tsx"
|
||||
},
|
||||
"./Dialogs/ConfirmDialog": {
|
||||
"import": "./src/Dialogs/ConfirmDialog.tsx",
|
||||
"types": "./src/Dialogs/ConfirmDialog.tsx"
|
||||
},
|
||||
"./Dialogs/ConfirmationModal": {
|
||||
"import": "./src/Dialogs/ConfirmationModal.tsx",
|
||||
"types": "./src/Dialogs/ConfirmationModal.tsx"
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect, useState } from 'react'
|
||||
import { Button, Form, Modal } from 'ui'
|
||||
|
||||
// [Joshen] As of 280222, let's just use ConfirmationModal as the one and only confirmation modal (Deprecate this)
|
||||
|
||||
interface ConfirmModalProps {
|
||||
visible: boolean
|
||||
danger?: boolean
|
||||
title: string
|
||||
description: string
|
||||
size?: 'tiny' | 'small' | 'medium' | 'large' | 'xlarge' | 'xxlarge'
|
||||
buttonLabel: string
|
||||
buttonLoadingLabel?: string
|
||||
onSelectCancel: () => void
|
||||
onSelectConfirm: () => void
|
||||
}
|
||||
|
||||
/** @deprecated use ConfirmationModal instead */
|
||||
const ConfirmModal = ({
|
||||
visible = false,
|
||||
danger = false,
|
||||
title = '',
|
||||
description = '',
|
||||
size = 'small',
|
||||
buttonLabel = '',
|
||||
buttonLoadingLabel = '',
|
||||
onSelectCancel = () => {},
|
||||
onSelectConfirm = () => {},
|
||||
}: ConfirmModalProps) => {
|
||||
useEffect(() => {
|
||||
if (visible) {
|
||||
setLoading(false)
|
||||
}
|
||||
}, [visible])
|
||||
|
||||
const [loading, setLoading] = useState(false)
|
||||
|
||||
const onConfirm = () => {
|
||||
setLoading(true)
|
||||
onSelectConfirm()
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
header={title}
|
||||
visible={visible}
|
||||
title={title}
|
||||
description={description}
|
||||
size={size}
|
||||
hideFooter
|
||||
onCancel={onSelectCancel}
|
||||
>
|
||||
<Form
|
||||
initialValues={{}}
|
||||
validateOnBlur
|
||||
onSubmit={() => onConfirm()}
|
||||
validate={() => {
|
||||
return []
|
||||
}}
|
||||
>
|
||||
{() => {
|
||||
return (
|
||||
<>
|
||||
<Modal.Content>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button
|
||||
block
|
||||
htmlType="button"
|
||||
type="default"
|
||||
onClick={onSelectCancel}
|
||||
disabled={loading}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
htmlType="submit"
|
||||
block
|
||||
type={danger ? 'danger' : 'primary'}
|
||||
disabled={loading}
|
||||
loading={loading}
|
||||
>
|
||||
{buttonLoadingLabel && loading
|
||||
? buttonLoadingLabel
|
||||
: buttonLabel
|
||||
? buttonLabel
|
||||
: 'Confirm'}
|
||||
</Button>
|
||||
</div>
|
||||
</Modal.Content>
|
||||
</>
|
||||
)
|
||||
}}
|
||||
</Form>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
export default ConfirmModal
|
||||
@@ -141,7 +141,7 @@ export const ConfirmationModal = forwardRef<
|
||||
onClick={onSubmit}
|
||||
className="truncate"
|
||||
>
|
||||
{confirmLabel}
|
||||
{loading && confirmLabelLoading ? confirmLabelLoading : confirmLabel}
|
||||
</Button>
|
||||
</div>
|
||||
</DialogContent>
|
||||
|
||||
Reference in new issue
Block a user