From 585477752aa35db85d41c0ef4ccd906e767ac503 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Thu, 31 Oct 2024 13:58:17 +0800 Subject: [PATCH] Update all documentation buttons (#30163) --- .../Auth/AuthProvidersForm/ProviderForm.tsx | 7 +- .../Auth/EmailTemplates/EmailTemplates.tsx | 15 +- .../PolicyEditorModalTitle.tsx | 18 +- .../interfaces/Auth/Policies/PolicyReview.tsx | 2 +- .../Auth/RedirectUrls/RedirectUrls.tsx | 18 +- .../interfaces/Billing/SpendCapModal.tsx | 21 +- .../BranchManagement/BranchManagement.tsx | 23 +- .../Database/Backups/PITR/PITRSelection.tsx | 3 - .../EnumeratedTypes/EnumeratedTypes.tsx | 48 +- .../Database/Extensions/Extensions.tsx | 34 +- .../Database/Hooks/HooksList/HooksList.tsx | 60 +-- .../Database/Wrappers/CreateWrapper.tsx | 380 ++++++++-------- .../Database/Wrappers/EditWrapper.tsx | 428 +++++++++--------- .../DiskManagementPanelForm.tsx | 20 +- .../interfaces/Docs/GeneratingTypes.tsx | 16 +- .../EdgeFunctionSecrets.tsx | 17 +- .../Functions/FunctionsEmptyState.tsx | 20 +- .../Functions/TerminalInstructions.tsx | 13 +- .../CronJobs/CronJobsDisabledState.tsx | 62 +-- .../Integrations/CronJobs/CronJobsListing.tsx | 16 +- .../LogDrainDestinationSheetForm.tsx | 23 +- .../OAuthApps/PublishAppSidePanel/index.tsx | 14 +- .../UpdateRolesPanel/UpdateRolesPanel.tsx | 13 +- .../UpdateRolesPanel/UpdateRolesPanelOld.tsx | 232 ---------- .../interfaces/Organization/Usage/Usage.tsx | 43 +- .../ProjectAPIDocs/Content/Entities.tsx | 18 +- .../ProjectAPIDocs/ResourceContent.tsx | 15 +- .../ProjectAPIDocs/SecondLevelNav.tsx | 13 +- .../IndexAdvisorDisabledState.tsx | 12 +- .../Inspector/ChooseChannelPopover/index.tsx | 15 +- .../Realtime/Inspector/MessagesTable.tsx | 11 +- .../Inspector/NoChannelEmptyState.tsx | 7 +- .../Settings/API/HardenAPIModal.tsx | 18 +- .../Settings/API/PostgrestConfig.tsx | 14 +- .../Settings/Database/BannedIPs.tsx | 12 +- .../ConnectionPooling/ConnectionPooling.tsx | 13 +- .../DatabaseConnectionString.tsx | 13 +- .../NetworkRestrictions.tsx | 18 +- .../Settings/Database/PoolingModesModal.tsx | 13 +- .../Settings/Database/SSLConfiguration.tsx | 15 +- .../CustomDomainActivate.tsx | 14 +- .../CustomDomainConfig/CustomDomainDelete.tsx | 14 +- .../CustomDomainConfig/CustomDomainVerify.tsx | 13 +- .../CustomDomainsConfigureHostname.tsx | 15 +- .../TransferProjectButton.tsx | 147 +++--- .../Infrastructure/InfrastructureActivity.tsx | 11 +- .../DeployNewReplicaPanel.tsx | 48 +- .../EnablePhysicalBackupsModal.tsx | 11 +- .../Vault/Keys/EncryptionKeysManagement.tsx | 58 +-- .../Vault/Secrets/SecretsManagement.tsx | 61 +-- .../ForeignKeySelector/ForeignKeySelector.tsx | 13 +- .../TableEditor/RLSDisableModal.tsx | 97 ++-- .../TableEditor/TableEditor.tsx | 75 ++- .../TableGridEditor/TableGridEditor.tsx | 13 +- .../EnableBranchingModal.tsx | 11 +- .../FunctionsLayout/FunctionsLayout.tsx | 13 +- .../layouts/SignInLayout/SignInLayout.tsx | 7 +- .../StoragePoliciesEditPolicyModal.tsx | 19 +- .../ui/AIAssistantPanel/AIAssistant.tsx | 13 +- apps/studio/components/ui/DocsButton.tsx | 18 + .../studio/components/ui/Forms/FormHeader.tsx | 13 +- .../components/ui/Logs/LogsExplorerHeader.tsx | 5 +- .../table-rows/table-row-delete-mutation.tsx | 23 +- .../pages/project/[ref]/auth/policies.tsx | 13 +- .../[ref]/database/column-privileges.tsx | 15 +- .../pages/project/[ref]/database/indexes.tsx | 15 +- .../project/[ref]/database/migrations.tsx | 15 +- .../pages/project/[ref]/database/types.tsx | 13 +- .../[ref]/integrations/wrappers/index.tsx | 15 +- .../project/[ref]/settings/log-drains.tsx | 14 +- 70 files changed, 916 insertions(+), 1649 deletions(-) delete mode 100644 apps/studio/components/interfaces/Organization/TeamSettings/UpdateRolesPanel/UpdateRolesPanelOld.tsx create mode 100644 apps/studio/components/ui/DocsButton.tsx diff --git a/apps/studio/components/interfaces/Auth/AuthProvidersForm/ProviderForm.tsx b/apps/studio/components/interfaces/Auth/AuthProvidersForm/ProviderForm.tsx index 57fe2333163..38e3a3632c0 100644 --- a/apps/studio/components/interfaces/Auth/AuthProvidersForm/ProviderForm.tsx +++ b/apps/studio/components/interfaces/Auth/AuthProvidersForm/ProviderForm.tsx @@ -7,6 +7,7 @@ import { toast } from 'sonner' import { useParams } from 'common' import { ButtonTooltip } from 'components/ui/ButtonTooltip' +import { DocsButton } from 'components/ui/DocsButton' import type { components } from 'data/api' import { useAuthConfigUpdateMutation } from 'data/auth/auth-config-update-mutation' import { useProjectSettingsV2Query } from 'data/config/project-settings-v2-query' @@ -286,11 +287,7 @@ const ProviderForm = ({ config, provider }: ProviderFormProps) => { )}
- +
+
{isError && ( diff --git a/apps/studio/components/interfaces/Auth/Policies/PolicyEditorModal/PolicyEditorModalTitle.tsx b/apps/studio/components/interfaces/Auth/Policies/PolicyEditorModal/PolicyEditorModalTitle.tsx index 05a5d4980ff..dc196a4e6e7 100644 --- a/apps/studio/components/interfaces/Auth/Policies/PolicyEditorModal/PolicyEditorModalTitle.tsx +++ b/apps/studio/components/interfaces/Auth/Policies/PolicyEditorModal/PolicyEditorModalTitle.tsx @@ -1,5 +1,7 @@ import { noop } from 'lodash' -import { ChevronLeft, ExternalLink, FlaskConical } from 'lucide-react' +import { ChevronLeft, FlaskConical } from 'lucide-react' + +import { DocsButton } from 'components/ui/DocsButton' import { Button } from 'ui' import { POLICY_MODAL_VIEWS } from '../Policies.constants' @@ -57,16 +59,10 @@ const PolicyEditorModalTitle = ({ Try Supabase Assistant )} - + ) diff --git a/apps/studio/components/interfaces/Auth/Policies/PolicyReview.tsx b/apps/studio/components/interfaces/Auth/Policies/PolicyReview.tsx index a9f8a75ee9c..69d3e94d0d3 100644 --- a/apps/studio/components/interfaces/Auth/Policies/PolicyReview.tsx +++ b/apps/studio/components/interfaces/Auth/Policies/PolicyReview.tsx @@ -26,7 +26,7 @@ const PolicyReview = ({ return ( <> -
+

diff --git a/apps/studio/components/interfaces/Auth/RedirectUrls/RedirectUrls.tsx b/apps/studio/components/interfaces/Auth/RedirectUrls/RedirectUrls.tsx index 23b74dc14f8..8202b5e94cb 100644 --- a/apps/studio/components/interfaces/Auth/RedirectUrls/RedirectUrls.tsx +++ b/apps/studio/components/interfaces/Auth/RedirectUrls/RedirectUrls.tsx @@ -1,9 +1,8 @@ -import { ExternalLink } from 'lucide-react' -import Link from 'next/link' import { useMemo, useState } from 'react' import { toast } from 'sonner' import { useParams } from 'common' +import { DocsButton } from 'components/ui/DocsButton' import { FormHeader } from 'components/ui/Forms/FormHeader' import { HorizontalShimmerWithIcon } from 'components/ui/Shimmers/Shimmers' import { useAuthConfigQuery } from 'data/auth/auth-config-query' @@ -71,22 +70,13 @@ export const RedirectUrls = () => { return (

-
+
-
- -
+
{isLoading && ( <> diff --git a/apps/studio/components/interfaces/Billing/SpendCapModal.tsx b/apps/studio/components/interfaces/Billing/SpendCapModal.tsx index 85b009f40bf..4515ee3c39e 100644 --- a/apps/studio/components/interfaces/Billing/SpendCapModal.tsx +++ b/apps/studio/components/interfaces/Billing/SpendCapModal.tsx @@ -1,16 +1,5 @@ -import { ExternalLink } from 'lucide-react' -import Link from 'next/link' -import { Button, Modal } from 'ui' -import { - Table, - TableBody, - TableCaption, - TableCell, - TableFooter, - TableHead, - TableHeader, - TableRow, -} from 'ui' +import { DocsButton } from 'components/ui/DocsButton' +import { Button, Modal, Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from 'ui' interface SpendCapModalProps { visible: boolean @@ -26,11 +15,7 @@ const SpendCapModal = ({ visible, onHide }: SpendCapModalProps) => { header={
Spend Cap - +
} showCloseButton={false} diff --git a/apps/studio/components/interfaces/BranchManagement/BranchManagement.tsx b/apps/studio/components/interfaces/BranchManagement/BranchManagement.tsx index 366d9e47ff2..74b5b491939 100644 --- a/apps/studio/components/interfaces/BranchManagement/BranchManagement.tsx +++ b/apps/studio/components/interfaces/BranchManagement/BranchManagement.tsx @@ -10,6 +10,7 @@ import { useParams } from 'common' import { ScaffoldContainer, ScaffoldSection } from 'components/layouts/Scaffold' import AlertError from 'components/ui/AlertError' import { ButtonTooltip } from 'components/ui/ButtonTooltip' +import { DocsButton } from 'components/ui/DocsButton' import NoPermission from 'components/ui/NoPermission' import { useBranchDeleteMutation } from 'data/branches/branch-delete-mutation' import { useBranchesDisableMutation } from 'data/branches/branches-disable-mutation' @@ -190,25 +191,21 @@ const BranchManagement = () => { All branches
-
+
- + {
{/* [Joshen] Ideally we have some links to a docs to explain why so */} - {/* */} - )} +
+ + {!isLocked && ( + + )} +
{isLocked && } diff --git a/apps/studio/components/interfaces/Database/Extensions/Extensions.tsx b/apps/studio/components/interfaces/Database/Extensions/Extensions.tsx index 4fac7a2a4cf..7435439eb3c 100644 --- a/apps/studio/components/interfaces/Database/Extensions/Extensions.tsx +++ b/apps/studio/components/interfaces/Database/Extensions/Extensions.tsx @@ -1,17 +1,17 @@ import { PermissionAction } from '@supabase/shared-types/out/constants' import { isNull, partition } from 'lodash' -import { AlertCircle, ExternalLink, Search } from 'lucide-react' -import Link from 'next/link' +import { AlertCircle, Search } from 'lucide-react' import { useEffect, useState } from 'react' import { useParams } from 'common' import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext' +import { DocsButton } from 'components/ui/DocsButton' import InformationBox from 'components/ui/InformationBox' import NoSearchResults from 'components/ui/NoSearchResults' import ShimmeringLoader from 'components/ui/ShimmeringLoader' import { useDatabaseExtensionsQuery } from 'data/database-extensions/database-extensions-query' import { useCheckPermissions, usePermissionsLoaded } from 'hooks/misc/useCheckPermissions' -import { Button, Input } from 'ui' +import { Input } from 'ui' import ExtensionCard from './ExtensionCard' import ExtensionCardSkeleton from './ExtensionCardSkeleton' import { HIDDEN_EXTENSIONS, SEARCH_TERMS } from './Extensions.constants' @@ -55,7 +55,7 @@ const Extensions = () => { return ( <>
-
+
{ className="w-64" icon={} /> - {isPermissionsLoaded && !canUpdateExtensions ? ( -
- } - title="You need additional permissions to update database extensions" - /> -
- ) : ( - - )} +
+ {isPermissionsLoaded && !canUpdateExtensions && ( + } + title="You need additional permissions to update database extensions" + /> + )} + {isLoading ? (
diff --git a/apps/studio/components/interfaces/Database/Hooks/HooksList/HooksList.tsx b/apps/studio/components/interfaces/Database/Hooks/HooksList/HooksList.tsx index 4bc6264d3ef..49430ddef1e 100644 --- a/apps/studio/components/interfaces/Database/Hooks/HooksList/HooksList.tsx +++ b/apps/studio/components/interfaces/Database/Hooks/HooksList/HooksList.tsx @@ -1,20 +1,20 @@ -import * as Tooltip from '@radix-ui/react-tooltip' import { PermissionAction } from '@supabase/shared-types/out/constants' import { includes, map as lodashMap, uniqBy } from 'lodash' -import Link from 'next/link' +import { BookOpen, Search } from 'lucide-react' import { useState } from 'react' import { Button, Input } from 'ui' import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext' import AlertError from 'components/ui/AlertError' +import { ButtonTooltip } from 'components/ui/ButtonTooltip' import NoSearchResults from 'components/ui/NoSearchResults' import { GenericSkeletonLoader } from 'components/ui/ShimmeringLoader' import { useDatabaseHooksQuery } from 'data/database-triggers/database-triggers-query' import { useCheckPermissions, usePermissionsLoaded } from 'hooks/misc/useCheckPermissions' import { noop } from 'lib/void' -import { ExternalLink, Search } from 'lucide-react' import HooksListEmpty from './HooksListEmpty' import SchemaTable from './SchemaTable' +import { DocsButton } from 'components/ui/DocsButton' export interface HooksListProps { createHook: () => void @@ -55,43 +55,23 @@ const HooksList = ({ createHook = noop, editHook = noop, deleteHook = noop }: Ho className="w-64" onChange={(e) => setFilterString(e.target.value)} /> -
- - - - - - {isPermissionsLoaded && !canCreateWebhooks && ( - - - -
- - You need additional permissions to create webhooks - -
-
-
- )} -
+
+ + createHook()} + disabled={!isPermissionsLoaded || !canCreateWebhooks} + tooltip={{ + content: { + side: 'bottom', + text: + isPermissionsLoaded && !canCreateWebhooks + ? 'You need additional permissions to create webhooks' + : undefined, + }, + }} + > + Create a new hook +
diff --git a/apps/studio/components/interfaces/Database/Wrappers/CreateWrapper.tsx b/apps/studio/components/interfaces/Database/Wrappers/CreateWrapper.tsx index 98056249503..9194d22335e 100644 --- a/apps/studio/components/interfaces/Database/Wrappers/CreateWrapper.tsx +++ b/apps/studio/components/interfaces/Database/Wrappers/CreateWrapper.tsx @@ -1,6 +1,7 @@ import { PermissionAction } from '@supabase/shared-types/out/constants' import { useQueryClient } from '@tanstack/react-query' import { isEmpty } from 'lodash' +import { ArrowLeft, Edit, Trash } from 'lucide-react' import Link from 'next/link' import { useRouter } from 'next/router' import { useState } from 'react' @@ -8,6 +9,7 @@ import { toast } from 'sonner' import { useParams } from 'common' import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext' +import { DocsButton } from 'components/ui/DocsButton' import { FormActions } from 'components/ui/Forms/FormActions' import { FormPanel } from 'components/ui/Forms/FormPanel' import { FormSection, FormSectionContent, FormSectionLabel } from 'components/ui/Forms/FormSection' @@ -16,7 +18,6 @@ import { useDatabaseExtensionsQuery } from 'data/database-extensions/database-ex import { invalidateSchemasQuery } from 'data/database/schemas-query' import { useFDWCreateMutation } from 'data/fdw/fdw-create-mutation' import { useCheckPermissions } from 'hooks/misc/useCheckPermissions' -import { ArrowLeft, Edit, ExternalLink, Trash } from 'lucide-react' import { AlertDescription_Shadcn_, AlertTitle_Shadcn_, @@ -25,6 +26,7 @@ import { Form, Input, WarningIcon, + cn, } from 'ui' import InputField from './InputField' import WrapperTableEditor from './WrapperTableEditor' @@ -133,200 +135,192 @@ const CreateWrapper = () => { } return ( - -
-
-
- -
- -

Back

-
- -
-

Create a {wrapperMeta.label} Wrapper

-
-
+ } + > + Wrapper Configuration}> + + 0 ? ( + <> + Your wrapper's server name will be{' '} + {values.wrapper_name}_server + + ) : ( + '' + ) + } + /> + + + {wrapperMeta.label} Configuration} + > + + {wrapperMeta.server.options + .filter((option) => !option.hidden) + .map((option) => ( + + ))} + + + +

Foreign Tables

+

+ You can query your data from these foreign tables after the wrapper is + created +

+ + } + > + + {newTables.length === 0 ? ( +
+ +
+ ) : ( +
+ {newTables.map((table, i) => ( +
+
+

+ {table.schema_name}.{table.table_name} +

+

+ Columns:{' '} + {table.columns.map((column: any) => column.name).join(', ')} +

+
+
+
+
+ ))} +
+ )} + {newTables.length > 0 && ( +
+ +
+ )} + {newTables.length === 0 && formErrors.tables && ( +

{formErrors.tables}

+ )} +
+
+ + ) + }} + + ) : ( + + + + Your extension version is outdated for this wrapper. + + +

+ The {wrapperMeta.label} wrapper requires a minimum extension version of{' '} + {wrapperMeta.minimumExtensionVersion}. You have version{' '} + {wrappersExtension?.installed_version} installed. Please{' '} + {databaseNeedsUpgrading && 'upgrade your database then '}update the extension by + disabling and enabling the wrappers extension to + create this wrapper. +

+

+ Warning: Before reinstalling the wrapper extension, you must first remove all existing + wrappers. Afterward, you can recreate the wrappers. +

+
+ + -
-
- - {hasRequiredVersion ? ( -
- {({ handleReset, values, initialValues }: any) => { - const hasChanges = JSON.stringify(values) !== JSON.stringify(initialValues) - return ( - - -
- } - > - Wrapper Configuration}> - - 0 ? ( - <> - Your wrapper's server name will be{' '} - {values.wrapper_name}_server - - ) : ( - '' - ) - } - /> - - - {wrapperMeta.label} Configuration} - > - - {wrapperMeta.server.options - .filter((option) => !option.hidden) - .map((option) => ( - - ))} - - - -

Foreign Tables

-

- You can query your data from these foreign tables after the wrapper is - created -

- - } - > - - {newTables.length === 0 ? ( -
- -
- ) : ( -
- {newTables.map((table, i) => ( -
-
-

- {table.schema_name}.{table.table_name} -

-

- Columns:{' '} - {table.columns.map((column: any) => column.name).join(', ')} -

-
-
-
-
- ))} -
- )} - {newTables.length > 0 && ( -
- -
- )} - {newTables.length === 0 && formErrors.tables && ( -

{formErrors.tables}

- )} -
-
- - ) - }} - - ) : ( - - - - Your extension version is outdated for this wrapper. - - -

- The {wrapperMeta.label} wrapper requires a minimum extension version of{' '} - {wrapperMeta.minimumExtensionVersion}. You have version{' '} - {wrappersExtension?.installed_version} installed. Please{' '} - {databaseNeedsUpgrading && 'upgrade your database then '}update the extension by - disabling and enabling the wrappers extension to - create this wrapper. -

-

- Warning: Before reinstalling the wrapper extension, you must first remove all - existing wrappers. Afterward, you can recreate the wrappers. -

-
- - - -
- )} -
+ + + )} { } return ( - -
-
-
- -
- -

Back

-
- -
-

- Edit {wrapperMeta.label} wrapper: {wrapper.name} -

-
- -
+ +
+
+ +
+ +

Back

+
+
+

+ Edit {wrapperMeta.label} wrapper: {wrapper.name} +

+ +
-
- {({ handleReset, values, initialValues, resetForm }: any) => { - // [Alaister] although this "technically" is breaking the rules of React hooks - // it won't error because the hooks are always rendered in the same order - // eslint-disable-next-line react-hooks/rules-of-hooks - const [loadingSecrets, setLoadingSecrets] = useState(false) + + {({ handleReset, values, initialValues, resetForm }: any) => { + // [Alaister] although this "technically" is breaking the rules of React hooks + // it won't error because the hooks are always rendered in the same order + // eslint-disable-next-line react-hooks/rules-of-hooks + const [loadingSecrets, setLoadingSecrets] = useState(false) - const initialTables = formatWrapperTables(wrapper?.tables ?? []) - const hasFormChanges = JSON.stringify(values) !== JSON.stringify(initialValues) - const hasTableChanges = JSON.stringify(initialTables) !== JSON.stringify(wrapperTables) - const hasChanges = hasFormChanges || hasTableChanges + const initialTables = formatWrapperTables(wrapper?.tables ?? []) + const hasFormChanges = JSON.stringify(values) !== JSON.stringify(initialValues) + const hasTableChanges = JSON.stringify(initialTables) !== JSON.stringify(wrapperTables) + const hasChanges = hasFormChanges || hasTableChanges - const encryptedOptions = wrapperMeta.server.options.filter((option) => option.encrypted) + const encryptedOptions = wrapperMeta.server.options.filter((option) => option.encrypted) - // [Alaister] although this "technically" is breaking the rules of React hooks - // it won't error because the hooks are always rendered in the same order - // eslint-disable-next-line react-hooks/rules-of-hooks - useEffect(() => { - const fetchEncryptedValues = async () => { - setLoadingSecrets(true) - // If the secrets haven't loaded, escape and run the effect again when they're loaded - if (isSecretsLoading) { - return - } - - const res = await Promise.all( - encryptedOptions.map(async (option) => { - const secret = secrets?.find( - (secret) => secret.name === `${wrapper.name}_${option.name}` - ) - if (secret !== undefined) { - const value = await getDecryptedValue({ - projectRef: project?.ref, - connectionString: project?.connectionString, - id: secret.id, - }) - return { [option.name]: value[0]?.decrypted_secret ?? '' } - } else { - return { [option.name]: '' } - } - }) - ) - const secretValues = res.reduce((a: any, b: any) => { - const [key] = Object.keys(b) - return { ...a, [key]: b[key] } - }, {}) - - resetForm({ - values: { ...values, ...secretValues }, - initialValues: { ...initialValues, ...secretValues }, - }) - setLoadingSecrets(false) + // [Alaister] although this "technically" is breaking the rules of React hooks + // it won't error because the hooks are always rendered in the same order + // eslint-disable-next-line react-hooks/rules-of-hooks + useEffect(() => { + const fetchEncryptedValues = async () => { + setLoadingSecrets(true) + // If the secrets haven't loaded, escape and run the effect again when they're loaded + if (isSecretsLoading) { + return } - if (encryptedOptions.length > 0) fetchEncryptedValues() - }, [isSecretsLoading]) + const res = await Promise.all( + encryptedOptions.map(async (option) => { + const secret = secrets?.find( + (secret) => secret.name === `${wrapper.name}_${option.name}` + ) + if (secret !== undefined) { + const value = await getDecryptedValue({ + projectRef: project?.ref, + connectionString: project?.connectionString, + id: secret.id, + }) + return { [option.name]: value[0]?.decrypted_secret ?? '' } + } else { + return { [option.name]: '' } + } + }) + ) + const secretValues = res.reduce((a: any, b: any) => { + const [key] = Object.keys(b) + return { ...a, [key]: b[key] } + }, {}) - return ( - - { - handleReset() - setWrapperTables(initialTables) - }} - disabled={!canUpdateWrapper} - helper={ - !canUpdateWrapper - ? 'You need additional permissions to edit a foreign data wrapper' - : undefined - } - /> -
+ resetForm({ + values: { ...values, ...secretValues }, + initialValues: { ...initialValues, ...secretValues }, + }) + setLoadingSecrets(false) + } + + if (encryptedOptions.length > 0) fetchEncryptedValues() + }, [isSecretsLoading]) + + return ( + + { + handleReset() + setWrapperTables(initialTables) + }} + disabled={!canUpdateWrapper} + helper={ + !canUpdateWrapper + ? 'You need additional permissions to edit a foreign data wrapper' + : undefined + } + /> +
+ } + > + Wrapper Configuration}> + + + Your wrapper's server name will be updated to{' '} + {values.wrapper_name}_server + + ) : ( + <> + Your wrapper's server name is{' '} + {values.wrapper_name}_server + + ) + } + /> + + + {wrapperMeta.label} Configuration} + > + + {wrapperMeta.server.options + .filter((option) => !option.hidden) + .map((option) => ( + + ))} + + + +

