diff --git a/apps/design-system/__registry__/index.tsx b/apps/design-system/__registry__/index.tsx index a1c90d5fc24..b668b042da0 100644 --- a/apps/design-system/__registry__/index.tsx +++ b/apps/design-system/__registry__/index.tsx @@ -38,17 +38,6 @@ export const Index: Record = { 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", diff --git a/apps/design-system/registry/fragments.ts b/apps/design-system/registry/fragments.ts index d35061a82ac..3a71fbcdb47 100644 --- a/apps/design-system/registry/fragments.ts +++ b/apps/design-system/registry/fragments.ts @@ -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', diff --git a/apps/studio/components/interfaces/Auth/Policies/Policies.tsx b/apps/studio/components/interfaces/Auth/Policies/Policies.tsx index c1a02eae473..6972d72c4f9 100644 --- a/apps/studio/components/interfaces/Auth/Policies/Policies.tsx +++ b/apps/studio/components/interfaces/Auth/Policies/Policies.tsx @@ -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({}) - 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} - - ) diff --git a/apps/studio/components/interfaces/Settings/General/CustomDomainConfig/CustomDomainActivate.tsx b/apps/studio/components/interfaces/Settings/General/CustomDomainConfig/CustomDomainActivate.tsx index d9b15525766..7ae49487102 100644 --- a/apps/studio/components/interfaces/Settings/General/CustomDomainConfig/CustomDomainActivate.tsx +++ b/apps/studio/components/interfaces/Settings/General/CustomDomainConfig/CustomDomainActivate.tsx @@ -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
-

- Setup complete! Press activate to enable the custom domain{' '} - {customDomain.hostname} for this project. -

- +

Enable your custom domain

+

+ Set up is almost complete. Press “Activate” below to enable{' '} + {customDomain.hostname} for this project. +

+

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) - + custom domain (e.g client side code or OAuth providers). +

- - - - Remember to retain your CNAME record for service continuity after activation - - -

- Your custom domain CNAME record for{' '} - {customDomain.hostname} should resolve - to{' '} - {endpoint ? ( - {endpoint} - ) : ( - "your project's API URL" - )} - . If you're using Cloudflare as your DNS provider, disable the proxy option. -

-
-
+ +

+ Your custom domain CNAME record for{' '} + {customDomain.hostname} should resolve to{' '} + {endpoint ? ( + {endpoint} + ) : ( + "your project's API URL" + )} + . If you're using Cloudflare as your DNS provider, disable the proxy option. +

+
@@ -107,22 +104,6 @@ const CustomDomainActivate = ({ projectRef, customDomain }: CustomDomainActivate Cancel
- - Are you sure you want to delete the custom domain{' '} - {customDomain.hostname} for the project? - - } - 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} + > +

+ Are you sure you want to delete the custom domain{' '} + {customDomain.hostname} for your + project? You will need to re-verify this domain if you want to use it again. +

