diff --git a/.github/workflows/docs-sync.yml b/.github/workflows/docs-sync.yml index d6aaaf8ef8093..e4e78f0fc7226 100644 --- a/.github/workflows/docs-sync.yml +++ b/.github/workflows/docs-sync.yml @@ -30,6 +30,7 @@ jobs: with: sparse-checkout: | apps/docs + packages - uses: pnpm/action-setup@v4 name: Install pnpm diff --git a/apps/docs/components/AuthSmsProviderConfig/AuthSmsProviderConfig.tsx b/apps/docs/components/AuthSmsProviderConfig/AuthSmsProviderConfig.tsx index 2039a78854e61..357b37ffa7784 100644 --- a/apps/docs/components/AuthSmsProviderConfig/AuthSmsProviderConfig.tsx +++ b/apps/docs/components/AuthSmsProviderConfig/AuthSmsProviderConfig.tsx @@ -8,6 +8,7 @@ import MessageBird from './MessageBirdConfig.mdx' import Twilio from './TwilioConfig.mdx' import Vonage from './VonageConfig.mdx' import TextLocal from './TextLocalConfig.mdx' +import { safeHistoryReplaceState } from '~/lib/historyUtils' const reducer = (_, action: (typeof PhoneLoginsItems)[number] | undefined) => { const url = new URL(document.location.href) @@ -16,7 +17,7 @@ const reducer = (_, action: (typeof PhoneLoginsItems)[number] | undefined) => { } else { url.searchParams.delete('showSmsProvider') } - window.history.replaceState(null, '', url) + safeHistoryReplaceState(url.toString()) return action } diff --git a/apps/docs/components/Navigation/Navigation.commands.tsx b/apps/docs/components/Navigation/Navigation.commands.tsx index 77080e7fd300f..2ab00c9e48788 100644 --- a/apps/docs/components/Navigation/Navigation.commands.tsx +++ b/apps/docs/components/Navigation/Navigation.commands.tsx @@ -1,5 +1,6 @@ import { ArrowRight } from 'lucide-react' +import { isFeatureEnabled } from 'common' import type { ICommand } from 'ui-patterns/CommandMenu' import { useRegisterCommands } from 'ui-patterns/CommandMenu' @@ -89,6 +90,7 @@ const navCommands = [ value: 'Reference, API, SDK: Go to Dart reference (Flutter)', route: '/reference/dart/introduction', icon: () => , + enabled: isFeatureEnabled('sdk:dart'), }, { id: 'nav-ref-python', @@ -96,6 +98,7 @@ const navCommands = [ value: 'Reference, API, SDK: Go to Python reference', route: '/reference/python/introduction', icon: () => , + enabled: isFeatureEnabled('sdk:python'), }, { id: 'nav-ref-csharp', @@ -103,6 +106,7 @@ const navCommands = [ value: 'Reference, API, SDK: Go to C# reference', route: '/reference/csharp/introduction', icon: () => , + enabled: isFeatureEnabled('sdk:csharp'), }, { id: 'nav-ref-swift', @@ -110,6 +114,7 @@ const navCommands = [ value: 'Reference, API, SDK: Go to Swift reference', route: '/reference/swift/introduction', icon: () => , + enabled: isFeatureEnabled('sdk:swift'), }, { id: 'nav-ref-kotlin', @@ -117,6 +122,7 @@ const navCommands = [ value: 'Reference, API, SDK: Go to Kotlin reference', route: '/reference/kotlin/introduction', icon: () => , + enabled: isFeatureEnabled('sdk:kotlin'), }, { id: 'nav-ref-cli', @@ -143,11 +149,14 @@ const navCommands = [ name: 'Go to Integrations', route: 'https://supabase.com/partners/integrations', icon: () => , + enabled: isFeatureEnabled('integrations:partners'), }, -] satisfies ICommand[] +] satisfies Array + +const filteredNavCommands = navCommands.filter((command) => command.enabled !== false) const useDocsNavCommands = () => { - useRegisterCommands('Go to', navCommands) + useRegisterCommands('Go to', filteredNavCommands) } export { useDocsNavCommands } diff --git a/apps/docs/components/Navigation/Navigation.types.ts b/apps/docs/components/Navigation/Navigation.types.ts index 816a5b363b005..b786a96586e48 100644 --- a/apps/docs/components/Navigation/Navigation.types.ts +++ b/apps/docs/components/Navigation/Navigation.types.ts @@ -38,4 +38,5 @@ export type NavMenuConstant = Readonly<{ icon: string url?: `/${string}` items: ReadonlyArray> + enabled?: boolean }> diff --git a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts index 53fb20900455a..611b514922ac1 100644 --- a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts +++ b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts @@ -9,6 +9,7 @@ const { billingAll: billingEnabled, docsCompliance: complianceEnabled, 'docsSelf-hosting': selfHostingEnabled, + integrationsPartners: integrationsEnabled, sdkCsharp: sdkCsharpEnabled, sdkDart: sdkDartEnabled, sdkKotlin: sdkKotlinEnabled, @@ -19,6 +20,7 @@ const { 'billing:all', 'docs:compliance', 'docs:self-hosting', + 'integrations:partners', 'sdk:csharp', 'sdk:dart', 'sdk:kotlin', @@ -128,6 +130,7 @@ export const GLOBAL_MENU_ITEMS: GlobalMenuItems = [ hasLightIcon: true, href: '/guides/integrations' as `/${string}`, level: 'integrations', + enabled: integrationsEnabled, }, ], ], @@ -2626,6 +2629,7 @@ export const integrations: NavMenuConstant = { title: 'Integrations', icon: 'integrations', url: '/guides/integrations', + enabled: integrationsEnabled, items: [ { name: 'Overview', diff --git a/apps/docs/components/Navigation/NavigationMenu/NavigationMenuGuideListItems.tsx b/apps/docs/components/Navigation/NavigationMenu/NavigationMenuGuideListItems.tsx index 9991c5c7266bb..bf34f7541a4c5 100644 --- a/apps/docs/components/Navigation/NavigationMenu/NavigationMenuGuideListItems.tsx +++ b/apps/docs/components/Navigation/NavigationMenu/NavigationMenuGuideListItems.tsx @@ -146,6 +146,10 @@ const ContentLink = React.memo(function ContentLink(props: any) { const Content = (props) => { const { menu, id } = props + if (menu.enabled === false) { + return null + } + return (
    diff --git a/apps/docs/components/Navigation/NavigationMenu/TopNavBar.tsx b/apps/docs/components/Navigation/NavigationMenu/TopNavBar.tsx index d505347d6e334..2655bb509c6da 100644 --- a/apps/docs/components/Navigation/NavigationMenu/TopNavBar.tsx +++ b/apps/docs/components/Navigation/NavigationMenu/TopNavBar.tsx @@ -84,12 +84,7 @@ const TopNavBar: FC = () => {
    {!isUserLoading && ( diff --git a/apps/docs/features/docs/Reference.ui.client.tsx b/apps/docs/features/docs/Reference.ui.client.tsx index bb4afdf2c1310..0f4a8bba00b48 100644 --- a/apps/docs/features/docs/Reference.ui.client.tsx +++ b/apps/docs/features/docs/Reference.ui.client.tsx @@ -3,6 +3,7 @@ import type { HTMLAttributes, PropsWithChildren } from 'react' import { useContext, useEffect, useRef, useState } from 'react' import { useInView } from 'react-intersection-observer' +import { safeHistoryReplaceState } from '~/lib/historyUtils' import { cn, @@ -44,7 +45,7 @@ export function ReferenceSectionWrapper({ initialScrollHappened && window.scrollY > 0 /* Don't update on first navigation to introduction */ ) { - window.history.replaceState(null, '', link) + safeHistoryReplaceState(link) } }, }) diff --git a/apps/docs/layouts/ref/RefSubLayout.tsx b/apps/docs/layouts/ref/RefSubLayout.tsx index 284d29b341a11..1b72eeb5e5cf8 100644 --- a/apps/docs/layouts/ref/RefSubLayout.tsx +++ b/apps/docs/layouts/ref/RefSubLayout.tsx @@ -6,6 +6,7 @@ import { useNavigationMenuContext } from '~/components/Navigation/NavigationMenu import { menuState } from '~/hooks/useMenuState' import Image from 'next/legacy/image' import { cn } from 'ui' +import { safeHistoryReplaceState } from '~/lib/historyUtils' interface ISectionContainer { id: string @@ -97,7 +98,7 @@ const StickyHeader: FC = ({ icon, ...props }) => { onChange: (inView, entry) => { if (inView && window) highlightSelectedNavItem(entry.target.attributes['data-ref-id'].value) if (inView && props.scrollSpyHeader) { - window.history.replaceState(null, '', entry.target.id) + safeHistoryReplaceState(entry.target.id) // if (setActiveRefItem) setActiveRefItem(entry.target.attributes['data-ref-id'].value) menuState.setMenuActiveRefId(entry.target.attributes['data-ref-id'].value) // router.push(`/reference/javascript/${entry.target.attributes['data-ref-id'].value}`, null, { diff --git a/apps/docs/layouts/ref/RefSubLayoutNonFunc.tsx b/apps/docs/layouts/ref/RefSubLayoutNonFunc.tsx index e841d71da7a11..c6f466e32f5d6 100644 --- a/apps/docs/layouts/ref/RefSubLayoutNonFunc.tsx +++ b/apps/docs/layouts/ref/RefSubLayoutNonFunc.tsx @@ -4,6 +4,7 @@ import { highlightSelectedNavItem } from 'ui/src/components/CustomHTMLElements/C import { useRouter } from 'next/compat/router' import { useNavigationMenuContext } from '~/components/Navigation/NavigationMenu/NavigationMenu.Context' import { menuState } from '~/hooks/useMenuState' +import { safeHistoryReplaceState } from '~/lib/historyUtils' interface ISectionContainer { id: string @@ -60,7 +61,7 @@ const StickyHeader: FC = (props) => { onChange: (inView, entry) => { if (inView && window) highlightSelectedNavItem(entry.target.attributes['data-ref-id'].value) if (inView && props.scrollSpyHeader) { - window.history.replaceState(null, '', entry.target.id) + safeHistoryReplaceState(entry.target.id) // if (setActiveRefItem) setActiveRefItem(entry.target.attributes['data-ref-id'].value) menuState.setMenuActiveRefId(entry.target.attributes['data-ref-id'].value) // router.push(`/reference/javascript/${entry.target.attributes['data-ref-id'].value}`, null, { diff --git a/apps/docs/lib/historyUtils.ts b/apps/docs/lib/historyUtils.ts new file mode 100644 index 0000000000000..ff58d8954be24 --- /dev/null +++ b/apps/docs/lib/historyUtils.ts @@ -0,0 +1,13 @@ +import { debounce } from 'lodash-es' + +export const safeHistoryReplaceState = debounce((url: string) => { + if (typeof window === 'undefined') return + + if (url === window.location.href) return + + try { + window.history.replaceState(null, '', url) + } catch (error) { + console.warn('Failed to call history.replaceState:', error) + } +}, 120) diff --git a/apps/studio/components/interfaces/Auth/AuthProvidersFormValidation.tsx b/apps/studio/components/interfaces/Auth/AuthProvidersFormValidation.tsx index b3dc3724fd5d5..5d0a3d42ea528 100644 --- a/apps/studio/components/interfaces/Auth/AuthProvidersFormValidation.tsx +++ b/apps/studio/components/interfaces/Auth/AuthProvidersFormValidation.tsx @@ -436,7 +436,7 @@ const EXTERNAL_PROVIDER_APPLE = { }, EXTERNAL_APPLE_CLIENT_ID: { title: 'Client IDs', - description: `Comma separated list of allowed Apple app (Web, OAuth, iOS, macOS, watchOS, or tvOS) bundle IDs for native sign in, or service IDs for Sign in with Apple JS. [Learn more](https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_js)`, + description: `Comma separated list of allowed Apple app (Web, OAuth, iOS, macOS, watchOS, or tvOS) bundle IDs for native sign in, or service IDs for Sign in with Apple JS. [Learn more](https://developer.apple.com/documentation/signinwithapplejs)`, type: 'string', }, EXTERNAL_APPLE_SECRET: { diff --git a/apps/studio/components/interfaces/QueryPerformance/QueryPerformanceGrid.tsx b/apps/studio/components/interfaces/QueryPerformance/QueryPerformanceGrid.tsx index 0fbbd5b0e0fb1..52eeda41fee5d 100644 --- a/apps/studio/components/interfaces/QueryPerformance/QueryPerformanceGrid.tsx +++ b/apps/studio/components/interfaces/QueryPerformance/QueryPerformanceGrid.tsx @@ -145,7 +145,7 @@ export const QueryPerformanceGrid = ({ queryPerformanceQuery }: QueryPerformance const fillWidth = Math.min(percentage, 100) return ( -
    +
    +
    {isTime && typeof value === 'number' && !isNaN(value) && isFinite(value) ? (

    +

    {typeof value === 'number' && !isNaN(value) && isFinite(value) ? (

    {value.toLocaleString()} @@ -208,7 +208,7 @@ export const QueryPerformanceGrid = ({ queryPerformanceQuery }: QueryPerformance if (col.id === 'max_time' || col.id === 'mean_time' || col.id === 'min_time') { return ( -

    +
    {typeof value === 'number' && !isNaN(value) && isFinite(value) ? (

    {Math.round(value).toLocaleString()}ms @@ -222,7 +222,7 @@ export const QueryPerformanceGrid = ({ queryPerformanceQuery }: QueryPerformance if (col.id === 'rows_read') { return ( -

    +
    {typeof value === 'number' && !isNaN(value) && isFinite(value) ? (

    {value.toLocaleString()} @@ -241,7 +241,7 @@ export const QueryPerformanceGrid = ({ queryPerformanceQuery }: QueryPerformance if (col.id === 'cache_hit_rate') { return ( -

    +
    {typeof value === 'string' ? (

    { if (['CURRENT_DATE'].includes(input)) return true @@ -170,9 +171,11 @@ export const validateFields = (field: ColumnField) => { const errors = {} as Dictionary if (field.name.length === 0) { errors['name'] = `Please assign a name for your column` + toast.error(errors['name']) } if (field.format.length === 0) { errors['format'] = `Please select a type for your column` + toast.error(errors['format']) } return errors } diff --git a/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/ColumnEditor/ColumnType.tsx b/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/ColumnEditor/ColumnType.tsx index 9c813d61bb2ab..dc918a77b07a7 100644 --- a/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/ColumnEditor/ColumnType.tsx +++ b/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/ColumnEditor/ColumnType.tsx @@ -66,6 +66,7 @@ const ColumnType = ({ description, showRecommendation = false, onOptionSelect = noop, + error, }: ColumnTypeProps) => { const [open, setOpen] = useState(false) const availableTypes = POSTGRES_DATA_TYPES.concat( @@ -167,7 +168,7 @@ const ColumnType = ({