diff --git a/apps/sim/app/workspace/[workspaceId]/components/oauth-modal.test.ts b/apps/sim/app/workspace/[workspaceId]/components/oauth-modal.test.ts new file mode 100644 index 00000000000..d0ca2abfe6f --- /dev/null +++ b/apps/sim/app/workspace/[workspaceId]/components/oauth-modal.test.ts @@ -0,0 +1,72 @@ +/** + * @vitest-environment node + */ +import { beforeEach, describe, expect, it, vi } from 'vitest' + +vi.mock('@/lib/auth/auth-client', () => ({ + client: { oauth2: { link: vi.fn() } }, + useSession: vi.fn(() => ({ data: null, isPending: false, error: null })), +})) + +vi.mock('@/lib/credentials/client-state', () => ({ + writeOAuthReturnContext: vi.fn(), +})) + +vi.mock('@/hooks/queries/credentials', () => ({ + useCreateCredentialDraft: vi.fn(() => ({ mutateAsync: vi.fn(), isPending: false })), +})) + +vi.mock('@/lib/oauth', () => ({ + getCanonicalScopesForProvider: vi.fn(() => []), + getProviderIdFromServiceId: vi.fn((id: string) => id), + OAUTH_PROVIDERS: {}, + parseProvider: vi.fn((p: string) => ({ baseProvider: p, variant: null })), +})) + +vi.mock('@/lib/oauth/utils', () => ({ + getScopeDescription: vi.fn((s: string) => s), +})) + +import { getDefaultCredentialName } from '@/app/workspace/[workspaceId]/components/oauth-modal' + +describe('getDefaultCredentialName', () => { + beforeEach(() => { + vi.clearAllMocks() + }) + + it('uses the user name when available', () => { + expect(getDefaultCredentialName('Waleed', 'Google Drive', 0)).toBe("Waleed's Google Drive 1") + }) + + it('increments the number based on existing credential count', () => { + expect(getDefaultCredentialName('Waleed', 'Google Drive', 2)).toBe("Waleed's Google Drive 3") + }) + + it('falls back to "My" when user name is null', () => { + expect(getDefaultCredentialName(null, 'Slack', 0)).toBe('My Slack 1') + }) + + it('falls back to "My" when user name is undefined', () => { + expect(getDefaultCredentialName(undefined, 'Gmail', 1)).toBe('My Gmail 2') + }) + + it('falls back to "My" when user name is empty string', () => { + expect(getDefaultCredentialName('', 'GitHub', 0)).toBe('My GitHub 1') + }) + + it('falls back to "My" when user name is whitespace-only', () => { + expect(getDefaultCredentialName(' ', 'Notion', 0)).toBe('My Notion 1') + }) + + it('trims whitespace from user name', () => { + expect(getDefaultCredentialName(' Waleed ', 'Linear', 0)).toBe("Waleed's Linear 1") + }) + + it('works with zero existing credentials', () => { + expect(getDefaultCredentialName('Alice', 'Jira', 0)).toBe("Alice's Jira 1") + }) + + it('works with many existing credentials', () => { + expect(getDefaultCredentialName('Bob', 'Slack', 9)).toBe("Bob's Slack 10") + }) +}) diff --git a/apps/sim/app/workspace/[workspaceId]/components/oauth-modal.tsx b/apps/sim/app/workspace/[workspaceId]/components/oauth-modal.tsx new file mode 100644 index 00000000000..5f4e89edd09 --- /dev/null +++ b/apps/sim/app/workspace/[workspaceId]/components/oauth-modal.tsx @@ -0,0 +1,304 @@ +'use client' + +import { useMemo, useState } from 'react' +import { createLogger } from '@sim/logger' +import { Check } from 'lucide-react' +import { + Badge, + Button, + Input, + Label, + Modal, + ModalBody, + ModalContent, + ModalFooter, + ModalHeader, +} from '@/components/emcn' +import { client, useSession } from '@/lib/auth/auth-client' +import type { OAuthReturnContext } from '@/lib/credentials/client-state' +import { writeOAuthReturnContext } from '@/lib/credentials/client-state' +import { + getCanonicalScopesForProvider, + getProviderIdFromServiceId, + OAUTH_PROVIDERS, + type OAuthProvider, + parseProvider, +} from '@/lib/oauth' +import { getScopeDescription } from '@/lib/oauth/utils' +import { useCreateCredentialDraft } from '@/hooks/queries/credentials' + +const logger = createLogger('OAuthModal') +const EMPTY_SCOPES: string[] = [] + +/** + * Generates a default credential display name. + * Format: "{User}'s {Provider} {N}" or "My {Provider} {N}" when no user name is available. + */ +export function getDefaultCredentialName( + userName: string | null | undefined, + providerName: string, + credentialCount: number +): string { + const trimmed = userName?.trim() + const num = credentialCount + 1 + if (trimmed) { + return `${trimmed}'s ${providerName} ${num}` + } + return `My ${providerName} ${num}` +} + +interface OAuthModalBaseProps { + isOpen: boolean + onClose: () => void + provider: OAuthProvider + serviceId: string +} + +type OAuthModalConnectProps = OAuthModalBaseProps & { + mode: 'connect' + workspaceId: string + credentialCount: number +} & ( + | { workflowId: string; knowledgeBaseId?: never } + | { workflowId?: never; knowledgeBaseId: string } + ) + +interface OAuthModalReauthorizeProps extends OAuthModalBaseProps { + mode: 'reauthorize' + toolName: string + requiredScopes?: string[] + newScopes?: string[] + onConnect?: () => Promise | void +} + +export type OAuthModalProps = OAuthModalConnectProps | OAuthModalReauthorizeProps + +export function OAuthModal(props: OAuthModalProps) { + const { isOpen, onClose, provider, serviceId, mode } = props + + const isConnect = mode === 'connect' + const credentialCount = isConnect ? props.credentialCount : 0 + const workspaceId = isConnect ? props.workspaceId : '' + const workflowId = isConnect ? props.workflowId : undefined + const knowledgeBaseId = isConnect ? props.knowledgeBaseId : undefined + const toolName = !isConnect ? props.toolName : '' + const requiredScopes = !isConnect ? (props.requiredScopes ?? EMPTY_SCOPES) : EMPTY_SCOPES + const newScopes = !isConnect ? (props.newScopes ?? EMPTY_SCOPES) : EMPTY_SCOPES + const onConnectOverride = !isConnect ? props.onConnect : undefined + + const { data: session } = useSession() + const [error, setError] = useState(null) + const createDraft = useCreateCredentialDraft() + + const { providerName, ProviderIcon } = useMemo(() => { + const { baseProvider } = parseProvider(provider) + const baseProviderConfig = OAUTH_PROVIDERS[baseProvider] + let name = baseProviderConfig?.name || provider + let Icon = baseProviderConfig?.icon || (() => null) + if (baseProviderConfig) { + for (const [key, service] of Object.entries(baseProviderConfig.services)) { + if (key === serviceId || service.providerId === provider) { + name = service.name + Icon = service.icon + break + } + } + } + return { providerName: name, ProviderIcon: Icon } + }, [provider, serviceId]) + + const providerId = getProviderIdFromServiceId(serviceId) + + const [displayName, setDisplayName] = useState(() => + isConnect ? getDefaultCredentialName(session?.user?.name, providerName, credentialCount) : '' + ) + + const newScopesSet = useMemo( + () => + new Set( + newScopes.filter( + (scope) => !scope.includes('userinfo.email') && !scope.includes('userinfo.profile') + ) + ), + [newScopes] + ) + + const displayScopes = useMemo(() => { + if (isConnect) { + return getCanonicalScopesForProvider(providerId).filter( + (scope) => !scope.includes('userinfo.email') && !scope.includes('userinfo.profile') + ) + } + const filtered = requiredScopes.filter( + (scope) => !scope.includes('userinfo.email') && !scope.includes('userinfo.profile') + ) + return filtered.sort((a, b) => { + const aIsNew = newScopesSet.has(a) + const bIsNew = newScopesSet.has(b) + if (aIsNew && !bIsNew) return -1 + if (!aIsNew && bIsNew) return 1 + return 0 + }) + }, [isConnect, providerId, requiredScopes, newScopesSet]) + + const handleClose = () => { + setError(null) + onClose() + } + + const handleConnect = async () => { + setError(null) + + try { + if (isConnect) { + const trimmedName = displayName.trim() + if (!trimmedName) { + setError('Display name is required.') + return + } + + await createDraft.mutateAsync({ + workspaceId, + providerId, + displayName: trimmedName, + }) + + const baseContext = { + displayName: trimmedName, + providerId, + preCount: credentialCount, + workspaceId, + requestedAt: Date.now(), + } + + const returnContext: OAuthReturnContext = knowledgeBaseId + ? { ...baseContext, origin: 'kb-connectors' as const, knowledgeBaseId } + : { ...baseContext, origin: 'workflow' as const, workflowId: workflowId! } + + writeOAuthReturnContext(returnContext) + } + + if (!isConnect && onConnectOverride) { + await onConnectOverride() + onClose() + return + } + + if (!isConnect) { + logger.info('Linking OAuth2:', { + providerId, + requiredScopes, + hasNewScopes: newScopes.length > 0, + }) + } + + if (providerId === 'trello') { + if (!isConnect) onClose() + window.location.href = '/api/auth/trello/authorize' + return + } + + if (providerId === 'shopify') { + if (!isConnect) onClose() + const returnUrl = encodeURIComponent(window.location.href) + window.location.href = `/api/auth/shopify/authorize?returnUrl=${returnUrl}` + return + } + + await client.oauth2.link({ providerId, callbackURL: window.location.href }) + handleClose() + } catch (err) { + logger.error('Failed to initiate OAuth connection', { error: err }) + setError('Failed to connect. Please try again.') + } + } + + const isPending = isConnect && createDraft.isPending + const isConnectDisabled = isConnect ? !displayName.trim() || Boolean(isPending) : false + + const subtitle = isConnect + ? `Grant access to use ${providerName} in your ${knowledgeBaseId ? 'knowledge base' : 'workflow'}` + : `The "${toolName}" tool requires access to your account` + + return ( + !open && handleClose()}> + + Connect {providerName} + +
+
+
+ +
+
+

+ Connect your {providerName} account +

+

{subtitle}

+
+
+ + {displayScopes.length > 0 && ( +
+
+

+ Permissions requested +

+
+
    + {displayScopes.map((scope) => ( +
  • +
    + +
    +
    + {getScopeDescription(scope)} + {!isConnect && newScopesSet.has(scope) && ( + + New + + )} +
    +
  • + ))} +
+
+ )} + + {isConnect && ( +
+ + { + setDisplayName(e.target.value) + setError(null) + }} + onKeyDown={(e) => { + if (e.key === 'Enter' && !isPending) void handleConnect() + }} + placeholder={`My ${providerName} account`} + autoComplete='off' + data-lpignore='true' + className='mt-1.5' + /> +
+ )} + + {error &&

{error}

} +
+
+ + + + +
+
+ ) +} diff --git a/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/add-connector-modal/add-connector-modal.tsx b/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/add-connector-modal/add-connector-modal.tsx index 9e266ec4e80..223091360bc 100644 --- a/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/add-connector-modal/add-connector-modal.tsx +++ b/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/add-connector-modal/add-connector-modal.tsx @@ -21,8 +21,8 @@ import { } from '@/components/emcn' import { consumeOAuthReturnContext } from '@/lib/credentials/client-state' import { getProviderIdFromServiceId, type OAuthProvider } from '@/lib/oauth' +import { OAuthModal } from '@/app/workspace/[workspaceId]/components/oauth-modal' import { ConnectorSelectorField } from '@/app/workspace/[workspaceId]/knowledge/[id]/components/add-connector-modal/components/connector-selector-field' -import { ConnectCredentialModal } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/credential-selector/components/connect-credential-modal' import { getDependsOnFields } from '@/blocks/utils' import { CONNECTOR_REGISTRY } from '@/connectors/registry' import type { ConnectorConfig, ConnectorConfigField } from '@/connectors/types' @@ -553,7 +553,8 @@ export function AddConnectorModal({ open, onOpenChange, knowledgeBaseId }: AddCo connectorConfig && connectorConfig.auth.mode === 'oauth' && connectorProviderId && ( - { consumeOAuthReturnContext() diff --git a/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/connectors-section/connectors-section.tsx b/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/connectors-section/connectors-section.tsx index 37049a212d2..578b6df254e 100644 --- a/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/connectors-section/connectors-section.tsx +++ b/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/connectors-section/connectors-section.tsx @@ -35,9 +35,8 @@ import { type OAuthProvider, } from '@/lib/oauth' import { getMissingRequiredScopes } from '@/lib/oauth/utils' +import { OAuthModal } from '@/app/workspace/[workspaceId]/components/oauth-modal' import { EditConnectorModal } from '@/app/workspace/[workspaceId]/knowledge/[id]/components/edit-connector-modal/edit-connector-modal' -import { ConnectCredentialModal } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/credential-selector/components/connect-credential-modal' -import { OAuthRequiredModal } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/credential-selector/components/oauth-required-modal' import { CONNECTOR_REGISTRY } from '@/connectors/registry' import type { ConnectorData, SyncLogData } from '@/hooks/queries/kb/connectors' import { @@ -520,7 +519,8 @@ function ConnectorCard({ )} {showOAuthModal && serviceId && providerId && !connector.credentialId && ( - { consumeOAuthReturnContext() @@ -535,7 +535,8 @@ function ConnectorCard({ )} {showOAuthModal && serviceId && providerId && connector.credentialId && ( - { consumeOAuthReturnContext() diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/credential-selector/components/connect-credential-modal.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/credential-selector/components/connect-credential-modal.tsx deleted file mode 100644 index c6574780423..00000000000 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/credential-selector/components/connect-credential-modal.tsx +++ /dev/null @@ -1,222 +0,0 @@ -'use client' - -import { useMemo, useState } from 'react' -import { createLogger } from '@sim/logger' -import { Check } from 'lucide-react' -import { - Button, - Input, - Label, - Modal, - ModalBody, - ModalContent, - ModalFooter, - ModalHeader, -} from '@/components/emcn' -import { client } from '@/lib/auth/auth-client' -import type { OAuthReturnContext } from '@/lib/credentials/client-state' -import { writeOAuthReturnContext } from '@/lib/credentials/client-state' -import { - getCanonicalScopesForProvider, - getProviderIdFromServiceId, - OAUTH_PROVIDERS, - type OAuthProvider, - parseProvider, -} from '@/lib/oauth' -import { getScopeDescription } from '@/lib/oauth/utils' -import { useCreateCredentialDraft } from '@/hooks/queries/credentials' - -const logger = createLogger('ConnectCredentialModal') - -interface ConnectCredentialModalBaseProps { - isOpen: boolean - onClose: () => void - provider: OAuthProvider - serviceId: string - workspaceId: string - /** Number of existing credentials for this provider — used to detect a successful new connection. */ - credentialCount: number -} - -export type ConnectCredentialModalProps = ConnectCredentialModalBaseProps & - ( - | { workflowId: string; knowledgeBaseId?: never } - | { workflowId?: never; knowledgeBaseId: string } - ) - -export function ConnectCredentialModal({ - isOpen, - onClose, - provider, - serviceId, - workspaceId, - workflowId, - knowledgeBaseId, - credentialCount, -}: ConnectCredentialModalProps) { - const [displayName, setDisplayName] = useState('') - const [error, setError] = useState(null) - - const createDraft = useCreateCredentialDraft() - - const { providerName, ProviderIcon } = useMemo(() => { - const { baseProvider } = parseProvider(provider) - const baseProviderConfig = OAUTH_PROVIDERS[baseProvider] - let name = baseProviderConfig?.name || provider - let Icon = baseProviderConfig?.icon || (() => null) - if (baseProviderConfig) { - for (const [key, service] of Object.entries(baseProviderConfig.services)) { - if (key === serviceId || service.providerId === provider) { - name = service.name - Icon = service.icon - break - } - } - } - return { providerName: name, ProviderIcon: Icon } - }, [provider, serviceId]) - - const providerId = getProviderIdFromServiceId(serviceId) - - const displayScopes = useMemo( - () => - getCanonicalScopesForProvider(providerId).filter( - (scope) => !scope.includes('userinfo.email') && !scope.includes('userinfo.profile') - ), - [providerId] - ) - - const handleClose = () => { - setDisplayName('') - setError(null) - onClose() - } - - const handleConnect = async () => { - const trimmedName = displayName.trim() - if (!trimmedName) { - setError('Display name is required.') - return - } - - setError(null) - - try { - await createDraft.mutateAsync({ workspaceId, providerId, displayName: trimmedName }) - - const baseContext = { - displayName: trimmedName, - providerId, - preCount: credentialCount, - workspaceId, - requestedAt: Date.now(), - } - - const returnContext: OAuthReturnContext = knowledgeBaseId - ? { ...baseContext, origin: 'kb-connectors' as const, knowledgeBaseId } - : { ...baseContext, origin: 'workflow' as const, workflowId: workflowId! } - - writeOAuthReturnContext(returnContext) - - if (providerId === 'trello') { - window.location.href = '/api/auth/trello/authorize' - return - } - - if (providerId === 'shopify') { - const returnUrl = encodeURIComponent(window.location.href) - window.location.href = `/api/auth/shopify/authorize?returnUrl=${returnUrl}` - return - } - - await client.oauth2.link({ providerId, callbackURL: window.location.href }) - handleClose() - } catch (err) { - logger.error('Failed to initiate OAuth connection', { error: err }) - setError('Failed to connect. Please try again.') - } - } - - const isPending = createDraft.isPending - - return ( - !open && handleClose()}> - - Connect {providerName} - -
-
-
- -
-
-