Foreign Tables

+

+ You can query your data from these foreign tables after the wrapper is created +

+ } > - Wrapper Configuration}> - - - Your wrapper's server name will be updated to{' '} - {values.wrapper_name}_server - - ) : ( - <> - Your wrapper's server name is{' '} - {values.wrapper_name}_server - - ) - } - /> - - - {wrapperMeta.label} Configuration} - > - - {wrapperMeta.server.options - .filter((option) => !option.hidden) - .map((option) => ( - - ))} - - - -

Foreign Tables

-

- You can query your data from these foreign tables after the wrapper is - created -

- - } - > - - {wrapperTables.length === 0 ? ( -
- -
- ) : ( -
- {wrapperTables.map((table, i) => { - const target = table?.table ?? table.object + + {wrapperTables.length === 0 ? ( +
+ +
+ ) : ( +
+ {wrapperTables.map((table, i) => { + const target = table?.table ?? table.object - return ( -
-
-

- {table.schema_name}.{table.table_name}{' '} -

-

- Target: {target} -

-

- Columns:{' '} - {table.columns.map((column: any) => column.name).join(', ')} -

-
-
-
+ return ( +
+
+

+ {table.schema_name}.{table.table_name}{' '} +

+

Target: {target}

+

+ Columns:{' '} + {table.columns.map((column: any) => column.name).join(', ')} +

- ) - })} -
- )} - {wrapperTables.length > 0 && ( -
- -
- )} - {wrapperTables.length === 0 && formErrors.tables && ( -

{formErrors.tables}

- )} - - - - ) - }} - -
+
+
+
+ ) + })} +
+ )} + {wrapperTables.length > 0 && ( +
+ +
+ )} + {wrapperTables.length === 0 && formErrors.tables && ( +

{formErrors.tables}

+ )} +
+
+ + ) + }} + } > -
- -
+ )} diff --git a/apps/studio/components/interfaces/Docs/GeneratingTypes.tsx b/apps/studio/components/interfaces/Docs/GeneratingTypes.tsx index 88c552c1f02..3854b9ac829 100644 --- a/apps/studio/components/interfaces/Docs/GeneratingTypes.tsx +++ b/apps/studio/components/interfaces/Docs/GeneratingTypes.tsx @@ -1,12 +1,14 @@ +import { Download } from 'lucide-react' import Link from 'next/link' import { useState } from 'react' import { toast } from 'sonner' -import { useProjectPostgrestConfigQuery } from 'data/config/project-postgrest-config-query' + import { useParams } from 'common' import CodeSnippet from 'components/interfaces/Docs/CodeSnippet' +import { DocsButton } from 'components/ui/DocsButton' +import { useProjectPostgrestConfigQuery } from 'data/config/project-postgrest-config-query' import { generateTypes } from 'data/projects/project-type-generation-query' import { Button } from 'ui' -import { ExternalLink, Download } from 'lucide-react' interface Props { selectedLang: 'bash' | 'js' @@ -41,15 +43,7 @@ export default function GeneratingTypes({ selectedLang }: Props) { <>

Generating types - +

diff --git a/apps/studio/components/interfaces/Functions/EdgeFunctionSecrets/EdgeFunctionSecrets.tsx b/apps/studio/components/interfaces/Functions/EdgeFunctionSecrets/EdgeFunctionSecrets.tsx index 903970244ed..8b7f43e33e8 100644 --- a/apps/studio/components/interfaces/Functions/EdgeFunctionSecrets/EdgeFunctionSecrets.tsx +++ b/apps/studio/components/interfaces/Functions/EdgeFunctionSecrets/EdgeFunctionSecrets.tsx @@ -1,22 +1,23 @@ import { PermissionAction } from '@supabase/shared-types/out/constants' import { useParams } from 'common' -import { ExternalLink, Search } from 'lucide-react' +import { Search } from 'lucide-react' import { useState } from 'react' import { toast } from 'sonner' import Table from 'components/to-be-cleaned/Table' import AlertError from 'components/ui/AlertError' import { ButtonTooltip } from 'components/ui/ButtonTooltip' +import { DocsButton } from 'components/ui/DocsButton' import NoPermission from 'components/ui/NoPermission' import { GenericSkeletonLoader } from 'components/ui/ShimmeringLoader' import { useSecretsDeleteMutation } from 'data/secrets/secrets-delete-mutation' import { ProjectSecret, useSecretsQuery } from 'data/secrets/secrets-query' import { useCheckPermissions } from 'hooks/misc/useCheckPermissions' -import { Badge, Button } from 'ui' +import { Badge } from 'ui' +import { Input } from 'ui-patterns/DataInputs/Input' import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal' import AddNewSecretModal from './AddNewSecretModal' import EdgeFunctionSecret from './EdgeFunctionSecret' -import { Input } from 'ui-patterns/DataInputs/Input' const EdgeFunctionSecrets = () => { const { ref: projectRef } = useParams() @@ -66,15 +67,7 @@ const EdgeFunctionSecrets = () => { icon={} />
- + setShowCreateSecret(true)} diff --git a/apps/studio/components/interfaces/Functions/FunctionsEmptyState.tsx b/apps/studio/components/interfaces/Functions/FunctionsEmptyState.tsx index 0e0a17e5463..4165486a8d8 100644 --- a/apps/studio/components/interfaces/Functions/FunctionsEmptyState.tsx +++ b/apps/studio/components/interfaces/Functions/FunctionsEmptyState.tsx @@ -1,6 +1,6 @@ -import { Code, ExternalLink } from 'lucide-react' -import Link from 'next/link' +import { ExternalLink } from 'lucide-react' +import { DocsButton } from 'components/ui/DocsButton' import { Button } from 'ui' import TerminalInstructions from './TerminalInstructions' @@ -17,24 +17,16 @@ const FunctionsEmptyState = () => { close to your users. They can be used for listening to webhooks or integrating your Supabase project with third-parties.

-
+
+ -
diff --git a/apps/studio/components/interfaces/Functions/TerminalInstructions.tsx b/apps/studio/components/interfaces/Functions/TerminalInstructions.tsx index 1c100782e7f..be42fca37be 100644 --- a/apps/studio/components/interfaces/Functions/TerminalInstructions.tsx +++ b/apps/studio/components/interfaces/Functions/TerminalInstructions.tsx @@ -1,9 +1,10 @@ -import { Code, ExternalLink, Maximize2, Minimize2, Terminal } from 'lucide-react' +import { ExternalLink, Maximize2, Minimize2, Terminal } from 'lucide-react' import { useRouter } from 'next/router' import { ComponentPropsWithoutRef, ElementRef, forwardRef, useState } from 'react' import { useParams } from 'common' import CommandRender from 'components/interfaces/Functions/CommandRender' +import { DocsButton } from 'components/ui/DocsButton' import { useAccessTokensQuery } from 'data/access-tokens/access-tokens-query' import { getAPIKeys, useProjectSettingsV2Query } from 'data/config/project-settings-v2-query' import { useCustomDomainsQuery } from 'data/custom-domains/custom-domains-query' @@ -136,20 +137,12 @@ const TerminalInstructions = forwardRef<

+ -
- - - - - {!canToggleExtensions && ( - - - -
- - You need additional permissions to enable pg_cron for this project - -
-
-
- )} -
- + setShowEnableExtensionModal(true)} + tooltip={{ + content: { + side: 'bottom', + text: !canToggleExtensions + ? 'You need additional permissions to enable pg_cron for this project' + : undefined, + }, + }} + > + Enable Cron Jobs + +
diff --git a/apps/studio/components/interfaces/Integrations/CronJobs/CronJobsListing.tsx b/apps/studio/components/interfaces/Integrations/CronJobs/CronJobsListing.tsx index ab5374ba74e..edfe0766c69 100644 --- a/apps/studio/components/interfaces/Integrations/CronJobs/CronJobsListing.tsx +++ b/apps/studio/components/interfaces/Integrations/CronJobs/CronJobsListing.tsx @@ -1,9 +1,6 @@ -import { ExternalLink } from 'lucide-react' -import Link from 'next/link' import { useEffect, useState } from 'react' import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext' -import ProductEmptyState from 'components/to-be-cleaned/ProductEmptyState' import AlertError from 'components/ui/AlertError' import { FormHeader } from 'components/ui/Forms/FormHeader' import { GenericSkeletonLoader } from 'components/ui/ShimmeringLoader' @@ -11,11 +8,12 @@ import { CronJob, useCronJobsQuery } from 'data/database-cron-jobs/database-cron import { useDatabaseExtensionsQuery } from 'data/database-extensions/database-extensions-query' import { Button, Sheet, SheetContent } from 'ui' +import EnableExtensionModal from 'components/interfaces/Database/Extensions/EnableExtensionModal' +import { DocsButton } from 'components/ui/DocsButton' import { CreateCronJobSheet } from './CreateCronJobSheet' import { CronJobCard } from './CronJobCard' import { CronJobsDisabledState } from './CronJobsDisabledState' import DeleteCronJob from './DeleteCronJob' -import EnableExtensionModal from 'components/interfaces/Database/Extensions/EnableExtensionModal' export const CronJobsListing = () => { const { project } = useProjectContext() @@ -69,15 +67,7 @@ export const CronJobsListing = () => { description="Schedule and automate tasks like running queries or maintenance routines at specified intervals" actions={ <> - + +
diff --git a/apps/studio/components/interfaces/Organization/TeamSettings/UpdateRolesPanel/UpdateRolesPanel.tsx b/apps/studio/components/interfaces/Organization/TeamSettings/UpdateRolesPanel/UpdateRolesPanel.tsx index c0628f4e509..77da13e4978 100644 --- a/apps/studio/components/interfaces/Organization/TeamSettings/UpdateRolesPanel/UpdateRolesPanel.tsx +++ b/apps/studio/components/interfaces/Organization/TeamSettings/UpdateRolesPanel/UpdateRolesPanel.tsx @@ -1,8 +1,9 @@ import { isEqual } from 'lodash' -import { ExternalLink, X } from 'lucide-react' +import { X } from 'lucide-react' import { useEffect, useState } from 'react' import { useParams } from 'common' +import { DocsButton } from 'components/ui/DocsButton' import { useOrganizationRolesV2Query } from 'data/organization-members/organization-roles-query' import { OrganizationMember } from 'data/organizations/organization-members-query' import { usePermissionsQuery } from 'data/permissions/permissions-query' @@ -170,15 +171,7 @@ export const UpdateRolesPanel = ({ visible, member, onClose }: UpdateRolesPanelP

Manage access for {member.username}

- + diff --git a/apps/studio/components/interfaces/Organization/TeamSettings/UpdateRolesPanel/UpdateRolesPanelOld.tsx b/apps/studio/components/interfaces/Organization/TeamSettings/UpdateRolesPanel/UpdateRolesPanelOld.tsx deleted file mode 100644 index f30642ae907..00000000000 --- a/apps/studio/components/interfaces/Organization/TeamSettings/UpdateRolesPanel/UpdateRolesPanelOld.tsx +++ /dev/null @@ -1,232 +0,0 @@ -import { ExternalLink } from 'lucide-react' -import { useEffect, useState } from 'react' - -import { useParams } from 'common' -import { OrganizationMember } from 'data/organizations/organization-members-query' -import { useOrganizationRolesQuery } from 'data/organizations/organization-roles-query' -import { useProjectsQuery } from 'data/projects/projects-query' -import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization' -import { Role } from 'types' -import { - AlertDescription_Shadcn_, - AlertTitle_Shadcn_, - Alert_Shadcn_, - Button, - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuTrigger, - Sheet, - SheetContent, - SheetFooter, - SheetHeader, - WarningIcon, - cn, -} from 'ui' -import { MultiSelectV2 } from 'ui-patterns/MultiSelectDeprecated/MultiSelectV2' - -interface UpdateRolesPanelProps { - visible: boolean - member: OrganizationMember - onClose: () => void -} - -// [Joshen] This is just scaffolding the UI logic, will need to change quite a bit to fit the API structure -// [Joshen] Keeping this for now, but once everything is ready we can deprecate this file, no one is using - -export const UpdateRolesPanel = ({ visible, member, onClose }: UpdateRolesPanelProps) => { - const { slug } = useParams() - const organization = useSelectedOrganization() - const { data } = useOrganizationRolesQuery({ slug }) - const { data: projects } = useProjectsQuery() - - const [roleConfiguration, setRoleConfiguration] = useState< - { id: number; name: string; projects: string[] }[] - >([]) - - // [Joshen] If any roles are applied to ALL projects, or there are overlapping projects across roles - const hasMoreThanOneRoleForAllProjects = - roleConfiguration.some((r) => r.projects.length === 0) && roleConfiguration.length > 1 - const selectedProjectsAcrossRoles = roleConfiguration.reduce((obj, item) => { - return obj.concat(item.projects) - }, [] as string[]) - const hasOverlappingProjects = - new Set(selectedProjectsAcrossRoles).size !== selectedProjectsAcrossRoles.length - const canSaveRoles = - roleConfiguration.length > 0 && !hasOverlappingProjects && !hasMoreThanOneRoleForAllProjects - - const availableRoles = data?.roles ?? [] - const appliedRoles = member.role_ids - .map((id) => { - return availableRoles.find((y) => id === y.id) - }) - .filter((x) => x !== undefined) as Role[] - - const sortByObject: any = ['Owner', 'Administrator', 'Developer'].reduce((obj, item, index) => { - return { ...obj, [item]: index } - }, {}) - - const onAddRole = (role: Role) => { - const updatedConfiguration = roleConfiguration.concat({ ...role, projects: [] }) - setRoleConfiguration( - updatedConfiguration.sort((a, b) => sortByObject[a.name] - sortByObject[b.name]) - ) - } - - const onRemoveRole = (role: Role) => { - setRoleConfiguration(roleConfiguration.filter((r) => r.id !== role.id)) - } - - const onUpdateRole = (role: Role, projects: string[]) => { - const updatedConfiguration = roleConfiguration.map((r) => { - if (role.id === r.id) { - return { ...r, projects } - } else { - return r - } - }) - setRoleConfiguration(updatedConfiguration) - } - - useEffect(() => { - // [Joshen] This is an assumption for the structure - if (visible) { - setRoleConfiguration( - appliedRoles.map((role) => { - return { ...role, projects: [] } - }) - ) - } - }, [visible]) - - return ( - onClose()}> - -
- -

Manage access for team member

- -
- -
-

- Manage access for {member.username} to each project under {organization?.name} -

- - {(hasOverlappingProjects || hasMoreThanOneRoleForAllProjects) && ( - - - - Members cannot be assigned multiple roles on a project - - - Ensure that the member has at most one role assigned for each project - - - )} - - {roleConfiguration.length === 0 && ( - - - - Team members need to be assigned at least one role - - - You may not remove all roles from a team member - - - )} - - {roleConfiguration.map((role) => { - const selectedProjects = - roleConfiguration.find((r) => r.id === role.id)?.projects ?? [] - - return ( -
-
-

{role.name}

-
- {role.projects.length > 0 && ( - - )} - -
-
- { - return { - id: project.id, - name: project.name, - value: project.ref, - disabled: false, - } - })} - value={selectedProjects} - placeholder="All projects" - searchPlaceholder="Search for a project" - onChange={(projects) => onUpdateRole(role, projects)} - /> -
- ) - })} - - - - - - {availableRoles - .sort((a, b) => sortByObject[a.name] - sortByObject[b.name]) - .map((role) => { - const alreadyAssigned = roleConfiguration.map((x) => x.id).includes(role.id) - return ( - onAddRole(role)} - > - {role.name} - {alreadyAssigned && ( - Already assigned - )} - - ) - })} - - -
- - - - - -
-
-
- ) -} diff --git a/apps/studio/components/interfaces/Organization/Usage/Usage.tsx b/apps/studio/components/interfaces/Organization/Usage/Usage.tsx index f8b780933a0..9892366170e 100644 --- a/apps/studio/components/interfaces/Organization/Usage/Usage.tsx +++ b/apps/studio/components/interfaces/Organization/Usage/Usage.tsx @@ -1,15 +1,13 @@ import { PermissionAction } from '@supabase/shared-types/out/constants' import clsx from 'clsx' import dayjs from 'dayjs' -import { ExternalLink, Info } from 'lucide-react' -import Link from 'next/link' import { useEffect, useMemo, useState } from 'react' import { useParams } from 'common' import { ScaffoldContainer, ScaffoldContainerLegacy } from 'components/layouts/Scaffold' import DateRangePicker from 'components/to-be-cleaned/DateRangePicker' import AlertError from 'components/ui/AlertError' -import InformationBox from 'components/ui/InformationBox' +import { DocsButton } from 'components/ui/DocsButton' import NoPermission from 'components/ui/NoPermission' import ShimmeringLoader from 'components/ui/ShimmeringLoader' import { useProjectsQuery } from 'data/projects/projects-query' @@ -17,7 +15,8 @@ import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-que import { useCheckPermissions } from 'hooks/misc/useCheckPermissions' import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization' import { TIME_PERIODS_BILLING, TIME_PERIODS_REPORTS } from 'lib/constants/metrics' -import { Button, Listbox } from 'ui' +import { Listbox } from 'ui' +import { Admonition } from 'ui-patterns' import { Restriction } from '../BillingSettings/Restriction' import Activity from './Activity' import Bandwidth from './Bandwidth' @@ -183,35 +182,21 @@ const Usage = () => { {selectedProjectRef ? ( - -

