diff --git a/apps/studio/components/interfaces/Settings/API/PostgrestConfig.tsx b/apps/studio/components/interfaces/Settings/API/PostgrestConfig.tsx index 34f4c0085f2..d5676bced49 100644 --- a/apps/studio/components/interfaces/Settings/API/PostgrestConfig.tsx +++ b/apps/studio/components/interfaces/Settings/API/PostgrestConfig.tsx @@ -11,12 +11,6 @@ import { z } from 'zod' import { useParams } from 'common' import { DocsButton } from 'components/ui/DocsButton' import { FormActions } from 'components/ui/Forms/FormActions' -import { - FormPanelContainer, - FormPanelContent, - FormPanelFooter, - FormPanelHeader, -} from 'components/ui/Forms/FormPanel' import { useProjectPostgrestConfigQuery } from 'data/config/project-postgrest-config-query' import { useProjectPostgrestConfigUpdateMutation } from 'data/config/project-postgrest-config-update-mutation' import { useDatabaseExtensionsQuery } from 'data/database-extensions/database-extensions-query' @@ -28,6 +22,10 @@ import { AlertTitle_Shadcn_, Alert_Shadcn_, Button, + Card, + CardContent, + CardFooter, + CardHeader, CollapsibleContent_Shadcn_, Collapsible_Shadcn_, FormControl_Shadcn_, @@ -39,7 +37,9 @@ import { Skeleton, Switch, WarningIcon, + cn, } from 'ui' +import { GenericSkeletonLoader } from 'ui-patterns' import { Admonition } from 'ui-patterns/admonition' import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout' import { @@ -83,7 +83,7 @@ export const PostgrestConfig = () => { const [showModal, setShowModal] = useState(false) - const { data: config, isError } = useProjectPostgrestConfigQuery({ projectRef }) + const { data: config, isError, isLoading } = useProjectPostgrestConfigQuery({ projectRef }) const { data: extensions } = useDatabaseExtensionsQuery({ projectRef: project?.ref, connectionString: project?.connectionString, @@ -101,10 +101,8 @@ export const PostgrestConfig = () => { const formId = 'project-postgres-config' const hiddenSchema = ['auth', 'pgbouncer', 'hooks', 'extensions'] - const { can: canUpdatePostgrestConfig } = useAsyncCheckProjectPermissions( - PermissionAction.UPDATE, - 'custom_config_postgrest' - ) + const { can: canUpdatePostgrestConfig, isSuccess: isPermissionsLoaded } = + useAsyncCheckProjectPermissions(PermissionAction.UPDATE, 'custom_config_postgrest') const isGraphqlExtensionEnabled = (extensions ?? []).find((ext) => ext.name === 'pg_graphql')?.installed_version !== null @@ -168,275 +166,274 @@ export const PostgrestConfig = () => { const isDataApiEnabledInForm = form.getValues('enableDataApi') return ( - <> - -
- - - Data API Settings -
- - -
-
- - {isError ? ( - - ) : ( - <> - ( - - - - { - field.onChange(value) - if (!value) { - form.setValue('enableDataApi', false) - form.setValue('dbSchema', []) - } else { - form.setValue('enableDataApi', true) - form.setValue('dbSchema', dbSchema) - } - }} - /> - - + + + Data API Settings +
+ + +
+
+ + + + {isLoading ? ( + + ) : isError ? ( + + ) : ( + <> + ( + + + + { + field.onChange(value) + if (!value) { + form.setValue('enableDataApi', false) + form.setValue('dbSchema', []) + } else { + form.setValue('enableDataApi', true) + form.setValue('dbSchema', dbSchema) + } + }} + /> + + - {!field.value && ( - <> - - - - - No schemas can be queried - - -

- With this setting disabled, you will not be able to query any - schemas via the Data API. -

-

- You will see errors from the Postgrest endpoint - /rest/v1/. -

-
-
- - )} -
- )} - /> - - - ( - - - {isLoadingSchemas ? ( -
- -
- ) : ( - - - - - {schema.length <= 0 ? ( - - no - - ) : ( - <> - {schema.map((x) => ( - - {x.name} - - ))} - - )} - - - - )} - - {!field.value.includes('public') && field.value.length > 0 && ( - -

- You will not be able to query tables and views in the{' '} - public schema via supabase-js or HTTP clients. -

- {isGraphqlExtensionEnabled && ( - <> -

- Tables in the public{' '} - schema are still exposed over our GraphQL endpoints. -

- - - )} - - } - /> - )} -
-
- )} - /> - - ( - - - - - - - - )} - /> - - ( - - - - - - - - )} - /> - - ( - - - - - field.onChange( - e.target.value === '' ? null : Number(e.target.value) - ) - } - value={field.value === null ? '' : field.value} - /> - - - - )} - /> -
-
- - )} - - + + + + + No schemas can be queried + + +

+ With this setting disabled, you will not be able to query any + schemas via the Data API. +

+

+ You will see errors from the Postgrest endpoint + /rest/v1/. +