- Connect your {providerName} account -

-

- Grant access to use {providerName} in your workflow -

-
-
- - {displayScopes.length > 0 && ( -
-
-

- Permissions requested -

-
-
    - {displayScopes.map((scope) => ( -
  • -
    - -
    -
    - {getScopeDescription(scope)} -
    -
  • - ))} -
-
- )} - -
- - { - setDisplayName(e.target.value) - setError(null) - }} - onKeyDown={(e) => { - if (e.key === 'Enter' && !isPending) void handleConnect() - }} - placeholder={`My ${providerName} account`} - autoComplete='off' - data-lpignore='true' - className='mt-1.5' - /> -
- - {error &&

{error}

} -
-
- - - - -
-
- ) -} diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/credential-selector/components/oauth-required-modal.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/credential-selector/components/oauth-required-modal.tsx deleted file mode 100644 index 61e95bc3cc9..00000000000 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/credential-selector/components/oauth-required-modal.tsx +++ /dev/null @@ -1,190 +0,0 @@ -'use client' - -import { useMemo, useState } from 'react' -import { createLogger } from '@sim/logger' -import { Check } from 'lucide-react' -import { - Badge, - Button, - Modal, - ModalBody, - ModalContent, - ModalFooter, - ModalHeader, -} from '@/components/emcn' -import { client } from '@/lib/auth/auth-client' -import { - getProviderIdFromServiceId, - getScopeDescription, - OAUTH_PROVIDERS, - type OAuthProvider, - parseProvider, -} from '@/lib/oauth' - -const logger = createLogger('OAuthRequiredModal') - -export interface OAuthRequiredModalProps { - isOpen: boolean - onClose: () => void - provider: OAuthProvider - toolName: string - requiredScopes?: string[] - serviceId: string - newScopes?: string[] - onConnect?: () => Promise | void -} - -export function OAuthRequiredModal({ - isOpen, - onClose, - provider, - toolName, - requiredScopes = [], - serviceId, - newScopes = [], - onConnect, -}: OAuthRequiredModalProps) { - const [error, setError] = useState(null) - const { baseProvider } = parseProvider(provider) - const baseProviderConfig = OAUTH_PROVIDERS[baseProvider] - - let providerName = baseProviderConfig?.name || provider - let ProviderIcon = baseProviderConfig?.icon || (() => null) - - if (baseProviderConfig) { - for (const [key, service] of Object.entries(baseProviderConfig.services)) { - if (key === serviceId || service.providerId === provider) { - providerName = service.name - ProviderIcon = service.icon - break - } - } - } - - const newScopesSet = useMemo( - () => - new Set( - (newScopes || []).filter( - (scope) => !scope.includes('userinfo.email') && !scope.includes('userinfo.profile') - ) - ), - [newScopes] - ) - - const displayScopes = useMemo(() => { - const filtered = requiredScopes.filter( - (scope) => !scope.includes('userinfo.email') && !scope.includes('userinfo.profile') - ) - return filtered.sort((a, b) => { - const aIsNew = newScopesSet.has(a) - const bIsNew = newScopesSet.has(b) - if (aIsNew && !bIsNew) return -1 - if (!aIsNew && bIsNew) return 1 - return 0 - }) - }, [requiredScopes, newScopesSet]) - - const handleConnectDirectly = async () => { - setError(null) - - try { - if (onConnect) { - await onConnect() - onClose() - return - } - - const providerId = getProviderIdFromServiceId(serviceId) - - logger.info('Linking OAuth2:', { - providerId, - requiredScopes, - hasNewScopes: newScopes.length > 0, - }) - - if (providerId === 'trello') { - onClose() - window.location.href = '/api/auth/trello/authorize' - return - } - - if (providerId === 'shopify') { - onClose() - const returnUrl = encodeURIComponent(window.location.href) - window.location.href = `/api/auth/shopify/authorize?returnUrl=${returnUrl}` - return - } - - await client.oauth2.link({ - providerId, - callbackURL: window.location.href, - }) - onClose() - } catch (err) { - logger.error('Error initiating OAuth flow:', { error: err }) - setError('Failed to connect. Please try again.') - } - } - - return ( - !open && onClose()}> - - Connect {providerName} - -
-
-
- -
-
-

- Connect your {providerName} account -

-

- The "{toolName}" tool requires access to your account -

-
-
- - {displayScopes.length > 0 && ( -
-
-

- Permissions requested -

-
-
    - {displayScopes.map((scope) => ( -
  • -
    - -
    -
    - {getScopeDescription(scope)} - {newScopesSet.has(scope) && ( - - New - - )} -
    -
  • - ))} -
-
- )} - - {error &&

{error}

} -
-
- - - - -
-
- ) -} diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/credential-selector/credential-selector.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/credential-selector/credential-selector.tsx index 26c897cf3d3..fcdd82b37cb 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/credential-selector/credential-selector.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/credential-selector/credential-selector.tsx @@ -16,8 +16,7 @@ import { parseProvider, } from '@/lib/oauth' import { getMissingRequiredScopes } from '@/lib/oauth/utils' -import { ConnectCredentialModal } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/credential-selector/components/connect-credential-modal' -import { OAuthRequiredModal } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/credential-selector/components/oauth-required-modal' +import { OAuthModal } from '@/app/workspace/[workspaceId]/components/oauth-modal' import { useDependsOnGate } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-depends-on-gate' import { useSubBlockValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-sub-block-value' import type { SubBlockConfig } from '@/blocks/types' @@ -378,7 +377,8 @@ export function CredentialSelector({ )} {showConnectModal && ( - setShowConnectModal(false)} provider={provider} @@ -390,7 +390,8 @@ export function CredentialSelector({ )} {showOAuthModal && ( - { consumeOAuthReturnContext() diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tool-input/components/tools/credential-selector.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tool-input/components/tools/credential-selector.tsx index 8193f36ff9d..8758bec1557 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tool-input/components/tools/credential-selector.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tool-input/components/tools/credential-selector.tsx @@ -15,8 +15,7 @@ import { parseProvider, } from '@/lib/oauth' import { getMissingRequiredScopes } from '@/lib/oauth/utils' -import { ConnectCredentialModal } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/credential-selector/components/connect-credential-modal' -import { OAuthRequiredModal } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/credential-selector/components/oauth-required-modal' +import { OAuthModal } from '@/app/workspace/[workspaceId]/components/oauth-modal' import { useWorkspaceCredential } from '@/hooks/queries/credentials' import { useOAuthCredentials } from '@/hooks/queries/oauth/oauth-credentials' import { useWorkflowMap } from '@/hooks/queries/workflows' @@ -245,7 +244,8 @@ export function ToolCredentialSelector({ )} {showConnectModal && ( - setShowConnectModal(false)} provider={provider} @@ -257,7 +257,8 @@ export function ToolCredentialSelector({ )} {showOAuthModal && ( - { consumeOAuthReturnContext() diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx index 55b6d056705..5e9bf92463b 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx @@ -23,6 +23,7 @@ import { consumeOAuthReturnContext, writeOAuthReturnContext } from '@/lib/creden import type { OAuthProvider } from '@/lib/oauth' import { BLOCK_DIMENSIONS, CONTAINER_DIMENSIONS } from '@/lib/workflows/blocks/block-dimensions' import { TriggerUtils } from '@/lib/workflows/triggers/triggers' +import { OAuthModal } from '@/app/workspace/[workspaceId]/components/oauth-modal' import { useWorkspacePermissionsContext } from '@/app/workspace/[workspaceId]/providers/workspace-permissions-provider' import { CommandList, @@ -97,11 +98,6 @@ const LazyChat = lazy(() => default: mod.Chat, })) ) -const LazyOAuthRequiredModal = lazy(() => - import( - '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/credential-selector/components/oauth-required-modal' - ).then((mod) => ({ default: mod.OAuthRequiredModal })) -) const logger = createLogger('Workflow') @@ -4127,20 +4123,19 @@ const WorkflowContent = React.memo( {(!embedded || sandbox) && } {!embedded && !sandbox && oauthModal && ( - - { - consumeOAuthReturnContext() - setOauthModal(null) - }} - provider={oauthModal.provider} - toolName={oauthModal.providerName} - serviceId={oauthModal.serviceId} - requiredScopes={oauthModal.requiredScopes} - newScopes={oauthModal.newScopes} - /> - + { + consumeOAuthReturnContext() + setOauthModal(null) + }} + provider={oauthModal.provider} + toolName={oauthModal.providerName} + serviceId={oauthModal.serviceId} + requiredScopes={oauthModal.requiredScopes} + newScopes={oauthModal.newScopes} + /> )} )