From d3c2e144ea35cd3cb55555ca92a04db83e422309 Mon Sep 17 00:00:00 2001 From: Saxon Fletcher Date: Tue, 26 Aug 2025 20:33:27 +1000 Subject: [PATCH] connect --- .../components/interfaces/Connect/Connect.tsx | 49 +++++- .../interfaces/HomeNew/AdvisorWidget.tsx | 2 +- .../interfaces/HomeNew/FrameworkSelector.tsx | 89 +++++++++++ .../interfaces/HomeNew/GettingStarted.tsx | 12 +- .../HomeNew/GettingStartedSection.tsx | 140 +++++++++++++++--- 5 files changed, 261 insertions(+), 31 deletions(-) create mode 100644 apps/studio/components/interfaces/HomeNew/FrameworkSelector.tsx diff --git a/apps/studio/components/interfaces/Connect/Connect.tsx b/apps/studio/components/interfaces/Connect/Connect.tsx index cbbcd64a943..a072b35e1c1 100644 --- a/apps/studio/components/interfaces/Connect/Connect.tsx +++ b/apps/studio/components/interfaces/Connect/Connect.tsx @@ -1,8 +1,8 @@ import { PermissionAction } from '@supabase/shared-types/out/constants' import { useParams } from 'common' import { ExternalLink, Plug } from 'lucide-react' -import { parseAsBoolean, useQueryState } from 'nuqs' -import { useMemo, useState } from 'react' +import { parseAsBoolean, parseAsString, useQueryState } from 'nuqs' +import { useEffect, useMemo, useState } from 'react' import { DatabaseConnectionString } from 'components/interfaces/Connect/DatabaseConnectionString' import { ButtonTooltip } from 'components/ui/ButtonTooltip' @@ -55,6 +55,16 @@ export const Connect = () => { parseAsBoolean.withDefault(false) ) + const [connectTab, setConnectTab] = useQueryState( + 'connectTab', + parseAsString.withDefault('direct').withOptions({ clearOnDefault: true }) + ) + + const [prefFramework] = useQueryState( + 'framework', + parseAsString.withDefault('').withOptions({ clearOnDefault: true }) + ) + const [connectionObject, setConnectionObject] = useState(frameworks) const [selectedParent, setSelectedParent] = useState(connectionObject[0].key) // aka nextjs const [selectedChild, setSelectedChild] = useState( @@ -185,6 +195,33 @@ export const Connect = () => { selectedGrandchild, }) + // Sync initial tab and framework from query params + useEffect(() => { + if (connectTab === 'frameworks') { + setConnectionObject(frameworks) + handleConnectionTypeChange(frameworks) + if (prefFramework) { + const exists = frameworks.find((f) => f.key === prefFramework) + if (exists) { + setSelectedParent(prefFramework) + // Reset children/grandchildren based on the new parent + const firstChild = exists.children?.[0]?.key ?? '' + setSelectedChild(firstChild) + const firstGrand = exists.children?.[0]?.children?.[0]?.key ?? '' + setSelectedGrandchild(firstGrand) + } + } + } + if (connectTab === 'mobiles') { + setConnectionObject(MOBILES) + handleConnectionTypeChange(MOBILES) + } + if (connectTab === 'orms') { + setConnectionObject(ORMS) + handleConnectionTypeChange(ORMS) + } + }, [connectTab, prefFramework]) + if (!isActiveHealthy) { return ( { - handleConnectionType(value)}> + { + handleConnectionType(value) + setConnectTab(value) + }} + > {connectionTypes.map((type) => ( diff --git a/apps/studio/components/interfaces/HomeNew/AdvisorWidget.tsx b/apps/studio/components/interfaces/HomeNew/AdvisorWidget.tsx index c99a9cedc22..f11c359b257 100644 --- a/apps/studio/components/interfaces/HomeNew/AdvisorWidget.tsx +++ b/apps/studio/components/interfaces/HomeNew/AdvisorWidget.tsx @@ -154,7 +154,7 @@ export const AdvisorWidget = () => { }} /> - + {lint.detail ? getFirstSentence(lint.detail) : lint.title} diff --git a/apps/studio/components/interfaces/HomeNew/FrameworkSelector.tsx b/apps/studio/components/interfaces/HomeNew/FrameworkSelector.tsx new file mode 100644 index 00000000000..72483cbf771 --- /dev/null +++ b/apps/studio/components/interfaces/HomeNew/FrameworkSelector.tsx @@ -0,0 +1,89 @@ +import { useState } from 'react' +import { + Button, + CommandEmpty_Shadcn_, + CommandGroup_Shadcn_, + CommandInput_Shadcn_, + CommandItem_Shadcn_, + CommandList_Shadcn_, + Command_Shadcn_, + PopoverContent_Shadcn_, + PopoverTrigger_Shadcn_, + Popover_Shadcn_, + cn, +} from 'ui' +import { Box, Check, ChevronDown } from 'lucide-react' +import { ConnectionType } from 'components/interfaces/Connect/Connect.constants' +import { ConnectionIcon } from 'components/interfaces/Connect/ConnectionIcon' + +interface FrameworkSelectorProps { + value: string + onChange: (value: string) => void + items: ConnectionType[] + label?: string + className?: string +} + +export const FrameworkSelector = ({ + value, + onChange, + items, + label = 'Framework', + className, +}: FrameworkSelectorProps) => { + const [open, setOpen] = useState(false) + + const selectedItem = items.find((item) => item.key === value) + + function handleSelect(key: string) { + onChange(key) + setOpen(false) + } + + return ( + +
+ + + +
+ + + + + No results found. + + {items.map((item) => ( + handleSelect(item.key)} + className="flex gap-2 items-center" + > + {item.icon ? : } + {item.label} + + + ))} + + + + +
+ ) +} + +export default FrameworkSelector diff --git a/apps/studio/components/interfaces/HomeNew/GettingStarted.tsx b/apps/studio/components/interfaces/HomeNew/GettingStarted.tsx index e9b0a59e4a8..a7656ca2db9 100644 --- a/apps/studio/components/interfaces/HomeNew/GettingStarted.tsx +++ b/apps/studio/components/interfaces/HomeNew/GettingStarted.tsx @@ -9,6 +9,7 @@ export type GettingStartedAction = { onClick?: () => void variant?: React.ComponentProps['type'] icon?: React.ReactNode + component?: React.ReactNode } export type GettingStartedStep = { @@ -17,6 +18,7 @@ export type GettingStartedStep = { icon?: React.ReactNode title: string description: string + image?: React.ReactNode actions: GettingStartedAction[] } @@ -36,7 +38,7 @@ export function GettingStarted({ steps, className }: GettingStartedProps) { {steps.map((step) => ( - +
{step.icon &&
{step.icon}
} @@ -49,10 +51,14 @@ export function GettingStarted({ steps, className }: GettingStartedProps) { {step.status} - + + {step.image &&
{step.image}
}

{step.description}

-
+
{step.actions.map((action, i) => { + if (action.component) { + return {action.component} + } const content = (