Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ const Pagination = () => {
<>
<div className="flex items-center gap-x-2">
<Button
aria-label="Previous page"
icon={<ArrowLeft />}
type="outline"
className="px-1.5"
Expand Down Expand Up @@ -179,6 +180,7 @@ const Pagination = () => {
<p className="text-xs text-foreground-light">of {totalPages.toLocaleString()}</p>

<Button
aria-label="Next page"
icon={<ArrowRight />}
type="outline"
className="px-1.5"
Expand Down
21 changes: 14 additions & 7 deletions apps/studio/components/interfaces/Auth/Users/UserOverview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { useUserSendOTPMutation } from 'data/auth/user-send-otp-mutation'
import { useUserUpdateMutation } from 'data/auth/user-update-mutation'
import { User } from 'data/auth/users-infinite-query'
import { useAsyncCheckProjectPermissions } from 'hooks/misc/useCheckPermissions'
import { useIsFeatureEnabled } from 'hooks/misc/useIsFeatureEnabled'
import { BASE_PATH } from 'lib/constants'
import { timeout } from 'lib/helpers'
import { Button, cn, Separator } from 'ui'
Expand Down Expand Up @@ -45,6 +46,10 @@ export const UserOverview = ({ user, onDeleteSuccess }: UserOverviewProps) => {
const isPhoneAuth = user.phone !== null
const isBanned = user.banned_until !== null

const { authenticationSignInProviders } = useIsFeatureEnabled([
'authentication:sign_in_providers',
])

const providers = ((user.raw_app_meta_data?.providers as string[]) ?? []).map(
(provider: string) => {
return {
Expand Down Expand Up @@ -238,13 +243,15 @@ export const UserOverview = ({ user, onDeleteSuccess }: UserOverviewProps) => {
Signed in with a {providerName} account via{' '}
{providerName === 'SAML' ? 'SSO' : 'OAuth'}
</p>
<Button asChild type="default" className="mt-2">
<Link
href={`/project/${projectRef}/auth/providers?provider=${provider.name === 'SAML' ? 'SAML 2.0' : provider.name}`}
>
Configure {providerName} provider
</Link>
</Button>
{authenticationSignInProviders && (
<Button asChild type="default" className="mt-2">
<Link
href={`/project/${projectRef}/auth/providers?provider=${provider.name === 'SAML' ? 'SAML 2.0' : provider.name}`}
>
Configure {providerName} provider
</Link>
</Button>
)}
</div>
{isActive ? (
<div className="flex items-center gap-1 rounded-full border border-brand-400 bg-brand-200 py-1 px-1 text-xs text-brand">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ export type ConnectionType = {
label: string
guideLink?: string
children: ConnectionType[]
files?: {
name: string
content: string
}[]
}

export const FRAMEWORKS: ConnectionType[] = [
Expand Down
45 changes: 33 additions & 12 deletions apps/studio/components/interfaces/Connect/Connect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { ButtonTooltip } from 'components/ui/ButtonTooltip'
import Panel from 'components/ui/Panel'
import { getKeys, useAPIKeysQuery } from 'data/api-keys/api-keys-query'
import { useProjectSettingsV2Query } from 'data/config/project-settings-v2-query'
import { useCustomContent } from 'hooks/custom-content/useCustomContent'
import { useAsyncCheckProjectPermissions } from 'hooks/misc/useCheckPermissions'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { PROJECT_STATUS } from 'lib/constants'
Expand All @@ -30,20 +31,31 @@ import {
} from 'ui'
import { CONNECTION_TYPES, ConnectionType, FRAMEWORKS, MOBILES, ORMS } from './Connect.constants'
import { getContentFilePath } from './Connect.utils'
import ConnectDropdown from './ConnectDropdown'
import ConnectTabContent from './ConnectTabContent'
import { ConnectDropdown } from './ConnectDropdown'
import { ConnectTabContent } from './ConnectTabContent'
import { ConnectTabContentCustom } from './ConnectTabContentCustom'

export const Connect = () => {
const { ref: projectRef } = useParams()
const { data: selectedProject } = useSelectedProjectQuery()
const isActiveHealthy = selectedProject?.status === PROJECT_STATUS.ACTIVE_HEALTHY

const { connectFrameworks } = useCustomContent(['connect:frameworks'])
const connectionTypes = !connectFrameworks
? CONNECTION_TYPES
: [
{ key: 'direct', label: 'Connection String', obj: [] },
connectFrameworks,
{ key: 'orms', label: 'ORMs', obj: ORMS },
]
const frameworks = !connectFrameworks ? FRAMEWORKS : connectFrameworks.obj

const [showConnect, setShowConnect] = useQueryState(
'showConnect',
parseAsBoolean.withDefault(false)
)

const [connectionObject, setConnectionObject] = useState<ConnectionType[]>(FRAMEWORKS)
const [connectionObject, setConnectionObject] = useState<ConnectionType[]>(frameworks)
const [selectedParent, setSelectedParent] = useState(connectionObject[0].key) // aka nextjs
const [selectedChild, setSelectedChild] = useState(
connectionObject.find((item) => item.key === selectedParent)?.children[0]?.key ?? ''
Expand All @@ -54,6 +66,8 @@ export const Connect = () => {
?.children.find((child) => child.key === selectedChild)?.children[0]?.key || ''
)

const isFrameworkSelected = frameworks.some((x) => x.key === selectedParent)

const { data: settings } = useProjectSettingsV2Query({ projectRef }, { enabled: showConnect })
const { can: canReadAPIKeys } = useAsyncCheckProjectPermissions(
PermissionAction.READ,
Expand Down Expand Up @@ -109,8 +123,8 @@ export const Connect = () => {

function handleConnectionType(type: string) {
if (type === 'frameworks') {
setConnectionObject(FRAMEWORKS)
handleConnectionTypeChange(FRAMEWORKS)
setConnectionObject(frameworks)
handleConnectionTypeChange(frameworks)
}

if (type === 'mobiles') {
Expand Down Expand Up @@ -207,14 +221,14 @@ export const Connect = () => {

<Tabs_Shadcn_ defaultValue="direct" onValueChange={(value) => handleConnectionType(value)}>
<TabsList_Shadcn_ className={cn('flex overflow-x-scroll gap-x-4', DIALOG_PADDING_X)}>
{CONNECTION_TYPES.map((type) => (
{connectionTypes.map((type) => (
<TabsTrigger_Shadcn_ key={type.key} value={type.key} className="px-0">
{type.label}
</TabsTrigger_Shadcn_>
))}
</TabsList_Shadcn_>

{CONNECTION_TYPES.map((type) => {
{connectionTypes.map((type) => {
const hasChildOptions =
(connectionObject.find((parent) => parent.key === selectedParent)?.children.length ||
0) > 0
Expand Down Expand Up @@ -290,11 +304,18 @@ export const Connect = () => {
<p className="text-xs text-foreground-lighter my-3">
Add the following files below to your application
</p>
<ConnectTabContent
projectKeys={projectKeys}
filePath={filePath}
className="rounded-b-none"
/>
{!!connectFrameworks && isFrameworkSelected ? (
<ConnectTabContentCustom
projectKeys={projectKeys}
framework={frameworks.find((x) => x.key === selectedParent)}
/>
) : (
<ConnectTabContent
projectKeys={projectKeys}
filePath={filePath}
className="rounded-b-none"
/>
)}
<Panel.Notice
className="border border-t-0 rounded-lg rounded-t-none"
title="New API keys coming 2025"
Expand Down
15 changes: 5 additions & 10 deletions apps/studio/components/interfaces/Connect/ConnectDropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,17 @@ import {
Popover_Shadcn_,
cn,
} from 'ui'
import { ConnectionType } from './Connect.constants'
import { ConnectionIcon } from './ConnectionIcon'

interface ConnectDropdownProps {
state: string
updateState: (state: string) => void
label: string
items: any[]
items: ConnectionType[]
}

const ConnectDropdown = ({
export const ConnectDropdown = ({
state,
updateState,
label,
Expand Down Expand Up @@ -53,11 +54,7 @@ const ConnectDropdown = ({
iconRight={<ChevronDown strokeWidth={1.5} />}
>
<div className="flex items-center gap-2">
{selectedItem?.icon ? (
<ConnectionIcon connection={selectedItem.icon} />
) : (
<Box size={12} />
)}
{selectedItem?.icon ? <ConnectionIcon icon={selectedItem.icon} /> : <Box size={12} />}
{selectedItem?.label}
</div>
</Button>
Expand All @@ -79,7 +76,7 @@ const ConnectDropdown = ({
}}
className="flex gap-2 items-center"
>
{item.icon ? <ConnectionIcon connection={item.icon} /> : <Box size={12} />}
{item.icon ? <ConnectionIcon icon={item.icon} /> : <Box size={12} />}
{item.label}
<Check
size={15}
Expand All @@ -94,5 +91,3 @@ const ConnectDropdown = ({
</Popover_Shadcn_>
)
}

export default ConnectDropdown
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ interface ConnectContentTabProps extends HTMLAttributes<HTMLDivElement> {
}
}

const ConnectTabContent = forwardRef<HTMLDivElement, ConnectContentTabProps>(
export const ConnectTabContent = forwardRef<HTMLDivElement, ConnectContentTabProps>(
({ projectKeys, filePath, ...props }, ref) => {
const { ref: projectRef } = useParams()
const { data: selectedOrg } = useSelectedOrganizationQuery()
Expand Down Expand Up @@ -102,5 +102,3 @@ const ConnectTabContent = forwardRef<HTMLDivElement, ConnectContentTabProps>(
)

ConnectTabContent.displayName = 'ConnectTabContent'

export default ConnectTabContent
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { useEffect, useState } from 'react'

import { cn, SimpleCodeBlock } from 'ui'
import { ConnectionType } from './Connect.constants'
import { projectKeys } from './Connect.types'
import {
ConnectTabContent,
ConnectTabs,
ConnectTabTrigger,
ConnectTabTriggers,
} from './ConnectTabs'

interface ConnectTabContentCustomProps {
projectKeys: projectKeys
framework?: ConnectionType
}

export const ConnectTabContentCustom = ({
projectKeys,
framework,
}: ConnectTabContentCustomProps) => {
const { files = [] } = framework ?? {}

const [selectedTab, setSelectedTab] = useState<string>()

useEffect(() => {
if (framework?.files) setSelectedTab(framework.files[0].name)
}, [framework])

return (
<div className={cn('border rounded-lg rounded-b-none')}>
<ConnectTabs value={selectedTab} onValueChange={setSelectedTab}>
<ConnectTabTriggers>
{files.map((x) => (
<ConnectTabTrigger key={`${x.name}-tab`} value={x.name} />
))}
</ConnectTabTriggers>

{files.map((x) => {
const format = x.name.split('.')[1] ?? 'bash'
const content = x.content
.replaceAll('{{apiUrl}}', projectKeys.apiUrl ?? '')
.replaceAll('{{anonKey}}', projectKeys.anonKey ?? '')
.replaceAll('{{publishableKey}}', projectKeys.publishableKey ?? '')
return (
<ConnectTabContent key={`${x.name}-content`} value={x.name}>
<SimpleCodeBlock className={format} parentClassName="min-h-72">
{content}
</SimpleCodeBlock>
</ConnectTabContent>
)
})}
</ConnectTabs>
</div>
)
}
10 changes: 8 additions & 2 deletions apps/studio/components/interfaces/Connect/ConnectTabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,26 @@ interface ConnectTabTriggersProps {

interface ConnectFileTabProps {
children: ReactNode[]
value?: string
onValueChange?: (value: string) => void
}

interface ConnectTabContentProps {
children: ReactNode
value: string
}
const ConnectTabs = ({ children }: ConnectFileTabProps) => {
const ConnectTabs = ({ children, value, onValueChange }: ConnectFileTabProps) => {
const firstChild = children[0]

const defaultValue = isValidElement(firstChild)
? (firstChild.props as any)?.children[0]?.props?.value || ''
: null

return <Tabs_Shadcn_ defaultValue={defaultValue}>{children}</Tabs_Shadcn_>
return (
<Tabs_Shadcn_ defaultValue={defaultValue} value={value} onValueChange={onValueChange}>
{children}
</Tabs_Shadcn_>
)
}

const ConnectTabTrigger = ({ value }: ConnectTabTriggerProps) => {
Expand Down
25 changes: 14 additions & 11 deletions apps/studio/components/interfaces/Connect/ConnectionIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,29 @@ import Image from 'next/image'
import { BASE_PATH } from 'lib/constants'

interface ConnectionIconProps {
connection: any
icon: string
}

export const ConnectionIcon = ({ connection }: ConnectionIconProps) => {
export const ConnectionIcon = ({ icon }: ConnectionIconProps) => {
const { resolvedTheme } = useTheme()

const imageFolder = ['ionic-angular'].includes(connection) ? 'icons/frameworks' : 'libraries'
const imageFolder = ['ionic-angular'].includes(icon) ? 'icons/frameworks' : 'libraries'
const imageExtension = imageFolder === 'icons/frameworks' ? '' : '-icon'

return (
<Image
className="transition-all group-hover:scale-110"
src={`${BASE_PATH}/img/${imageFolder}/${connection.toLowerCase()}${
['expo', 'nextjs', 'prisma', 'drizzle', 'astro', 'remix'].includes(connection.toLowerCase())
const iconImgSrc = icon.startsWith('http')
? icon
: `${BASE_PATH}/img/${imageFolder}/${icon.toLowerCase()}${
['expo', 'nextjs', 'prisma', 'drizzle', 'astro', 'remix'].includes(icon.toLowerCase())
? resolvedTheme?.includes('dark')
? '-dark'
: ''
: ''
}${imageExtension}.svg`}
alt={`${connection} logo`}
}${imageExtension}.svg`

return (
<Image
className="transition-all group-hover:scale-110"
src={iconImgSrc}
alt={`${icon} logo`}
width={14}
height={14}
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import type { ContentFileProps } from 'components/interfaces/Connect/Connect.types'

import {
ConnectTabContent,
ConnectTabs,
ConnectTabTrigger,
ConnectTabTriggers,
ConnectTabContent,
} from 'components/interfaces/Connect/ConnectTabs'
import { SimpleCodeBlock } from 'ui'

Expand Down Expand Up @@ -154,4 +154,6 @@ export const createClient = (request: NextRequest) => {
)
}

// [Joshen] Used as a dynamic import
// eslint-disable-next-line no-restricted-exports
export default ContentFile
2 changes: 1 addition & 1 deletion apps/studio/components/interfaces/Docs/GeneralContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Authentication from 'components/interfaces/Docs/Authentication'
import Introduction from 'components/interfaces/Docs/Introduction'
import RpcIntroduction from 'components/interfaces/Docs/Pages/Rpc/Introduction'
import TablesIntroduction from 'components/interfaces/Docs/Pages/Tables/Introduction'
import UserManagement from 'components/interfaces/Docs/Pages/UserManagement'
import { UserManagement } from 'components/interfaces/Docs/Pages/UserManagement'

interface GeneralContentProps {
page?: string
Expand Down
Loading
Loading