- You are currently viewing usage for the " - {selectedProject?.name || selectedProjectRef}" project. Since your organization is + description={`You are currently viewing usage for the " + ${selectedProject?.name || selectedProjectRef}" project. Since your organization is using the new organization-based billing, the included quota is for your whole organization and not just this project. For billing purposes, we sum up usage from all your projects. To view your usage quota, set the project filter above back to - "All Projects". -

-
- -
-
- } - defaultVisibility - hideCollapse - icon={} - /> + "All Projects".`} + > + + ) : ( diff --git a/apps/studio/components/interfaces/ProjectAPIDocs/Content/Entities.tsx b/apps/studio/components/interfaces/ProjectAPIDocs/Content/Entities.tsx index cb39f18d376..d5456a73d51 100644 --- a/apps/studio/components/interfaces/ProjectAPIDocs/Content/Entities.tsx +++ b/apps/studio/components/interfaces/ProjectAPIDocs/Content/Entities.tsx @@ -1,15 +1,15 @@ -import Link from 'next/link' import { useState } from 'react' import { toast } from 'sonner' import { useParams } from 'common' +import { DocsButton } from 'components/ui/DocsButton' +import { useProjectPostgrestConfigQuery } from 'data/config/project-postgrest-config-query' import { generateTypes } from 'data/projects/project-type-generation-query' +import { Download } from 'lucide-react' import { Button } from 'ui' import ContentSnippet from '../ContentSnippet' import { DOCS_CONTENT } from '../ProjectAPIDocs.constants' import type { ContentProps } from './Content.types' -import { ExternalLink, Download } from 'lucide-react' -import { useProjectPostgrestConfigQuery } from 'data/config/project-postgrest-config-query' const Entities = ({ language }: ContentProps) => { const { ref } = useParams() @@ -41,16 +41,8 @@ const Entities = ({ language }: ContentProps) => {
-
- +
+ - )} + {snippet.docsUrl !== undefined && }
{snippet.description !== undefined && (
diff --git a/apps/studio/components/interfaces/ProjectAPIDocs/SecondLevelNav.tsx b/apps/studio/components/interfaces/ProjectAPIDocs/SecondLevelNav.tsx index 11f4781bc9a..dbf51229290 100644 --- a/apps/studio/components/interfaces/ProjectAPIDocs/SecondLevelNav.tsx +++ b/apps/studio/components/interfaces/ProjectAPIDocs/SecondLevelNav.tsx @@ -23,6 +23,7 @@ import { useIsAPIDocsSidePanelEnabled } from '../App/FeaturePreview/FeaturePrevi import { navigateToSection } from './Content/Content.utils' import { DOCS_RESOURCE_CONTENT } from './ProjectAPIDocs.constants' import { ChevronLeft, Code, ExternalLink } from 'lucide-react' +import { DocsButton } from 'components/ui/DocsButton' const SecondLevelNav = () => { const { ref } = useParams() @@ -155,17 +156,7 @@ const SecondLevelNav = () => {

Head over to our docs site for the full API documentation.

- +
diff --git a/apps/studio/components/interfaces/QueryPerformance/IndexAdvisorDisabledState.tsx b/apps/studio/components/interfaces/QueryPerformance/IndexAdvisorDisabledState.tsx index 2298148eeb2..deffaf61c07 100644 --- a/apps/studio/components/interfaces/QueryPerformance/IndexAdvisorDisabledState.tsx +++ b/apps/studio/components/interfaces/QueryPerformance/IndexAdvisorDisabledState.tsx @@ -1,9 +1,9 @@ -import { ExternalLink } from 'lucide-react' import Link from 'next/link' import { toast } from 'sonner' import { useParams } from 'common' import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext' +import { DocsButton } from 'components/ui/DocsButton' import { useDatabaseExtensionEnableMutation } from 'data/database-extensions/database-extension-enable-mutation' import { useDatabaseExtensionsQuery } from 'data/database-extensions/database-extensions-query' import { AlertDescription_Shadcn_, AlertTitle_Shadcn_, Alert_Shadcn_, Button } from 'ui' @@ -88,15 +88,7 @@ export const IndexAdvisorDisabledState = () => { Enable extensions )} - +
diff --git a/apps/studio/components/interfaces/Realtime/Inspector/ChooseChannelPopover/index.tsx b/apps/studio/components/interfaces/Realtime/Inspector/ChooseChannelPopover/index.tsx index 0ee820fcd3b..463f700c670 100644 --- a/apps/studio/components/interfaces/Realtime/Inspector/ChooseChannelPopover/index.tsx +++ b/apps/studio/components/interfaces/Realtime/Inspector/ChooseChannelPopover/index.tsx @@ -20,6 +20,7 @@ import { Switch, } from 'ui' import { RealtimeConfig } from '../useRealtimeMessages' +import { DocsButton } from 'components/ui/DocsButton' interface ChooseChannelPopoverProps { config: RealtimeConfig @@ -154,15 +155,11 @@ export const ChooseChannelPopover = ({ config, onChangeConfig }: ChooseChannelPo )} /> - + diff --git a/apps/studio/components/interfaces/Realtime/Inspector/MessagesTable.tsx b/apps/studio/components/interfaces/Realtime/Inspector/MessagesTable.tsx index 5d3083ea4de..b70c6d08831 100644 --- a/apps/studio/components/interfaces/Realtime/Inspector/MessagesTable.tsx +++ b/apps/studio/components/interfaces/Realtime/Inspector/MessagesTable.tsx @@ -12,6 +12,7 @@ import MessageSelection from './MessageSelection' import type { LogData } from './Messages.types' import NoChannelEmptyState from './NoChannelEmptyState' import { ColumnRenderer } from './RealtimeMessageColumnRenderer' +import { DocsButton } from 'components/ui/DocsButton' export const isErrorLog = (l: LogData) => { return l.message === 'SYSTEM' && l.metadata?.status === 'error' @@ -83,15 +84,7 @@ const NoResultAlert = ({

Not sure what to do?

Browse our documentation

- +
diff --git a/apps/studio/components/interfaces/Realtime/Inspector/NoChannelEmptyState.tsx b/apps/studio/components/interfaces/Realtime/Inspector/NoChannelEmptyState.tsx index 85c1243e61e..eff1b6944e8 100644 --- a/apps/studio/components/interfaces/Realtime/Inspector/NoChannelEmptyState.tsx +++ b/apps/studio/components/interfaces/Realtime/Inspector/NoChannelEmptyState.tsx @@ -1,3 +1,4 @@ +import { DocsButton } from 'components/ui/DocsButton' import { ExternalLink } from 'lucide-react' import { Button } from 'ui' @@ -18,11 +19,7 @@ const NoChannelEmptyState = () => {

Not sure what to do?

Browse our documentation

- +
) diff --git a/apps/studio/components/interfaces/Settings/API/HardenAPIModal.tsx b/apps/studio/components/interfaces/Settings/API/HardenAPIModal.tsx index cabc8158278..027786d1e67 100644 --- a/apps/studio/components/interfaces/Settings/API/HardenAPIModal.tsx +++ b/apps/studio/components/interfaces/Settings/API/HardenAPIModal.tsx @@ -1,7 +1,8 @@ -import { Check, ChevronDown, ExternalLink } from 'lucide-react' +import { Check, ChevronDown } from 'lucide-react' import { toast } from 'sonner' import { ButtonTooltip } from 'components/ui/ButtonTooltip' +import { DocsButton } from 'components/ui/DocsButton' import InformationBox from 'components/ui/InformationBox' import { useCreateAndExposeAPISchemaMutation } from 'data/api-settings/create-and-expose-api-schema-mutation' import { useProjectPostgrestConfigQuery } from 'data/config/project-postgrest-config-query' @@ -12,7 +13,6 @@ import { AlertDescription_Shadcn_, AlertTitle_Shadcn_, Alert_Shadcn_, - Button, CodeBlock, CollapsibleContent_Shadcn_, CollapsibleTrigger_Shadcn_, @@ -119,15 +119,11 @@ export const HardenAPIModal = ({ visible, onClose }: HardenAPIModalProps) => { public schema to{' '} prevent accidental exposure of data.

- + diff --git a/apps/studio/components/interfaces/Settings/API/PostgrestConfig.tsx b/apps/studio/components/interfaces/Settings/API/PostgrestConfig.tsx index 6eeb336b9ec..8baf3105949 100644 --- a/apps/studio/components/interfaces/Settings/API/PostgrestConfig.tsx +++ b/apps/studio/components/interfaces/Settings/API/PostgrestConfig.tsx @@ -1,7 +1,7 @@ import { zodResolver } from '@hookform/resolvers/zod' import { PermissionAction } from '@supabase/shared-types/out/constants' import { indexOf } from 'lodash' -import { ExternalLink, Lock } from 'lucide-react' +import { Lock } from 'lucide-react' import Link from 'next/link' import { useEffect, useState } from 'react' import { useForm } from 'react-hook-form' @@ -10,6 +10,7 @@ import { z } from 'zod' import { useParams } from 'common' import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext' +import { DocsButton } from 'components/ui/DocsButton' import { FormActions } from 'components/ui/Forms/FormActions' import { FormPanelContainer, @@ -44,7 +45,6 @@ import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout' import { MultiSelector, MultiSelectorContent, - MultiSelectorInput, MultiSelectorItem, MultiSelectorList, MultiSelectorTrigger, @@ -196,15 +196,7 @@ export const PostgrestConfig = () => { Data API Settings
- + diff --git a/apps/studio/components/interfaces/Settings/Database/BannedIPs.tsx b/apps/studio/components/interfaces/Settings/Database/BannedIPs.tsx index cc291744349..5a266fb906d 100644 --- a/apps/studio/components/interfaces/Settings/Database/BannedIPs.tsx +++ b/apps/studio/components/interfaces/Settings/Database/BannedIPs.tsx @@ -14,6 +14,7 @@ import { useCheckPermissions } from 'hooks/misc/useCheckPermissions' import { BASE_PATH } from 'lib/constants' import { Badge, Button } from 'ui' import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal' +import { DocsButton } from 'components/ui/DocsButton' const BannedIPs = () => { const { ref } = useParams() @@ -69,18 +70,13 @@ const BannedIPs = () => { return (
-
+
- +
{ipList && ipList.banned_ipv4_addresses.length > 0 ? ( diff --git a/apps/studio/components/interfaces/Settings/Database/ConnectionPooling/ConnectionPooling.tsx b/apps/studio/components/interfaces/Settings/Database/ConnectionPooling/ConnectionPooling.tsx index 18fff47468a..a5b6073f66d 100644 --- a/apps/studio/components/interfaces/Settings/Database/ConnectionPooling/ConnectionPooling.tsx +++ b/apps/studio/components/interfaces/Settings/Database/ConnectionPooling/ConnectionPooling.tsx @@ -2,7 +2,6 @@ import { zodResolver } from '@hookform/resolvers/zod' import { PermissionAction } from '@supabase/shared-types/out/constants' import { useParams } from 'common' import { capitalize } from 'lodash' -import { ExternalLink } from 'lucide-react' import { Fragment, useEffect } from 'react' import { SubmitHandler, useForm } from 'react-hook-form' import { toast } from 'sonner' @@ -10,6 +9,7 @@ import z from 'zod' import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext' import AlertError from 'components/ui/AlertError' +import { DocsButton } from 'components/ui/DocsButton' import { FormActions } from 'components/ui/Forms/FormActions' import Panel from 'components/ui/Panel' import ShimmeringLoader from 'components/ui/ShimmeringLoader' @@ -24,7 +24,6 @@ import { AlertTitle_Shadcn_, Alert_Shadcn_, Badge, - Button, FormControl_Shadcn_, FormDescription_Shadcn_, FormField_Shadcn_, @@ -162,15 +161,7 @@ export const ConnectionPooling = () => {

Supavisor
- +
} footer={ diff --git a/apps/studio/components/interfaces/Settings/Database/DatabaseSettings/DatabaseConnectionString.tsx b/apps/studio/components/interfaces/Settings/Database/DatabaseSettings/DatabaseConnectionString.tsx index ba3b44118ec..ebfa5449d43 100644 --- a/apps/studio/components/interfaces/Settings/Database/DatabaseSettings/DatabaseConnectionString.tsx +++ b/apps/studio/components/interfaces/Settings/Database/DatabaseSettings/DatabaseConnectionString.tsx @@ -1,4 +1,4 @@ -import { ChevronDown, ExternalLink } from 'lucide-react' +import { ChevronDown } from 'lucide-react' import { useEffect, useRef, useState } from 'react' import { useParams } from 'common' @@ -6,6 +6,7 @@ import { getAddons } from 'components/interfaces/Billing/Subscription/Subscripti import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext' import AlertError from 'components/ui/AlertError' import DatabaseSelector from 'components/ui/DatabaseSelector' +import { DocsButton } from 'components/ui/DocsButton' import Panel from 'components/ui/Panel' import ShimmeringLoader from 'components/ui/ShimmeringLoader' import { usePoolingConfigurationQuery } from 'data/database/pooling-configuration-query' @@ -17,7 +18,6 @@ import { pluckObjectFields } from 'lib/helpers' import { useDatabaseSelectorStateSnapshot } from 'state/database-selector' import { useDatabaseSettingsStateSnapshot } from 'state/database-settings' import { - Button, CollapsibleContent_Shadcn_, CollapsibleTrigger_Shadcn_, Collapsible_Shadcn_, @@ -181,14 +181,7 @@ export const DatabaseConnectionString = ({ appearance }: DatabaseConnectionStrin )} > - +
{ return ( <>
-
+
-
- +
+ {!canUpdateNetworkRestrictions ? ( {

Which pooling mode should I use?

- +
diff --git a/apps/studio/components/interfaces/Settings/Database/SSLConfiguration.tsx b/apps/studio/components/interfaces/Settings/Database/SSLConfiguration.tsx index 88a0ac96998..6c333b39ce7 100644 --- a/apps/studio/components/interfaces/Settings/Database/SSLConfiguration.tsx +++ b/apps/studio/components/interfaces/Settings/Database/SSLConfiguration.tsx @@ -1,5 +1,5 @@ import { PermissionAction } from '@supabase/shared-types/out/constants' -import { Download, ExternalLink, Loader2 } from 'lucide-react' +import { Download, Loader2 } from 'lucide-react' import Link from 'next/link' import { useEffect, useState } from 'react' import { toast } from 'sonner' @@ -7,6 +7,7 @@ import { toast } from 'sonner' import { useParams } from 'common' import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext' import { ButtonTooltip } from 'components/ui/ButtonTooltip' +import { DocsButton } from 'components/ui/DocsButton' import { FormHeader } from 'components/ui/Forms/FormHeader' import { FormPanel } from 'components/ui/Forms/FormPanel' import { FormSection, FormSectionContent, FormSectionLabel } from 'components/ui/Forms/FormSection' @@ -81,15 +82,9 @@ const SSLConfiguration = () => { return (
-
- -
- -
+
+ +
- +
+ +
+ ) } /> diff --git a/apps/studio/components/interfaces/Settings/General/TransferProjectPanel/TransferProjectButton.tsx b/apps/studio/components/interfaces/Settings/General/TransferProjectPanel/TransferProjectButton.tsx index 9777aca6547..2e2d539eaa6 100644 --- a/apps/studio/components/interfaces/Settings/General/TransferProjectPanel/TransferProjectButton.tsx +++ b/apps/studio/components/interfaces/Settings/General/TransferProjectPanel/TransferProjectButton.tsx @@ -1,16 +1,16 @@ import { PermissionAction } from '@supabase/shared-types/out/constants' -import Link from 'next/link' +import { Loader, Shield, Users, Wrench } from 'lucide-react' import { useEffect, useState } from 'react' import { toast } from 'sonner' import { ButtonTooltip } from 'components/ui/ButtonTooltip' +import { DocsButton } from 'components/ui/DocsButton' import { useOrganizationsQuery } from 'data/organizations/organizations-query' import { useProjectTransferMutation } from 'data/projects/project-transfer-mutation' import { useProjectTransferPreviewQuery } from 'data/projects/project-transfer-preview-query' import { useCheckPermissions } from 'hooks/misc/useCheckPermissions' import { useSelectedProject } from 'hooks/misc/useSelectedProject' import { useFlag } from 'hooks/ui/useFlag' -import { Loader, Shield, Users, Wrench } from 'lucide-react' import { Alert, Button, InfoIcon, Listbox, Loading, Modal } from 'ui' const TransferProjectButton = () => { @@ -113,20 +113,9 @@ const TransferProjectButton = () => {

To transfer projects, the owner must be a member of both the source and target - organizations. For further information see our{' '} - - Documentation - - . + organizations. Consider the following before transferring your project:

-

Transferring considerations:

-
  • @@ -167,8 +156,16 @@ const TransferProjectButton = () => {
+ + + + {organizations && (
@@ -204,67 +201,69 @@ const TransferProjectButton = () => { )} - - -
- {transferPreviewData && transferPreviewData.warnings.length > 0 && ( - -
- {transferPreviewData.warnings.map((warning) => ( -

{warning.message}

- ))} -
-
- )} - {transferPreviewData && transferPreviewData.errors.length > 0 && ( - -
- {transferPreviewData.errors.map((error) => ( -

{error.message}

- ))} -
- {transferPreviewData.members_exceeding_free_project_limit.length > 0 && ( -
-

- These members have reached their maximum limits for the number of active - Free plan projects within organizations where they are an administrator or - owner: -

-
    - {(transferPreviewData.members_exceeding_free_project_limit || []).map( - (member, idx: number) => ( -
  • - {member.name} (Limit: {member.limit} free projects) -
  • - ) - )} -
-

- These members will need to either delete, pause, or upgrade one or more of - their projects before you can downgrade this project. -

+ {selectedOrg !== undefined && ( + + +
+ {transferPreviewData && transferPreviewData.warnings.length > 0 && ( + +
+ {transferPreviewData.warnings.map((warning) => ( +

{warning.message}

+ ))}
- )} -
- )} - {transferPreviewError && !transferError && ( - -

{transferPreviewError.message}

-
- )} - {transferError && ( - -

{transferError.message}

-
- )} -
-
-
+ + )} + {transferPreviewData && transferPreviewData.errors.length > 0 && ( + +
+ {transferPreviewData.errors.map((error) => ( +

{error.message}

+ ))} +
+ {transferPreviewData.members_exceeding_free_project_limit.length > 0 && ( +
+

+ These members have reached their maximum limits for the number of active + Free plan projects within organizations where they are an administrator or + owner: +

+
    + {(transferPreviewData.members_exceeding_free_project_limit || []).map( + (member, idx: number) => ( +
  • + {member.name} (Limit: {member.limit} free projects) +
  • + ) + )} +
+

+ These members will need to either delete, pause, or upgrade one or more of + their projects before you can downgrade this project. +

+
+ )} +
+ )} + {transferPreviewError && !transferError && ( + +

{transferPreviewError.message}

+
+ )} + {transferError && ( + +

{transferError.message}

+
+ )} +
+ + + )} ) diff --git a/apps/studio/components/interfaces/Settings/Infrastructure/InfrastructureActivity.tsx b/apps/studio/components/interfaces/Settings/Infrastructure/InfrastructureActivity.tsx index e573716ba1c..a50cbc18c1e 100644 --- a/apps/studio/components/interfaces/Settings/Infrastructure/InfrastructureActivity.tsx +++ b/apps/studio/components/interfaces/Settings/Infrastructure/InfrastructureActivity.tsx @@ -21,6 +21,7 @@ import { } from 'components/layouts/Scaffold' import DateRangePicker from 'components/to-be-cleaned/DateRangePicker' import DatabaseSelector from 'components/ui/DatabaseSelector' +import { DocsButton } from 'components/ui/DocsButton' import Panel from 'components/ui/Panel' import ShimmeringLoader from 'components/ui/ShimmeringLoader' import { DataPoint } from 'data/analytics/constants' @@ -33,7 +34,6 @@ import { useSelectedProject } from 'hooks/misc/useSelectedProject' import { INSTANCE_MICRO_SPECS, INSTANCE_NANO_SPECS } from 'lib/constants' import { TIME_PERIODS_BILLING, TIME_PERIODS_REPORTS } from 'lib/constants/metrics' import { useDatabaseSelectorStateSnapshot } from 'state/database-selector' -import { Button } from 'ui' import { Admonition } from 'ui-patterns/admonition' import { INFRA_ACTIVITY_METRICS } from './Infrastructure.constants' @@ -397,11 +397,10 @@ const InfrastructureActivity = () => { add-ons are designed for sustained, high performance with specific IOPS and throughput limits without needing to burst." > - + ) : chartMeta[attribute.key].isLoading ? ( diff --git a/apps/studio/components/interfaces/Settings/Infrastructure/InfrastructureConfiguration/DeployNewReplicaPanel.tsx b/apps/studio/components/interfaces/Settings/Infrastructure/InfrastructureConfiguration/DeployNewReplicaPanel.tsx index 7a6bcf26f45..51e11bb8b8b 100644 --- a/apps/studio/components/interfaces/Settings/Infrastructure/InfrastructureConfiguration/DeployNewReplicaPanel.tsx +++ b/apps/studio/components/interfaces/Settings/Infrastructure/InfrastructureConfiguration/DeployNewReplicaPanel.tsx @@ -1,4 +1,4 @@ -import { ChevronDown, ExternalLink } from 'lucide-react' +import { ChevronDown } from 'lucide-react' import Link from 'next/link' import { useEffect, useState } from 'react' import { toast } from 'sonner' @@ -12,6 +12,7 @@ import { calculateIOPSPrice, calculateThroughputPrice, } from 'components/interfaces/DiskManagement/DiskManagement.utils' +import { DocsButton } from 'components/ui/DocsButton' import { useDiskAttributesQuery } from 'data/config/disk-attributes-query' import { useEnablePhysicalBackupsMutation } from 'data/database/enable-physical-backups-mutation' import { useOverdueInvoicesQuery } from 'data/invoices/invoices-overdue-query' @@ -51,7 +52,6 @@ import { cn, } from 'ui' import { AVAILABLE_REPLICA_REGIONS } from './InstanceConfiguration.constants' -import { Admonition } from 'ui-patterns' // [Joshen] FYI this is purely for AWS only, need to update to support Fly eventually @@ -242,7 +242,7 @@ const DeployNewReplicaPanel = ({
- ) : !isAWSProvider ? ( + ) : isAWSProvider ? ( @@ -253,17 +253,11 @@ const DeployNewReplicaPanel = ({ Projects provisioned by other cloud providers currently will not be able to use read replicas - + ) : currentPgVersion < 15 ? ( @@ -310,15 +304,10 @@ const DeployNewReplicaPanel = ({ {isFreePlan ? 'Upgrade to Pro' : 'Change compute size'} - +
@@ -356,15 +345,10 @@ const DeployNewReplicaPanel = ({ > Enable physical backups - + )} diff --git a/apps/studio/components/interfaces/Settings/Infrastructure/InfrastructureConfiguration/EnablePhysicalBackupsModal.tsx b/apps/studio/components/interfaces/Settings/Infrastructure/InfrastructureConfiguration/EnablePhysicalBackupsModal.tsx index fd896c97329..8d991bdf912 100644 --- a/apps/studio/components/interfaces/Settings/Infrastructure/InfrastructureConfiguration/EnablePhysicalBackupsModal.tsx +++ b/apps/studio/components/interfaces/Settings/Infrastructure/InfrastructureConfiguration/EnablePhysicalBackupsModal.tsx @@ -23,6 +23,7 @@ import { DialogTrigger, } from 'ui' import { AVAILABLE_REPLICA_REGIONS } from './InstanceConfiguration.constants' +import { DocsButton } from 'components/ui/DocsButton' interface EnablePhysicalBackupsModalProps { selectedRegion: string @@ -123,15 +124,7 @@ export const EnablePhysicalBackupsModal = ({ selectedRegion }: EnablePhysicalBac {!enabling && ( - +
-
- - - - - - {!canManageKeys && ( - - - -
- - You need additional permissions to add keys - -
-
-
- )} -
+
+ + setShowAddKeyModal(true)} + tooltip={{ + content: { + side: 'bottom', + text: !canManageKeys ? 'You need additional permissions to add keys' : undefined, + }, + }} + > + Add new key +
diff --git a/apps/studio/components/interfaces/Settings/Vault/Secrets/SecretsManagement.tsx b/apps/studio/components/interfaces/Settings/Vault/Secrets/SecretsManagement.tsx index e2d8e34e308..f4702527080 100644 --- a/apps/studio/components/interfaces/Settings/Vault/Secrets/SecretsManagement.tsx +++ b/apps/studio/components/interfaces/Settings/Vault/Secrets/SecretsManagement.tsx @@ -1,11 +1,12 @@ -import * as Tooltip from '@radix-ui/react-tooltip' import { PermissionAction } from '@supabase/shared-types/out/constants' import { sortBy } from 'lodash' -import Link from 'next/link' +import { Loader, Search, X } from 'lucide-react' import { Fragment, useEffect, useState } from 'react' import { useParams } from 'common' import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext' +import { ButtonTooltip } from 'components/ui/ButtonTooltip' +import { DocsButton } from 'components/ui/DocsButton' import { useVaultSecretsQuery } from 'data/vault/vault-secrets-query' import { useCheckPermissions } from 'hooks/misc/useCheckPermissions' import type { VaultSecret } from 'types' @@ -14,7 +15,6 @@ import AddNewSecretModal from './AddNewSecretModal' import DeleteSecretModal from './DeleteSecretModal' import EditSecretModal from './EditSecretModal' import SecretRow from './SecretRow' -import { Search, X, ExternalLink, Loader } from 'lucide-react' const SecretsManagement = () => { const { search } = useParams() @@ -110,44 +110,23 @@ const SecretsManagement = () => {
-
- - - - - - {!canManageSecrets && ( - - - -
- - You need additional permissions to add secrets - -
-
-
- )} -
+
+ + setShowAddSecretModal(true)} + tooltip={{ + content: { + side: 'bottom', + text: !canManageSecrets + ? 'You need additional permissions to add secrets' + : undefined, + }, + }} + > + Add new secret +
diff --git a/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/ForeignKeySelector/ForeignKeySelector.tsx b/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/ForeignKeySelector/ForeignKeySelector.tsx index a037edacd9b..7a82772b5fa 100644 --- a/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/ForeignKeySelector/ForeignKeySelector.tsx +++ b/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/ForeignKeySelector/ForeignKeySelector.tsx @@ -1,6 +1,6 @@ import type { PostgresTable } from '@supabase/postgres-meta' import { sortBy } from 'lodash' -import { ArrowRight, Database, ExternalLink, HelpCircle, Table, X } from 'lucide-react' +import { ArrowRight, Database, HelpCircle, Table, X } from 'lucide-react' import { Fragment, useEffect, useState } from 'react' import { AlertDescription_Shadcn_, @@ -12,6 +12,7 @@ import { } from 'ui' import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext' +import { DocsButton } from 'components/ui/DocsButton' import InformationBox from 'components/ui/InformationBox' import { FOREIGN_KEY_CASCADE_ACTION } from 'data/database/database-query-constants' import { useSchemasQuery } from 'data/database/schemas-query' @@ -498,15 +499,7 @@ export const ForeignKeySelector = ({ label="Action if referenced row is removed" // @ts-ignore labelOptional={ - + } descriptionText={ <> diff --git a/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/TableEditor/RLSDisableModal.tsx b/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/TableEditor/RLSDisableModal.tsx index 582a559788f..ca590e3d15e 100644 --- a/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/TableEditor/RLSDisableModal.tsx +++ b/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/TableEditor/RLSDisableModal.tsx @@ -1,62 +1,53 @@ -import { AlertOctagon, BookOpen, Lock, ShieldOff } from 'lucide-react' -import Link from 'next/link' -import { Alert, Button } from 'ui' +import { AlertOctagon, Lock, ShieldOff } from 'lucide-react' + +import { DocsButton } from 'components/ui/DocsButton' +import { Alert } from 'ui' export default function RLSDisableModalContent() { return ( -
-
-
- -

Anyone can edit or delete data in this table.

-
-
    -
  • - - All requests to this table will be accepted. -
  • +
    +
    + +

    Anyone can edit or delete data in this table.

    +
    +
      +
    • + + All requests to this table will be accepted. +
    • -
    • - - Auth policies will not be enforced. -
    • +
    • + + Auth policies will not be enforced. +
    • -
    • - -
      - Before you turn off Row Level Security, consider: -
        -
      • - Any personal information in this table will be publicly accessible. -
      • -
      • - Anyone will be able to modify, add or delete any row in this table. -
      • -
      -
      -
    • -
    -
    - -
    -

    - -

    -
    +
  • + +
    + Before you turn off Row Level Security, consider: +
      +
    • + Any personal information in this table will be publicly accessible. +
    • +
    • + Anyone will be able to modify, add or delete any row in this table. +
    • +
    +
    +
  • +
+ +
) } diff --git a/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/TableEditor/TableEditor.tsx b/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/TableEditor/TableEditor.tsx index 9eca50d7fc2..637f4497429 100644 --- a/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/TableEditor/TableEditor.tsx +++ b/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/TableEditor/TableEditor.tsx @@ -2,10 +2,11 @@ import type { PostgresTable } from '@supabase/postgres-meta' import { isEmpty, isUndefined, noop } from 'lodash' import { useEffect, useState } from 'react' import { toast } from 'sonner' -import { Alert, Badge, Button, Checkbox, Input, SidePanel } from 'ui' +import { Badge, Checkbox, Input, SidePanel } from 'ui' import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal' import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext' +import { DocsButton } from 'components/ui/DocsButton' import { useDatabasePublicationsQuery } from 'data/database-publications/database-publications-query' import { CONSTRAINT_TYPE, @@ -21,7 +22,8 @@ import { useIsFeatureEnabled } from 'hooks/misc/useIsFeatureEnabled' import { useQuerySchemaState } from 'hooks/misc/useSchemaQueryState' import { useUrlState } from 'hooks/ui/useUrlState' import { EXCLUDED_SCHEMAS_WITHOUT_EXTENSIONS } from 'lib/constants/schemas' -import { ExternalLink } from 'lucide-react' +import { useTableEditorStateSnapshot } from 'state/table-editor' +import { Admonition } from 'ui-patterns' import ActionBar from '../ActionBar' import type { ForeignKey } from '../ForeignKeySelector/ForeignKeySelector.types' import { formatForeignKeys } from '../ForeignKeySelector/ForeignKeySelector.utils' @@ -39,7 +41,6 @@ import { generateTableFieldFromPostgresTable, validateFields, } from './TableEditor.utils' -import { useTableEditorStateSnapshot } from 'state/table-editor' export interface TableEditorProps { table?: PostgresTable @@ -304,49 +305,41 @@ const TableEditor = ({ size="medium" /> {tableFields.isRLSEnabled ? ( - + You need to create an access policy before you can query data from this table. + Without a policy, querying this table will return an{' '} + empty array of results.{' '} + {isNewRecord ? 'You can create policies after saving this table.' : ''} + + } > -

- You need to create an access policy before you can query data from this table. Without - a policy, querying this table will return an{' '} - empty array of results.{' '} - {isNewRecord ? 'You can create policies after saving this table.' : ''} -

- -
+ + ) : ( - + {tableFields.name ? `The table ${tableFields.name}` : 'Your table'} will be publicly + writable and readable + + } > -

- {tableFields.name ? `The table ${tableFields.name}` : 'Your table'} will be publicly - writable and readable -

- -
+ + )} {realtimeEnabled && (
), diff --git a/apps/studio/components/layouts/AppLayout/EnableBranchingButton/EnableBranchingModal.tsx b/apps/studio/components/layouts/AppLayout/EnableBranchingButton/EnableBranchingModal.tsx index bba23f61333..a7d477bc14d 100644 --- a/apps/studio/components/layouts/AppLayout/EnableBranchingButton/EnableBranchingModal.tsx +++ b/apps/studio/components/layouts/AppLayout/EnableBranchingButton/EnableBranchingModal.tsx @@ -26,6 +26,7 @@ import BranchingPITRNotice from './BranchingPITRNotice' import BranchingPlanNotice from './BranchingPlanNotice' import BranchingPostgresVersionNotice from './BranchingPostgresVersionNotice' import GithubRepositorySelection from './GithubRepositorySelection' +import { DocsButton } from 'components/ui/DocsButton' const EnableBranchingModal = () => { const { ref } = useParams() @@ -152,15 +153,7 @@ const EnableBranchingModal = () => {

Manage environments in Supabase

- + {isLoading && ( diff --git a/apps/studio/components/layouts/FunctionsLayout/FunctionsLayout.tsx b/apps/studio/components/layouts/FunctionsLayout/FunctionsLayout.tsx index 17b66566919..1b3a03d852d 100644 --- a/apps/studio/components/layouts/FunctionsLayout/FunctionsLayout.tsx +++ b/apps/studio/components/layouts/FunctionsLayout/FunctionsLayout.tsx @@ -14,6 +14,7 @@ import { Code, ExternalLink } from 'lucide-react' import { Button } from 'ui' import FunctionsNav from '../../interfaces/Functions/FunctionsNav' import ProjectLayout from '../ProjectLayout/ProjectLayout' +import { DocsButton } from 'components/ui/DocsButton' interface FunctionsLayoutProps { title?: string @@ -121,16 +122,10 @@ const FunctionsLayout = ({ title, children }: PropsWithChildren )} - + href="https://supabase.com/docs/guides/functions" + />
diff --git a/apps/studio/components/layouts/SignInLayout/SignInLayout.tsx b/apps/studio/components/layouts/SignInLayout/SignInLayout.tsx index 8049b02c43e..1d208dc484d 100644 --- a/apps/studio/components/layouts/SignInLayout/SignInLayout.tsx +++ b/apps/studio/components/layouts/SignInLayout/SignInLayout.tsx @@ -11,6 +11,7 @@ import { BASE_PATH } from 'lib/constants' import { auth, buildPathWithParams, getAccessToken, getReturnToPath } from 'lib/gotrue' import { tweets } from 'shared-data' import { Button } from 'ui' +import { DocsButton } from 'components/ui/DocsButton' type SignInLayoutProps = { heading: string @@ -109,11 +110,7 @@ const SignInLayout = ({
- +
diff --git a/apps/studio/components/to-be-cleaned/Storage/StoragePolicies/StoragePoliciesEditPolicyModal.tsx b/apps/studio/components/to-be-cleaned/Storage/StoragePolicies/StoragePoliciesEditPolicyModal.tsx index acdc98c0c40..3259f2cd36c 100644 --- a/apps/studio/components/to-be-cleaned/Storage/StoragePolicies/StoragePoliciesEditPolicyModal.tsx +++ b/apps/studio/components/to-be-cleaned/Storage/StoragePolicies/StoragePoliciesEditPolicyModal.tsx @@ -5,7 +5,9 @@ import { toast } from 'sonner' import { POLICY_MODAL_VIEWS } from 'components/interfaces/Auth/Policies/Policies.constants' import PolicySelection from 'components/interfaces/Auth/Policies/PolicySelection' import PolicyTemplates from 'components/interfaces/Auth/Policies/PolicyTemplates' -import { Button, Modal } from 'ui' +import { DocsButton } from 'components/ui/DocsButton' +import { ChevronLeft } from 'lucide-react' +import { Modal } from 'ui' import { applyBucketIdToTemplateDefinition, createPayloadsForAddPolicy, @@ -14,7 +16,6 @@ import { import { STORAGE_POLICY_TEMPLATES } from './StoragePolicies.constants' import StoragePoliciesEditor from './StoragePoliciesEditor' import StoragePoliciesReview from './StoragePoliciesReview' -import { ChevronLeft, ExternalLink } from 'lucide-react' const newPolicyTemplate: any = { name: '', @@ -181,18 +182,9 @@ const StoragePoliciesEditPolicyModal = ({ ) } return ( -
+

{getTitle()}

- +
) } @@ -200,6 +192,7 @@ const StoragePoliciesEditPolicyModal = ({ return ( - {docsUrl !== undefined && ( - - )} + {docsUrl !== undefined && }
)} diff --git a/apps/studio/components/ui/DocsButton.tsx b/apps/studio/components/ui/DocsButton.tsx new file mode 100644 index 00000000000..f4cc0478950 --- /dev/null +++ b/apps/studio/components/ui/DocsButton.tsx @@ -0,0 +1,18 @@ +import { BookOpen } from 'lucide-react' +import { Button } from 'ui' + +interface DocsButtonProps { + href: string + abbrev?: boolean + className?: string +} + +export const DocsButton = ({ href, abbrev = true, className }: DocsButtonProps) => { + return ( + + ) +} diff --git a/apps/studio/components/ui/Forms/FormHeader.tsx b/apps/studio/components/ui/Forms/FormHeader.tsx index efc7565f57d..a29b9caa98a 100644 --- a/apps/studio/components/ui/Forms/FormHeader.tsx +++ b/apps/studio/components/ui/Forms/FormHeader.tsx @@ -1,10 +1,9 @@ -import { ExternalLink } from 'lucide-react' -import Link from 'next/link' import ReactMarkdown from 'react-markdown' import { Markdown } from 'components/interfaces/Markdown' import { ReactNode } from 'react' -import { Button, cn } from 'ui' +import { cn } from 'ui' +import { DocsButton } from '../DocsButton' const FormHeader = ({ title, @@ -30,13 +29,7 @@ const FormHeader = ({ {description && }
- {docsUrl !== undefined && ( - - )} + {docsUrl !== undefined && } {actions}
diff --git a/apps/studio/components/ui/Logs/LogsExplorerHeader.tsx b/apps/studio/components/ui/Logs/LogsExplorerHeader.tsx index 9cffa37b5ed..72f2ff55fd6 100644 --- a/apps/studio/components/ui/Logs/LogsExplorerHeader.tsx +++ b/apps/studio/components/ui/Logs/LogsExplorerHeader.tsx @@ -8,6 +8,7 @@ import { copyToClipboard } from 'lib/helpers' import { BookOpen, Check, Clipboard, ExternalLink, List, X } from 'lucide-react' import { logConstants } from 'shared-data' import { Button, SidePanel, Tabs } from 'ui' +import { DocsButton } from '../DocsButton' export interface LogsExplorerHeaderProps { subtitle?: string @@ -31,9 +32,7 @@ const LogsExplorerHeader = ({ subtitle }: LogsExplorerHeaderProps) => { {subtitle && {subtitle}}
- + */} - +
), }) @@ -130,15 +121,7 @@ export const useTableRowDeleteMutation = ({ each row before updating or deleting the row.

), diff --git a/apps/studio/pages/project/[ref]/auth/policies.tsx b/apps/studio/pages/project/[ref]/auth/policies.tsx index 1d13326de51..e603d3734b9 100644 --- a/apps/studio/pages/project/[ref]/auth/policies.tsx +++ b/apps/studio/pages/project/[ref]/auth/policies.tsx @@ -20,6 +20,7 @@ import { useUrlState } from 'hooks/ui/useUrlState' import { EXCLUDED_SCHEMAS } from 'lib/constants/schemas' import type { NextPageWithLayout } from 'types' import { Button, Input } from 'ui' +import { DocsButton } from 'components/ui/DocsButton' /** * Filter tables by table name and policy name @@ -132,17 +133,7 @@ const AuthPoliciesPage: NextPageWithLayout = () => { icon={} /> -
- -
+ diff --git a/apps/studio/pages/project/[ref]/database/column-privileges.tsx b/apps/studio/pages/project/[ref]/database/column-privileges.tsx index ed96d7ee14c..a8a881a5c96 100644 --- a/apps/studio/pages/project/[ref]/database/column-privileges.tsx +++ b/apps/studio/pages/project/[ref]/database/column-privileges.tsx @@ -1,5 +1,5 @@ import { useParams } from 'common' -import { AlertCircle, ExternalLink, XIcon } from 'lucide-react' +import { AlertCircle, XIcon } from 'lucide-react' import Link from 'next/link' import { useCallback, useMemo, useState } from 'react' import { toast } from 'sonner' @@ -18,6 +18,7 @@ import DatabaseLayout from 'components/layouts/DatabaseLayout/DatabaseLayout' import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext' import { ScaffoldContainer, ScaffoldSection } from 'components/layouts/Scaffold' import AlertError from 'components/ui/AlertError' +import { DocsButton } from 'components/ui/DocsButton' import { GenericSkeletonLoader } from 'components/ui/ShimmeringLoader' import { PgRole, useDatabaseRolesQuery } from 'data/database-roles/database-roles-query' import { useColumnPrivilegesQuery } from 'data/privileges/column-privileges-query' @@ -227,17 +228,7 @@ const PrivilegesPage: NextPageWithLayout = () => {

Grant or revoke privileges on a column based on user role.

- + {isEnabled ? ( diff --git a/apps/studio/pages/project/[ref]/database/indexes.tsx b/apps/studio/pages/project/[ref]/database/indexes.tsx index 52c47328953..f2da747faf2 100644 --- a/apps/studio/pages/project/[ref]/database/indexes.tsx +++ b/apps/studio/pages/project/[ref]/database/indexes.tsx @@ -8,6 +8,7 @@ import { ScaffoldSectionContent, ScaffoldSectionDetail, } from 'components/layouts/Scaffold' +import { DocsButton } from 'components/ui/DocsButton' import { FormHeader } from 'components/ui/Forms/FormHeader' import type { NextPageWithLayout } from 'types' import { Button } from 'ui' @@ -24,16 +25,10 @@ const IndexesPage: NextPageWithLayout = () => { /> - + +
diff --git a/apps/studio/pages/project/[ref]/database/types.tsx b/apps/studio/pages/project/[ref]/database/types.tsx index 766aa861f3d..b8830224da6 100644 --- a/apps/studio/pages/project/[ref]/database/types.tsx +++ b/apps/studio/pages/project/[ref]/database/types.tsx @@ -11,6 +11,7 @@ import { import { FormHeader } from 'components/ui/Forms/FormHeader' import type { NextPageWithLayout } from 'types' import { Button } from 'ui' +import { DocsButton } from 'components/ui/DocsButton' const DatabaseEnumeratedTypes: NextPageWithLayout = () => { return ( @@ -23,18 +24,6 @@ const DatabaseEnumeratedTypes: NextPageWithLayout = () => { description="Custom data types that you can use in your database tables or functions." /> - - -
diff --git a/apps/studio/pages/project/[ref]/integrations/wrappers/index.tsx b/apps/studio/pages/project/[ref]/integrations/wrappers/index.tsx index 6360dd51395..aae646b283d 100644 --- a/apps/studio/pages/project/[ref]/integrations/wrappers/index.tsx +++ b/apps/studio/pages/project/[ref]/integrations/wrappers/index.tsx @@ -19,6 +19,7 @@ import { useCheckPermissions, usePermissionsLoaded } from 'hooks/misc/useCheckPe import type { NextPageWithLayout } from 'types' import { Button } from 'ui' import { GenericSkeletonLoader } from 'ui-patterns/ShimmeringLoader' +import { DocsButton } from 'components/ui/DocsButton' const DatabaseWrappers: NextPageWithLayout = () => { const { project } = useProjectContext() @@ -53,16 +54,10 @@ const DatabaseWrappers: NextPageWithLayout = () => { /> - + {isWrappersEnabled && } diff --git a/apps/studio/pages/project/[ref]/settings/log-drains.tsx b/apps/studio/pages/project/[ref]/settings/log-drains.tsx index b887bae6caa..5ed27057f7f 100644 --- a/apps/studio/pages/project/[ref]/settings/log-drains.tsx +++ b/apps/studio/pages/project/[ref]/settings/log-drains.tsx @@ -1,6 +1,4 @@ import { PermissionAction } from '@supabase/shared-types/out/constants' -import { ExternalLink } from 'lucide-react' -import Link from 'next/link' import { useState } from 'react' import { toast } from 'sonner' @@ -15,6 +13,7 @@ import { ScaffoldHeader, ScaffoldTitle, } from 'components/layouts/Scaffold' +import { DocsButton } from 'components/ui/DocsButton' import { useCreateLogDrainMutation } from 'data/log-drains/create-log-drain-mutation' import { LogDrainData, useLogDrainsQuery } from 'data/log-drains/log-drains-query' import { useUpdateLogDrainMutation } from 'data/log-drains/update-log-drain-mutation' @@ -89,15 +88,8 @@ const LogDrainsSettings: NextPageWithLayout = () => {
- + + {!(logDrains?.length === 0) && (