+
+
+ + )} +
+ )} /> - -
-
-
-
+ + + ( + + + {isLoadingSchemas ? ( +
+ +
+ ) : ( + + + + + {schema.length <= 0 ? ( + + no + + ) : ( + <> + {schema.map((x) => ( + + {x.name} + + ))} + + )} + + + + )} + + {!field.value.includes('public') && field.value.length > 0 && ( + +

+ You will not be able to query tables and views in the{' '} + public schema via supabase-js + or HTTP clients. +

+ {isGraphqlExtensionEnabled && ( + <> +

+ Tables in the public{' '} + schema are still exposed over our GraphQL endpoints. +

+ + + )} + + } + /> + )} +
+
+ )} + /> + + ( + + + + + + + + )} + /> + + ( + + + + + + + + )} + /> + + ( + + + + + field.onChange( + e.target.value === '' ? null : Number(e.target.value) + ) + } + value={field.value === null ? '' : field.value} + /> + + + + )} + /> +
+
+ + )} + + + + + + setShowModal(false)} /> - + ) } diff --git a/apps/studio/components/interfaces/Settings/API/ServiceList.tsx b/apps/studio/components/interfaces/Settings/API/ServiceList.tsx index a12eb57794c..34145807e93 100644 --- a/apps/studio/components/interfaces/Settings/API/ServiceList.tsx +++ b/apps/studio/components/interfaces/Settings/API/ServiceList.tsx @@ -1,8 +1,8 @@ import { AlertCircle } from 'lucide-react' import { useParams } from 'common' +import { ScaffoldSection } from 'components/layouts/Scaffold' import DatabaseSelector from 'components/ui/DatabaseSelector' -import Panel from 'components/ui/Panel' import { GenericSkeletonLoader } from 'components/ui/ShimmeringLoader' import { useCustomDomainsQuery } from 'data/custom-domains/custom-domains-query' import { useLoadBalancersQuery } from 'data/read-replicas/load-balancers-query' @@ -10,16 +10,22 @@ import { useReadReplicasQuery } from 'data/read-replicas/replicas-query' import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject' import { PROJECT_STATUS } from 'lib/constants' import { useDatabaseSelectorStateSnapshot } from 'state/database-selector' -import { Badge, Input } from 'ui' +import { Alert_Shadcn_, AlertTitle_Shadcn_, Badge, Card, CardContent, CardHeader } from 'ui' +import { Input } from 'ui-patterns/DataInputs/Input' +import { FormLayout } from 'ui-patterns/form/Layout/FormLayout' import { PostgrestConfig } from './PostgrestConfig' -const ServiceList = () => { +export const ServiceList = () => { const { data: project, isLoading } = useSelectedProjectQuery() const { ref: projectRef } = useParams() const state = useDatabaseSelectorStateSnapshot() const { data: customDomainData } = useCustomDomainsQuery({ projectRef }) - const { data: databases, isError } = useReadReplicasQuery({ projectRef }) + const { + data: databases, + isError, + isLoading: isLoadingDatabases, + } = useReadReplicasQuery({ projectRef }) const { data: loadBalancers } = useLoadBalancersQuery({ projectRef }) // Get the API service @@ -36,77 +42,65 @@ const ServiceList = () => { : selectedDatabase?.restUrl return ( -
- {isLoading ? ( - - ) : project?.status !== PROJECT_STATUS.ACTIVE_HEALTHY ? ( -
+ + {!isLoading && project?.status !== PROJECT_STATUS.ACTIVE_HEALTHY ? ( + -

+ API settings are unavailable as the project is not active -

-
+ + ) : ( <> -
- -
Project URL
- 0 - ? [{ id: 'load-balancer', name: 'API Load Balancer' }] - : [] - } - /> -
- } - > - - {isError ? ( -
- -

Failed to retrieve project URL

-
- ) : ( - -

URL

- Custom domain active - - ) : ( - 'URL' - ) - } - readOnly - disabled - className="input-mono" - value={endpoint} - descriptionText={ - loadBalancerSelected - ? 'RESTful endpoint for querying and managing your databases through your load balancer' - : replicaSelected - ? 'RESTful endpoint for querying your read replica' - : 'RESTful endpoint for querying and managing your database' - } - layout="horizontal" - /> - )} -
- - + + + Project URL + 0 + ? [{ id: 'load-balancer', name: 'API Load Balancer' }] + : [] + } + /> + + + {isLoading || isLoadingDatabases ? ( + + ) : isError ? ( + + + Failed to retrieve project URL + + ) : ( + +

URL

+ Custom domain active + + ) : ( + 'URL' + ) + } + description={ + loadBalancerSelected + ? 'RESTful endpoint for querying and managing your databases through your load balancer' + : replicaSelected + ? 'RESTful endpoint for querying your read replica' + : 'RESTful endpoint for querying and managing your database' + } + > + +
+ )} +
+
-
- -
+ )} - + ) } - -export default ServiceList diff --git a/apps/studio/pages/project/[ref]/settings/api.tsx b/apps/studio/pages/project/[ref]/settings/api.tsx index 2b0ad1b5c19..2914082b6a7 100644 --- a/apps/studio/pages/project/[ref]/settings/api.tsx +++ b/apps/studio/pages/project/[ref]/settings/api.tsx @@ -1,27 +1,23 @@ -import ServiceList from 'components/interfaces/Settings/API/ServiceList' +import { ServiceList } from 'components/interfaces/Settings/API/ServiceList' import DefaultLayout from 'components/layouts/DefaultLayout' +import { PageLayout } from 'components/layouts/PageLayout/PageLayout' import SettingsLayout from 'components/layouts/ProjectSettingsLayout/SettingsLayout' -import { ScaffoldContainer, ScaffoldHeader, ScaffoldTitle } from 'components/layouts/Scaffold' +import { ScaffoldContainer } from 'components/layouts/Scaffold' import type { NextPageWithLayout } from 'types' const ApiSettings: NextPageWithLayout = () => { return ( - <> - - - API Settings - - - - - - + + + ) } ApiSettings.getLayout = (page) => ( - {page} + + {page} + ) export default ApiSettings