+ ) } diff --git a/apps/studio/components/interfaces/Settings/General/CustomDomainConfig/CustomDomainVerify.tsx b/apps/studio/components/interfaces/Settings/General/CustomDomainConfig/CustomDomainVerify.tsx index 56496082cbc..943ac726035 100644 --- a/apps/studio/components/interfaces/Settings/General/CustomDomainConfig/CustomDomainVerify.tsx +++ b/apps/studio/components/interfaces/Settings/General/CustomDomainConfig/CustomDomainVerify.tsx @@ -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 = () => {

Configure TXT verification for your custom domain{' '} - {customDomain?.hostname} + {customDomain?.hostname}

Set the following TXT record(s) in your DNS provider, then click verify to confirm your @@ -94,47 +95,30 @@ const CustomDomainVerify = () => {

{!isValidating && (
- - {isNotVerifiedYet ? ( - - ) : ( - - )} - - {isNotVerifiedYet + - -
- {isNotVerifiedYet && ( -

- Please check again soon. Note that it may take up to 24 hours for changes in - DNS records to propagate. -

- )} -

- You may also visit{' '} - - here - {' '} - to check if your DNS has been propagated successfully before clicking verify. -

- {isNotVerifiedYet && ( -

- 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`. -

- )} -
-
-
+ : 'Please note that it may take up to 24 hours for the DNS records to propagate.' + } + > +

+ You may also visit{' '} + + here + {' '} + to check if your DNS has been propagated successfully before clicking verify. +

+ {isNotVerifiedYet && ( +

+ 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. +

+ )} +
)}
diff --git a/apps/studio/components/interfaces/Settings/General/Infrastructure/RestartServerButton.tsx b/apps/studio/components/interfaces/Settings/General/Infrastructure/RestartServerButton.tsx index c36605d0561..b2a9580b975 100644 --- a/apps/studio/components/interfaces/Settings/General/Infrastructure/RestartServerButton.tsx +++ b/apps/studio/components/interfaces/Settings/General/Infrastructure/RestartServerButton.tsx @@ -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 = () => { )} - - Are you sure you want to restart the{' '} - {serviceToRestart}? 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') { diff --git a/apps/studio/components/interfaces/Storage/StoragePolicies/StoragePolicies.tsx b/apps/studio/components/interfaces/Storage/StoragePolicies/StoragePolicies.tsx index a602a9e0051..09f21e6e093 100644 --- a/apps/studio/components/interfaces/Storage/StoragePolicies/StoragePolicies.tsx +++ b/apps/studio/components/interfaces/Storage/StoragePolicies/StoragePolicies.tsx @@ -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} /> - ) diff --git a/apps/studio/components/interfaces/TableGridEditor/GridHeaderActions.tsx b/apps/studio/components/interfaces/TableGridEditor/GridHeaderActions.tsx index 83781c793ac..cab44ab4f0f 100644 --- a/apps/studio/components/interfaces/TableGridEditor/GridHeaderActions.tsx +++ b/apps/studio/components/interfaces/TableGridEditor/GridHeaderActions.tsx @@ -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 && ( - )} diff --git a/apps/ui-library/components/side-navigation-item.tsx b/apps/ui-library/components/side-navigation-item.tsx index df1b7b0389c..74c8b636cd7 100644 --- a/apps/ui-library/components/side-navigation-item.tsx +++ b/apps/ui-library/components/side-navigation-item.tsx @@ -108,11 +108,7 @@ const NavigationItem: React.FC = ({ item, onClick, ...props )} /> {item.title} - {item.new && ( - - New - - )} + {item.new && New} ) } diff --git a/e2e/studio/features/rls-policies.spec.ts b/e2e/studio/features/rls-policies.spec.ts index 1b33676a6e5..98b55f0f096 100644 --- a/e2e/studio/features/rls-policies.spec.ts +++ b/e2e/studio/features/rls-policies.spec.ts @@ -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( diff --git a/packages/ui-patterns/package.json b/packages/ui-patterns/package.json index 915a6e99e92..e55f201de6d 100644 --- a/packages/ui-patterns/package.json +++ b/packages/ui-patterns/package.json @@ -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" diff --git a/packages/ui-patterns/src/Dialogs/ConfirmDialog.tsx b/packages/ui-patterns/src/Dialogs/ConfirmDialog.tsx deleted file mode 100644 index 24a6d10f8b3..00000000000 --- a/packages/ui-patterns/src/Dialogs/ConfirmDialog.tsx +++ /dev/null @@ -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 ( - -
onConfirm()} - validate={() => { - return [] - }} - > - {() => { - return ( - <> - -
- - -
-
- - ) - }} -
-
- ) -} - -export default ConfirmModal diff --git a/packages/ui-patterns/src/Dialogs/ConfirmationModal.tsx b/packages/ui-patterns/src/Dialogs/ConfirmationModal.tsx index a71b0a4915d..b4420c6af97 100644 --- a/packages/ui-patterns/src/Dialogs/ConfirmationModal.tsx +++ b/packages/ui-patterns/src/Dialogs/ConfirmationModal.tsx @@ -141,7 +141,7 @@ export const ConfirmationModal = forwardRef< onClick={onSubmit} className="truncate" > - {confirmLabel} + {loading && confirmLabelLoading ? confirmLabelLoading : confirmLabel}