diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/channel-selector/components/slack-channel-selector.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/channel-selector/components/slack-channel-selector.tsx
index faad342cba..56190fb6cb 100644
--- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/channel-selector/components/slack-channel-selector.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/channel-selector/components/slack-channel-selector.tsx
@@ -147,10 +147,7 @@ export function SlackChannelSelector({
{cachedChannelName ? (
- <>
-
-
{cachedChannelName}
- >
+
{cachedChannelName}
) : (
{label}
)}
diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/file-selector/components/confluence-file-selector.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/file-selector/components/confluence-file-selector.tsx
index dc496d7e50..edc34632a4 100644
--- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/file-selector/components/confluence-file-selector.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/file-selector/components/confluence-file-selector.tsx
@@ -210,21 +210,23 @@ export function ConfluenceFileSelector({
}
const data = await response.json()
- if (data.file) {
- setSelectedFile(data.file)
- onFileInfoChange?.(data.file)
- } else {
- const fileInfo: ConfluenceFileInfo = {
- id: data.id || pageId,
- name: data.title || `Page ${pageId}`,
- mimeType: 'confluence/page',
- webViewLink: undefined,
- modifiedTime: undefined,
- spaceId: undefined,
- url: undefined,
- }
- setSelectedFile(fileInfo)
- onFileInfoChange?.(fileInfo)
+ const fileInfo: ConfluenceFileInfo = {
+ id: data.id || pageId,
+ name: data.title || `Page ${pageId}`,
+ mimeType: 'confluence/page',
+ webViewLink: `https://${domain}/wiki/pages/${data.id}`,
+ modifiedTime: data.version?.when,
+ spaceId: data.spaceId,
+ url: `https://${domain}/wiki/pages/${data.id}`,
+ }
+ setSelectedFile(fileInfo)
+ onFileInfoChange?.(fileInfo)
+
+ // Cache the page name in display names store
+ if (selectedCredentialId) {
+ useDisplayNamesStore
+ .getState()
+ .setDisplayNames('files', selectedCredentialId, { [fileInfo.id]: fileInfo.name })
}
} catch (error) {
logger.error('Error fetching page info:', error)
@@ -394,6 +396,13 @@ export function ConfluenceFileSelector({
}
}, [value, onFileInfoChange])
+ // Fetch page info on mount if we have a value but no selectedFile state
+ useEffect(() => {
+ if (value && selectedCredentialId && domain && !selectedFile) {
+ fetchPageInfo(value)
+ }
+ }, [value, selectedCredentialId, domain, selectedFile, fetchPageInfo])
+
// Handle file selection
const handleSelectFile = (file: ConfluenceFileInfo) => {
setSelectedFileId(file.id)
diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/file-selector/components/jira-issue-selector.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/file-selector/components/jira-issue-selector.tsx
index 534e2a8189..d7e2b26f3b 100644
--- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/file-selector/components/jira-issue-selector.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/file-selector/components/jira-issue-selector.tsx
@@ -435,6 +435,13 @@ export function JiraIssueSelector({
}
}, [value, onIssueInfoChange])
+ // Fetch issue info on mount if we have a value but no selectedIssue state
+ useEffect(() => {
+ if (value && selectedCredentialId && domain && projectId && !selectedIssue) {
+ fetchIssueInfo(value)
+ }
+ }, [value, selectedCredentialId, domain, projectId, selectedIssue, fetchIssueInfo])
+
// Handle issue selection
const handleSelectIssue = (issue: JiraIssueInfo) => {
setSelectedIssueId(issue.id)
diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/file-selector/components/teams-message-selector.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/file-selector/components/teams-message-selector.tsx
index 75e2d5713e..5e233911b7 100644
--- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/file-selector/components/teams-message-selector.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/file-selector/components/teams-message-selector.tsx
@@ -84,6 +84,7 @@ export function TeamsMessageSelector({
const initialFetchRef = useRef(false)
const [error, setError] = useState
(null)
const [selectionStage, setSelectionStage] = useState<'team' | 'channel' | 'chat'>(selectionType)
+ const lastRestoredValueRef = useRef(null)
// Get cached display name
const cachedMessageName = useDisplayNamesStore(
@@ -240,6 +241,18 @@ export function TeamsMessageSelector({
setChannels(channelsData)
+ // Cache channel names in display names store
+ if (selectedCredentialId && channelsData.length > 0) {
+ const channelMap = channelsData.reduce(
+ (acc: Record, channel: TeamsMessageInfo) => {
+ acc[channel.channelId!] = channel.displayName
+ return acc
+ },
+ {}
+ )
+ useDisplayNamesStore.getState().setDisplayNames('files', selectedCredentialId, channelMap)
+ }
+
// If we have a selected channel ID, find it in the list
if (selectedChannelId) {
const channel = channelsData.find(
@@ -304,6 +317,14 @@ export function TeamsMessageSelector({
setChats(chatsData)
+ if (selectedCredentialId && chatsData.length > 0) {
+ const chatMap = chatsData.reduce((acc: Record, chat: TeamsMessageInfo) => {
+ acc[chat.id] = chat.displayName
+ return acc
+ }, {})
+ useDisplayNamesStore.getState().setDisplayNames('files', selectedCredentialId, chatMap)
+ }
+
// If we have a selected chat ID, find it in the list
if (selectedChatId) {
const chat = chatsData.find((c: TeamsMessageInfo) => c.chatId === selectedChatId)
@@ -547,6 +568,19 @@ export function TeamsMessageSelector({
if (response.ok) {
const data = await response.json()
+
+ // Cache all chat names
+ if (data.chats && selectedCredentialId) {
+ const chatMap = data.chats.reduce(
+ (acc: Record, c: { id: string; displayName: string }) => {
+ acc[c.id] = c.displayName
+ return acc
+ },
+ {}
+ )
+ useDisplayNamesStore.getState().setDisplayNames('files', selectedCredentialId, chatMap)
+ }
+
const chat = data.chats.find((c: { id: string; displayName: string }) => c.id === chatId)
if (chat) {
const chatInfo: TeamsMessageInfo = {
@@ -691,14 +725,20 @@ export function TeamsMessageSelector({
// Restore selection whenever the canonical value changes
useEffect(() => {
if (value && selectedCredentialId) {
- if (selectionType === 'team') {
- restoreTeamSelection(value)
- } else if (selectionType === 'chat') {
- restoreChatSelection(value)
- } else if (selectionType === 'channel') {
- restoreChannelSelection(value)
+ // Only restore if we haven't already restored this value
+ if (lastRestoredValueRef.current !== value) {
+ lastRestoredValueRef.current = value
+
+ if (selectionType === 'team') {
+ restoreTeamSelection(value)
+ } else if (selectionType === 'chat') {
+ restoreChatSelection(value)
+ } else if (selectionType === 'channel') {
+ restoreChannelSelection(value)
+ }
}
} else {
+ lastRestoredValueRef.current = null
setSelectedMessage(null)
}
}, [
diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/file-selector/components/wealthbox-file-selector.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/file-selector/components/wealthbox-file-selector.tsx
index 3eded742f9..49ce0b9e2e 100644
--- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/file-selector/components/wealthbox-file-selector.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/file-selector/components/wealthbox-file-selector.tsx
@@ -194,6 +194,14 @@ export function WealthboxFileSelector({
if (data.item) {
setSelectedItem(data.item)
onFileInfoChange?.(data.item)
+
+ // Cache the item name in display names store
+ if (selectedCredentialId) {
+ useDisplayNamesStore
+ .getState()
+ .setDisplayNames('files', selectedCredentialId, { [data.item.id]: data.item.name })
+ }
+
return data.item
}
} else {
@@ -233,7 +241,20 @@ export function WealthboxFileSelector({
}
}, [selectedCredentialId, open, fetchAvailableItems])
- // Fetch the selected item metadata only once when needed
+ // Fetch item info on mount if we have a value but no selectedItem state
+ useEffect(() => {
+ if (value && selectedCredentialId && !selectedItem) {
+ fetchItemById(value)
+ }
+ }, [value, selectedCredentialId, selectedItem, fetchItemById])
+
+ // Clear selectedItem when value is cleared
+ useEffect(() => {
+ if (!value) {
+ setSelectedItem(null)
+ onFileInfoChange?.(null)
+ }
+ }, [value, onFileInfoChange])
// Handle search input changes with debouncing
const handleSearchChange = useCallback(
diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/project-selector/components/jira-project-selector.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/project-selector/components/jira-project-selector.tsx
index 0640ab9778..1f795eee50 100644
--- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/project-selector/components/jira-project-selector.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/project-selector/components/jira-project-selector.tsx
@@ -1,7 +1,7 @@
'use client'
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
-import { Check, ChevronDown, RefreshCw } from 'lucide-react'
+import { Check, ChevronDown, ExternalLink, RefreshCw, X } from 'lucide-react'
import { JiraIcon } from '@/components/icons'
import { Button } from '@/components/ui/button'
import {
@@ -74,6 +74,7 @@ export function JiraProjectSelector({
const [projects, setProjects] = useState([])
const [selectedCredentialId, setSelectedCredentialId] = useState(credentialId || '')
const [selectedProjectId, setSelectedProjectId] = useState(value)
+ const [selectedProject, setSelectedProject] = useState(null)
const [isLoading, setIsLoading] = useState(false)
const [showOAuthModal, setShowOAuthModal] = useState(false)
const initialFetchRef = useRef(false)
@@ -210,8 +211,10 @@ export function JiraProjectSelector({
}
if (projectInfo) {
+ setSelectedProject(projectInfo)
onProjectInfoChange?.(projectInfo)
} else {
+ setSelectedProject(null)
onProjectInfoChange?.(null)
}
} catch (error) {
@@ -322,6 +325,7 @@ export function JiraProjectSelector({
(project: JiraProjectInfo) => project.id === selectedProjectId
)
if (projectInfo) {
+ setSelectedProject(projectInfo)
onProjectInfoChange?.(projectInfo)
} else if (!searchQuery && selectedProjectId) {
// If we can't find the project in the list, try to fetch it directly
@@ -370,10 +374,18 @@ export function JiraProjectSelector({
// Clear callback when value is cleared
useEffect(() => {
if (!value) {
+ setSelectedProject(null)
onProjectInfoChange?.(null)
}
}, [value, onProjectInfoChange])
+ // Fetch project info on mount if we have a value but no selectedProject state
+ useEffect(() => {
+ if (value && selectedCredentialId && domain && !selectedProject) {
+ fetchProjectInfo(value)
+ }
+ }, [value, selectedCredentialId, domain, selectedProject, fetchProjectInfo])
+
// Handle open change
const handleOpenChange = (isOpen: boolean) => {
setOpen(isOpen)
@@ -386,6 +398,7 @@ export function JiraProjectSelector({
// Handle project selection
const handleSelectProject = (project: JiraProjectInfo) => {
setSelectedProjectId(project.id)
+ setSelectedProject(project)
onChange(project.id, project)
onProjectInfoChange?.(project)
setOpen(false)
@@ -401,6 +414,7 @@ export function JiraProjectSelector({
// Clear selection
const handleClearSelection = () => {
setSelectedProjectId('')
+ setSelectedProject(null)
setError(null)
onChange('', undefined)
onProjectInfoChange?.(null)
@@ -558,6 +572,55 @@ export function JiraProjectSelector({
)}
+
+ {/* Project preview */}
+ {showPreview && selectedProject && (
+
+
+
+
+
+
+ {selectedProject.avatarUrl ? (
+

+ ) : (
+
+ )}
+
+
+
+
+ )}
{showOAuthModal && (
diff --git a/apps/sim/hooks/use-display-name.ts b/apps/sim/hooks/use-display-name.ts
index fbe1eb0774..b4f49b3a11 100644
--- a/apps/sim/hooks/use-display-name.ts
+++ b/apps/sim/hooks/use-display-name.ts
@@ -234,23 +234,32 @@ export function useDisplayName(
const projectContext = `${context.provider}-${context.credentialId}`
setIsFetching(true)
- if (context.provider === 'jira' && context.domain) {
- fetch('/api/tools/jira/projects', {
+ if (context.provider === 'jira' && context.domain && context.credentialId) {
+ // Fetch access token then get project info
+ fetch('/api/auth/oauth/token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({ credentialId: context.credentialId, domain: context.domain }),
+ body: JSON.stringify({ credentialId: context.credentialId }),
})
+ .then((res) => res.json())
+ .then((tokenData) => {
+ if (!tokenData.accessToken) throw new Error('No access token')
+ return fetch('/api/tools/jira/projects', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({
+ domain: context.domain,
+ accessToken: tokenData.accessToken,
+ projectId: value,
+ }),
+ })
+ })
.then((res) => res.json())
.then((data) => {
- if (data.projects) {
- const projectMap = data.projects.reduce(
- (acc: Record