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 fee66c7f94..faad342cba 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 @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useState } from 'react' +import { useCallback, useState } from 'react' import { Check, ChevronDown, Hash, Lock, RefreshCw } from 'lucide-react' import { SlackIcon } from '@/components/icons' import { Button } from '@/components/ui/button' @@ -11,6 +11,7 @@ import { CommandList, } from '@/components/ui/command' import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover' +import { useDisplayNamesStore } from '@/stores/display-names/store' export interface SlackChannelInfo { id: string @@ -41,9 +42,19 @@ export function SlackChannelSelector({ const [loading, setLoading] = useState(false) const [error, setError] = useState(null) const [open, setOpen] = useState(false) - const [selectedChannel, setSelectedChannel] = useState(null) const [initialFetchDone, setInitialFetchDone] = useState(false) + // Get cached display name + const cachedChannelName = useDisplayNamesStore( + useCallback( + (state) => { + if (!credential || !value) return null + return state.cache.channels[credential]?.[value] || null + }, + [credential, value] + ) + ) + // Fetch channels from Slack API const fetchChannels = useCallback(async () => { if (!credential) return @@ -76,6 +87,18 @@ export function SlackChannelSelector({ } else { setChannels(data.channels) setInitialFetchDone(true) + + // Cache channel names in display names store + if (credential) { + const channelMap = data.channels.reduce( + (acc: Record, ch: SlackChannelInfo) => { + acc[ch.id] = `#${ch.name}` + return acc + }, + {} + ) + useDisplayNamesStore.getState().setDisplayNames('channels', credential, channelMap) + } } } catch (err) { if ((err as Error).name === 'AbortError') return @@ -97,27 +120,7 @@ export function SlackChannelSelector({ } } - // Sync selected channel with value prop - useEffect(() => { - if (value && channels.length > 0) { - const channelInfo = channels.find((c) => c.id === value) - setSelectedChannel(channelInfo || null) - } else if (!value) { - setSelectedChannel(null) - } - }, [value, channels]) - - // If we have a value but no channel info and haven't fetched yet, get just that channel - useEffect(() => { - if (value && !selectedChannel && !loading && !initialFetchDone && credential) { - // For now, we'll fetch all channels when needed - // In the future, we could optimize to fetch just the selected channel - fetchChannels() - } - }, [value, selectedChannel, loading, initialFetchDone, credential, fetchChannels]) - const handleSelectChannel = (channel: SlackChannelInfo) => { - setSelectedChannel(channel) onChange(channel.id, channel) setOpen(false) } @@ -143,15 +146,10 @@ export function SlackChannelSelector({ >
- {selectedChannel ? ( - <> - {getChannelIcon(selectedChannel)} - {formatChannelName(selectedChannel)} - - ) : value ? ( + {cachedChannelName ? ( <> - {value} + {cachedChannelName} ) : ( {label} diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/credential-selector/credential-selector.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/credential-selector/credential-selector.tsx index 7d3a353a45..7575d08aa0 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/credential-selector/credential-selector.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/credential-selector/credential-selector.tsx @@ -27,6 +27,7 @@ import { useSubBlockValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/c import type { SubBlockConfig } from '@/blocks/types' import { useCollaborativeWorkflow } from '@/hooks/use-collaborative-workflow' import { getMissingRequiredScopes } from '@/hooks/use-oauth-scope-status' +import { useDisplayNamesStore } from '@/stores/display-names/store' import { useWorkflowRegistry } from '@/stores/workflows/registry/store' const logger = createLogger('CredentialSelector') @@ -116,6 +117,17 @@ export function CredentialSelector({ setHasForeignMeta(foreignMetaFound) setCredentials(creds) + // Cache credential names in display names store + if (effectiveProviderId) { + const credentialMap = creds.reduce((acc: Record, cred: Credential) => { + acc[cred.id] = cred.name + return acc + }, {}) + useDisplayNamesStore + .getState() + .setDisplayNames('credentials', effectiveProviderId, credentialMap) + } + // Do not auto-select or reset. We only show what's persisted. } } catch (error) { diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/document-selector/document-selector.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/document-selector/document-selector.tsx index 46adf16398..5d783d07fc 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/document-selector/document-selector.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/document-selector/document-selector.tsx @@ -1,6 +1,6 @@ 'use client' -import { useCallback, useEffect, useState } from 'react' +import { useCallback, useEffect, useMemo, useState } from 'react' import { Check, ChevronDown, FileText, RefreshCw } from 'lucide-react' import { Button } from '@/components/ui/button' import { @@ -15,24 +15,8 @@ import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover import { useDependsOnGate } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/hooks/use-depends-on-gate' import { useSubBlockValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/hooks/use-sub-block-value' import type { SubBlockConfig } from '@/blocks/types' - -interface DocumentData { - id: string - knowledgeBaseId: string - filename: string - fileUrl: string - fileSize: number - mimeType: string - chunkCount: number - tokenCount: number - characterCount: number - processingStatus: string - processingStartedAt: Date | null - processingCompletedAt: Date | null - processingError: string | null - enabled: boolean - uploadedAt: Date -} +import { useDisplayNamesStore } from '@/stores/display-names/store' +import { type DocumentData, useKnowledgeStore } from '@/stores/knowledge/store' interface DocumentSelectorProps { blockId: string @@ -51,110 +35,107 @@ export function DocumentSelector({ isPreview = false, previewValue, }: DocumentSelectorProps) { - const [documents, setDocuments] = useState([]) const [error, setError] = useState(null) const [open, setOpen] = useState(false) - const [selectedDocument, setSelectedDocument] = useState(null) - const [loading, setLoading] = useState(false) - // Use the proper hook to get the current value and setter const [storeValue, setStoreValue] = useSubBlockValue(blockId, subBlock.id) - - // Get the knowledge base ID from the same block's knowledgeBaseId subblock const [knowledgeBaseId] = useSubBlockValue(blockId, 'knowledgeBaseId') + const normalizedKnowledgeBaseId = + typeof knowledgeBaseId === 'string' && knowledgeBaseId.trim().length > 0 + ? knowledgeBaseId + : null + + const documentsCache = useKnowledgeStore( + useCallback( + (state) => + normalizedKnowledgeBaseId ? state.documents[normalizedKnowledgeBaseId] : undefined, + [normalizedKnowledgeBaseId] + ) + ) + + const isDocumentsLoading = useKnowledgeStore( + useCallback( + (state) => + normalizedKnowledgeBaseId ? state.isDocumentsLoading(normalizedKnowledgeBaseId) : false, + [normalizedKnowledgeBaseId] + ) + ) + + const getDocuments = useKnowledgeStore((state) => state.getDocuments) - // Use preview value when in preview mode, otherwise use store value const value = isPreview ? previewValue : storeValue const { finalDisabled } = useDependsOnGate(blockId, subBlock, { disabled, isPreview }) const isDisabled = finalDisabled - // Fetch documents for the selected knowledge base - const fetchDocuments = useCallback(async () => { - if (!knowledgeBaseId) { - setDocuments([]) + const documents = useMemo(() => { + if (!documentsCache) return [] + return documentsCache.documents ?? [] + }, [documentsCache]) + + const loadDocuments = useCallback(async () => { + if (!normalizedKnowledgeBaseId) { setError('No knowledge base selected') return } - setLoading(true) setError(null) try { - const response = await fetch(`/api/knowledge/${knowledgeBaseId}/documents`) - - if (!response.ok) { - throw new Error(`Failed to fetch documents: ${response.statusText}`) - } + const fetchedDocuments = await getDocuments(normalizedKnowledgeBaseId) - const result = await response.json() + if (fetchedDocuments.length > 0) { + const documentMap = fetchedDocuments.reduce>((acc, doc) => { + acc[doc.id] = doc.filename + return acc + }, {}) - if (!result.success) { - throw new Error(result.error || 'Failed to fetch documents') + useDisplayNamesStore + .getState() + .setDisplayNames('documents', normalizedKnowledgeBaseId, documentMap) } - - const fetchedDocuments = result.data.documents || result.data || [] - setDocuments(fetchedDocuments) } catch (err) { - if ((err as Error).name === 'AbortError') return - setError((err as Error).message) - setDocuments([]) - } finally { - setLoading(false) + if (err instanceof Error && err.name === 'AbortError') return + setError(err instanceof Error ? err.message : 'Failed to fetch documents') } - }, [knowledgeBaseId]) + }, [normalizedKnowledgeBaseId, getDocuments]) - // Handle dropdown open/close - fetch documents when opening const handleOpenChange = (isOpen: boolean) => { - if (isPreview) return - if (isDisabled) return + if (isPreview || isDisabled) return setOpen(isOpen) - // Fetch fresh documents when opening the dropdown - if (isOpen) { - fetchDocuments() + if (isOpen && (!documentsCache || !documentsCache.documents.length)) { + void loadDocuments() } } - // Handle document selection const handleSelectDocument = (document: DocumentData) => { if (isPreview) return - setSelectedDocument(document) setStoreValue(document.id) onDocumentSelect?.(document.id) setOpen(false) } - // Sync selected document with value prop - useEffect(() => { - if (isDisabled) return - if (value && documents.length > 0) { - const docInfo = documents.find((doc) => doc.id === value) - setSelectedDocument(docInfo || null) - } else { - setSelectedDocument(null) - } - }, [value, documents, isDisabled]) - - // Reset documents when knowledge base changes useEffect(() => { - setDocuments([]) - setSelectedDocument(null) setError(null) - }, [knowledgeBaseId]) + }, [normalizedKnowledgeBaseId]) - // Fetch documents when knowledge base is available useEffect(() => { - if (knowledgeBaseId && !isPreview && !isDisabled) { - fetchDocuments() - } - }, [knowledgeBaseId, isPreview, isDisabled, fetchDocuments]) + if (!normalizedKnowledgeBaseId || documents.length === 0) return - const formatDocumentName = (document: DocumentData) => { - return document.filename - } + const documentMap = documents.reduce>((acc, doc) => { + acc[doc.id] = doc.filename + return acc + }, {}) + + useDisplayNamesStore + .getState() + .setDisplayNames('documents', normalizedKnowledgeBaseId as string, documentMap) + }, [documents, normalizedKnowledgeBaseId]) + + const formatDocumentName = (document: DocumentData) => document.filename const getDocumentDescription = (document: DocumentData) => { const statusMap: Record = { @@ -171,6 +152,18 @@ export function DocumentSelector({ } const label = subBlock.placeholder || 'Select document' + const isLoading = isDocumentsLoading && !error + + // Always use cached display name + const displayName = useDisplayNamesStore( + useCallback( + (state) => { + if (!normalizedKnowledgeBaseId || !value || typeof value !== 'string') return null + return state.cache.documents[normalizedKnowledgeBaseId]?.[value] || null + }, + [normalizedKnowledgeBaseId, value] + ) + ) return (
@@ -185,8 +178,8 @@ export function DocumentSelector({ >
- {selectedDocument ? ( - {formatDocumentName(selectedDocument)} + {displayName ? ( + {displayName} ) : ( {label} )} @@ -199,7 +192,7 @@ export function DocumentSelector({ - {loading ? ( + {isLoading ? (
Loading documents... @@ -208,7 +201,7 @@ export function DocumentSelector({

{error}

- ) : !knowledgeBaseId ? ( + ) : !normalizedKnowledgeBaseId ? (

No knowledge base selected

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 157fb04f08..dc496d7e50 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 @@ -21,6 +21,7 @@ import { type OAuthProvider, } from '@/lib/oauth' import { OAuthRequiredModal } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/credential-selector/components/oauth-required-modal' +import { useDisplayNamesStore } from '@/stores/display-names/store' const logger = createLogger('ConfluenceFileSelector') @@ -75,6 +76,18 @@ export function ConfluenceFileSelector({ const [showOAuthModal, setShowOAuthModal] = useState(false) const initialFetchRef = useRef(false) const [error, setError] = useState(null) + + // Get cached display name + const cachedFileName = useDisplayNamesStore( + useCallback( + (state) => { + const effectiveCredentialId = credentialId || selectedCredentialId + if (!effectiveCredentialId || !value) return null + return state.cache.files[effectiveCredentialId]?.[value] || null + }, + [credentialId, selectedCredentialId, value] + ) + ) // Keep internal credential in sync with prop (handles late arrival and BFCache restores) useEffect(() => { if (credentialId && credentialId !== selectedCredentialId) { @@ -306,7 +319,19 @@ export function ConfluenceFileSelector({ logger.info(`Received ${data.files?.length || 0} files from API`) setFiles(data.files || []) - // If we have a selected file ID, find the file info + // Cache file names in display names store + if (selectedCredentialId && data.files) { + const fileMap = data.files.reduce( + (acc: Record, file: ConfluenceFileInfo) => { + acc[file.id] = file.name + return acc + }, + {} + ) + useDisplayNamesStore.getState().setDisplayNames('files', selectedCredentialId, fileMap) + } + + // If we have a selected file ID, update state and notify parent if (selectedFileId) { const fileInfo = data.files.find((file: ConfluenceFileInfo) => file.id === selectedFileId) if (fileInfo) { @@ -354,21 +379,6 @@ export function ConfluenceFileSelector({ } } - // Fetch the selected page metadata once credentials and domain are ready or changed - useEffect(() => { - if (value && selectedCredentialId && !selectedFile && domain && domain.includes('.')) { - fetchPageInfo(value) - } - }, [ - value, - selectedCredentialId, - selectedFile, - domain, - fetchPageInfo, - workflowId, - isForeignCredential, - ]) - // Keep internal selectedFileId in sync with the value prop useEffect(() => { if (value !== selectedFileId) { @@ -376,7 +386,7 @@ export function ConfluenceFileSelector({ } }, [value]) - // Clear preview when value is cleared (e.g., collaborator cleared or domain change cascade) + // Clear callback when value is cleared useEffect(() => { if (!value) { setSelectedFile(null) @@ -403,7 +413,6 @@ export function ConfluenceFileSelector({ // Clear selection const handleClearSelection = () => { setSelectedFileId('') - setSelectedFile(null) onChange('', undefined) onFileInfoChange?.(null) } @@ -421,10 +430,10 @@ export function ConfluenceFileSelector({ disabled={disabled || !domain || isForeignCredential} >

- {selectedFile ? ( + {cachedFileName ? ( <> - {selectedFile.name} + {cachedFileName} ) : ( <> @@ -554,7 +563,6 @@ export function ConfluenceFileSelector({ )} - {/* File preview */} {showPreview && selectedFile && selectedFileId && selectedFile.id === selectedFileId && (
@@ -580,7 +588,7 @@ export function ConfluenceFileSelector({ )}
- {selectedFile.webViewLink ? ( + {selectedFile.webViewLink && ( Open in Confluence - ) : ( - <> )}
diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/file-selector/components/google-calendar-selector.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/file-selector/components/google-calendar-selector.tsx index cb01097132..06c6483652 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/file-selector/components/google-calendar-selector.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/file-selector/components/google-calendar-selector.tsx @@ -14,6 +14,7 @@ import { } from '@/components/ui/command' import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover' import { createLogger } from '@/lib/logs/console/logger' +import { useDisplayNamesStore } from '@/stores/display-names/store' const logger = createLogger('GoogleCalendarSelector') @@ -56,6 +57,17 @@ export function GoogleCalendarSelector({ const [error, setError] = useState(null) const [initialFetchDone, setInitialFetchDone] = useState(false) + // Get cached display name + const cachedCalendarName = useDisplayNamesStore( + useCallback( + (state) => { + if (!credentialId || !value) return null + return state.cache.files[credentialId]?.[value] || null + }, + [credentialId, value] + ) + ) + const fetchCalendarsFromAPI = useCallback(async (): Promise => { if (!credentialId) { throw new Error('Google Calendar account is required') @@ -87,15 +99,19 @@ export function GoogleCalendarSelector({ const calendars = await fetchCalendarsFromAPI() setCalendars(calendars) - const currentSelectedId = selectedCalendarId - if (currentSelectedId) { - const calendarInfo = calendars.find( - (calendar: GoogleCalendarInfo) => calendar.id === currentSelectedId - ) - if (calendarInfo) { - setSelectedCalendar(calendarInfo) - onCalendarInfoChange?.(calendarInfo) - } + // Cache calendar names + if (credentialId && calendars.length > 0) { + const calendarMap = calendars.reduce>((acc, cal) => { + acc[cal.id] = cal.summary + return acc + }, {}) + useDisplayNamesStore.getState().setDisplayNames('files', credentialId, calendarMap) + } + + // Update selected calendar if we have a value + if (selectedCalendarId && calendars.length > 0) { + const calendar = calendars.find((c) => c.id === selectedCalendarId) + setSelectedCalendar(calendar || null) } } catch (error) { logger.error('Error fetching calendars:', error) @@ -105,7 +121,7 @@ export function GoogleCalendarSelector({ setIsLoading(false) setInitialFetchDone(true) } - }, [fetchCalendarsFromAPI, selectedCalendarId, onCalendarInfoChange]) + }, [fetchCalendarsFromAPI, credentialId]) const handleOpenChange = (isOpen: boolean) => { setOpen(isOpen) @@ -115,67 +131,12 @@ export function GoogleCalendarSelector({ } } - const fetchSelectedCalendarInfo = useCallback(async () => { - if (!selectedCalendarId) return - - setIsLoading(true) - setError(null) - - try { - const calendars = await fetchCalendarsFromAPI() - - if (calendars.length > 0) { - const calendarInfo = calendars.find( - (calendar: GoogleCalendarInfo) => calendar.id === selectedCalendarId - ) - if (calendarInfo) { - setSelectedCalendar(calendarInfo) - onCalendarInfoChange?.(calendarInfo) - } - } - } catch (error) { - logger.error('Error fetching calendar info:', error) - setError((error as Error).message) - } finally { - setIsLoading(false) - } - }, [fetchCalendarsFromAPI, selectedCalendarId, onCalendarInfoChange]) - - // Fetch selected calendar info when component mounts or dependencies change - useEffect(() => { - if (value && credentialId && (!selectedCalendar || selectedCalendar.id !== value)) { - fetchSelectedCalendarInfo() - } - }, [value, credentialId, selectedCalendar, fetchSelectedCalendarInfo]) - - // Sync with external value + // Sync selected ID with external value useEffect(() => { if (value !== selectedCalendarId) { setSelectedCalendarId(value) - - // Find calendar info for the new value - if (value && calendars.length > 0) { - const calendarInfo = calendars.find((calendar) => calendar.id === value) - setSelectedCalendar(calendarInfo || null) - onCalendarInfoChange?.(calendarInfo || null) - } else if (value) { - // If we have a value but no calendar info, we might need to fetch it - if (!selectedCalendar || selectedCalendar.id !== value) { - fetchSelectedCalendarInfo() - } - } else { - setSelectedCalendar(null) - onCalendarInfoChange?.(null) - } } - }, [ - value, - calendars, - selectedCalendarId, - selectedCalendar, - fetchSelectedCalendarInfo, - onCalendarInfoChange, - ]) + }, [value, selectedCalendarId]) // Handle calendar selection const handleSelectCalendar = (calendar: GoogleCalendarInfo) => { @@ -189,7 +150,6 @@ export function GoogleCalendarSelector({ // Clear selection const handleClearSelection = () => { setSelectedCalendarId('') - setSelectedCalendar(null) onChange('', undefined) onCalendarInfoChange?.(null) setError(null) @@ -215,17 +175,10 @@ export function GoogleCalendarSelector({ disabled={disabled || !credentialId} >
- {selectedCalendar ? ( + {cachedCalendarName ? ( <> -
- - {getCalendarDisplayName(selectedCalendar)} - + + {cachedCalendarName} ) : ( <> @@ -298,7 +251,6 @@ export function GoogleCalendarSelector({ - {/* Calendar preview */} {showPreview && selectedCalendar && (
diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/file-selector/components/google-drive-picker.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/file-selector/components/google-drive-picker.tsx index 536beaefc0..e8ce75e172 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/file-selector/components/google-drive-picker.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/file-selector/components/google-drive-picker.tsx @@ -17,6 +17,7 @@ import { parseProvider, } from '@/lib/oauth' import { OAuthRequiredModal } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/credential-selector/components/oauth-required-modal' +import { useDisplayNamesStore } from '@/stores/display-names/store' const logger = createLogger('GoogleDrivePicker') @@ -100,6 +101,15 @@ export function GoogleDrivePicker({ if (response.ok) { const data = await response.json() setCredentials(data.credentials) + + const credentialMap = (data.credentials || []).reduce( + (acc: Record, cred: Credential) => { + acc[cred.id] = cred.name + return acc + }, + {} + ) + useDisplayNamesStore.getState().setDisplayNames('credentials', providerId, credentialMap) if (credentialId && !data.credentials.some((c: any) => c.id === credentialId)) { setSelectedCredentialId('') } 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 b6c9c106e9..534e2a8189 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 @@ -21,6 +21,7 @@ import { type OAuthProvider, } from '@/lib/oauth' import { OAuthRequiredModal } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/credential-selector/components/oauth-required-modal' +import { useDisplayNamesStore } from '@/stores/display-names/store' const logger = createLogger('JiraIssueSelector') @@ -78,6 +79,18 @@ export function JiraIssueSelector({ const [error, setError] = useState(null) const [cloudId, setCloudId] = useState(null) + // Get cached display name + const cachedIssueName = useDisplayNamesStore( + useCallback( + (state) => { + const effectiveCredentialId = credentialId || selectedCredentialId + if (!effectiveCredentialId || !value) return null + return state.cache.files[effectiveCredentialId]?.[value] || null + }, + [credentialId, selectedCredentialId, value] + ) + ) + // Keep local credential state in sync with persisted credentialId prop useEffect(() => { if (credentialId && credentialId !== selectedCredentialId) { @@ -224,8 +237,6 @@ export function JiraIssueSelector({ } catch (error) { logger.error('Error fetching issue info:', error) setError((error as Error).message) - // Clear selection on error to prevent infinite retry loops - setSelectedIssue(null) onIssueInfoChange?.(null) } finally { setIsLoading(false) @@ -342,7 +353,19 @@ export function JiraIssueSelector({ logger.info(`Received ${foundIssues.length} issues from API`) setIssues(foundIssues) - // If we have a selected issue ID, find the issue info + // Cache issue names in display names store + if (selectedCredentialId && foundIssues.length > 0) { + const issueMap = foundIssues.reduce( + (acc: Record, issue: JiraIssueInfo) => { + acc[issue.id] = issue.name + return acc + }, + {} + ) + useDisplayNamesStore.getState().setDisplayNames('files', selectedCredentialId, issueMap) + } + + // If we have a selected issue ID, update state and notify parent if (selectedIssueId) { const issueInfo = foundIssues.find((issue: JiraIssueInfo) => issue.id === selectedIssueId) if (issueInfo) { @@ -397,18 +420,6 @@ export function JiraIssueSelector({ } // Fetch selected issue metadata once credentials are ready or changed - useEffect(() => { - if ( - value && - selectedCredentialId && - domain && - domain.includes('.') && - (!selectedIssue || selectedIssue.id !== value) - ) { - fetchIssueInfo(value) - } - }, [value, selectedCredentialId, selectedIssue, domain, fetchIssueInfo]) - // Keep internal selectedIssueId in sync with the value prop useEffect(() => { if (value !== selectedIssueId) { @@ -422,7 +433,7 @@ export function JiraIssueSelector({ setError(null) onIssueInfoChange?.(null) } - }, [value]) + }, [value, onIssueInfoChange]) // Handle issue selection const handleSelectIssue = (issue: JiraIssueInfo) => { @@ -443,8 +454,7 @@ export function JiraIssueSelector({ // Clear selection const handleClearSelection = () => { setSelectedIssueId('') - setSelectedIssue(null) - setError(null) // Clear any existing errors + setError(null) onChange('', undefined) onIssueInfoChange?.(null) } @@ -462,10 +472,10 @@ export function JiraIssueSelector({ disabled={disabled || !domain || !selectedCredentialId || isForeignCredential} >
- {selectedIssue ? ( + {cachedIssueName ? ( <> - {selectedIssue.name} + {cachedIssueName} ) : ( <> @@ -595,7 +605,6 @@ export function JiraIssueSelector({ )} - {/* Issue preview */} {showPreview && selectedIssue && (
@@ -621,7 +630,7 @@ export function JiraIssueSelector({ )}
- {selectedIssue.webViewLink ? ( + {selectedIssue.webViewLink && ( Open in Jira - ) : ( - <> )}
diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/file-selector/components/microsoft-file-selector.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/file-selector/components/microsoft-file-selector.tsx index 699da7dea4..abed39499a 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/file-selector/components/microsoft-file-selector.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/file-selector/components/microsoft-file-selector.tsx @@ -24,6 +24,7 @@ import { parseProvider, } from '@/lib/oauth' import { OAuthRequiredModal } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/credential-selector/components/oauth-required-modal' +import { useDisplayNamesStore } from '@/stores/display-names/store' import type { PlannerTask } from '@/tools/microsoft_planner/types' const logger = createLogger('MicrosoftFileSelector') @@ -90,14 +91,24 @@ export function MicrosoftFileSelector({ const [showOAuthModal, setShowOAuthModal] = useState(false) const [credentialsLoaded, setCredentialsLoaded] = useState(false) const initialFetchRef = useRef(false) - // Track the last (credentialId, fileId) we attempted to resolve to avoid tight retry loops const lastMetaAttemptRef = useRef('') - // Handle Microsoft Planner task selection const [plannerTasks, setPlannerTasks] = useState([]) const [isLoadingTasks, setIsLoadingTasks] = useState(false) const [selectedTask, setSelectedTask] = useState(null) + // Get cached display name + const cachedFileName = useDisplayNamesStore( + useCallback( + (state) => { + const effectiveCredentialId = credentialId || selectedCredentialId + if (!effectiveCredentialId || !value) return null + return state.cache.files[effectiveCredentialId]?.[value] || null + }, + [credentialId, selectedCredentialId, value] + ) + ) + // Determine the appropriate service ID based on provider and scopes const getServiceId = (): string => { if (serviceId) return serviceId @@ -179,6 +190,18 @@ export function MicrosoftFileSelector({ if (response.ok) { const data = await response.json() setAvailableFiles(data.files || []) + + // Cache file names in display names store + if (selectedCredentialId && data.files) { + const fileMap = data.files.reduce( + (acc: Record, file: MicrosoftFileInfo) => { + acc[file.id] = file.name + return acc + }, + {} + ) + useDisplayNamesStore.getState().setDisplayNames('files', selectedCredentialId, fileMap) + } } else { const txt = await response.text() if (response.status === 401 || response.status === 403) { @@ -472,11 +495,9 @@ export function MicrosoftFileSelector({ modifiedTime: task.createdDateTime, } - // Update internal state first to avoid race with list refetch setSelectedFileId(taskId) - setSelectedFile(taskAsFileInfo) setSelectedTask(task) - // Then propagate up + setSelectedFile(taskAsFileInfo) onChange(taskId, taskAsFileInfo) onFileInfoChange?.(taskAsFileInfo) setOpen(false) @@ -500,76 +521,22 @@ export function MicrosoftFileSelector({ if (!selectedCredentialId) { // No credentials - clear everything - if (selectedFile) { - setSelectedFile(null) - setSelectedFileId('') - onChange('') - } + setSelectedFileId('') + onChange('') // Reset memo when credential is cleared lastMetaAttemptRef.current = '' } else if (prevCredentialId && prevCredentialId !== selectedCredentialId) { - // Credentials changed (not initial load) - clear file info to force refetch - if (selectedFile) { - setSelectedFile(null) - } // Reset memo when switching credentials lastMetaAttemptRef.current = '' } - }, [selectedCredentialId, selectedFile, onChange]) + }, [selectedCredentialId, onChange]) - // Fetch the selected file metadata once credentials are loaded or changed + // Keep internal selectedFileId in sync with the value prop useEffect(() => { - // Fetch metadata when the external value doesn't match our current selectedFile - if ( - value && - selectedCredentialId && - credentialsLoaded && - (!selectedFile || selectedFile.id !== value) && - !isLoadingSelectedFile - ) { - // Avoid tight retry loops by memoizing the last attempt tuple - const attemptKey = `${selectedCredentialId}::${value}` - if (lastMetaAttemptRef.current === attemptKey) { - return - } - lastMetaAttemptRef.current = attemptKey - - if (serviceId === 'microsoft-planner') { - void fetchPlannerTaskById(value) - } else { - void fetchFileById(value) - } - } - }, [ - value, - selectedCredentialId, - credentialsLoaded, - selectedFile, - isLoadingSelectedFile, - fetchFileById, - fetchPlannerTaskById, - serviceId, - ]) - - // Resolve planner task selection for collaborators - useEffect(() => { - if ( - value && - selectedCredentialId && - credentialsLoaded && - !selectedTask && - serviceId === 'microsoft-planner' - ) { - void fetchPlannerTaskById(value) + if (value !== selectedFileId) { + setSelectedFileId(value) } - }, [ - value, - selectedCredentialId, - credentialsLoaded, - selectedTask, - serviceId, - fetchPlannerTaskById, - ]) + }, [value, selectedFileId]) // Handle selecting a file from the available files const handleFileSelect = (file: MicrosoftFileInfo) => { @@ -578,7 +545,7 @@ export function MicrosoftFileSelector({ onChange(file.id, file) onFileInfoChange?.(file) setOpen(false) - setSearchQuery('') // Clear search when file is selected + setSearchQuery('') } // Handle adding a new credential @@ -593,6 +560,7 @@ export function MicrosoftFileSelector({ const handleClearSelection = () => { setSelectedFileId('') setSelectedFile(null) + setSelectedTask(null) onChange('', undefined) onFileInfoChange?.(null) } @@ -787,15 +755,10 @@ export function MicrosoftFileSelector({ } >
- {canShowPreview ? ( + {cachedFileName ? ( <> - {getFileIcon(selectedFile, 'sm')} - {selectedFile.name} - - ) : selectedFileId && isLoadingSelectedFile && selectedCredentialId ? ( - <> - - Loading document... + {getProviderIcon(provider)} + {cachedFileName} ) : ( <> @@ -961,7 +924,6 @@ export function MicrosoftFileSelector({ )} - {/* File preview */} {canShowPreview && (
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 686e5e9f37..75e2d5713e 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 @@ -21,6 +21,7 @@ import { type OAuthProvider, } from '@/lib/oauth' import { OAuthRequiredModal } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/credential-selector/components/oauth-required-modal' +import { useDisplayNamesStore } from '@/stores/display-names/store' const logger = createLogger('TeamsMessageSelector') @@ -84,6 +85,17 @@ export function TeamsMessageSelector({ const [error, setError] = useState(null) const [selectionStage, setSelectionStage] = useState<'team' | 'channel' | 'chat'>(selectionType) + // Get cached display name + const cachedMessageName = useDisplayNamesStore( + useCallback( + (state) => { + if (!credential || !value) return null + return state.cache.files[credential]?.[value] || null + }, + [credential, value] + ) + ) + // Determine the appropriate service ID based on provider and scopes const getServiceId = (): string => { if (serviceId) return serviceId @@ -156,6 +168,15 @@ export function TeamsMessageSelector({ setTeams(teamsData) + // Cache team names in display names store + if (selectedCredentialId && teamsData.length > 0) { + const teamMap = teamsData.reduce((acc: Record, team: TeamsMessageInfo) => { + acc[team.id] = team.displayName + return acc + }, {}) + useDisplayNamesStore.getState().setDisplayNames('files', selectedCredentialId, teamMap) + } + // If we have a selected team ID, find it in the list if (selectedTeamId) { const team = teamsData.find((t: TeamsMessageInfo) => t.teamId === selectedTeamId) @@ -702,10 +723,10 @@ export function TeamsMessageSelector({ disabled={disabled || isForeignCredential} >
- {selectedMessage ? ( + {cachedMessageName ? ( <> - {selectedMessage.displayName} + {cachedMessageName} ) : ( <> 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 a71bf62d78..3eded742f9 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 @@ -1,7 +1,7 @@ 'use client' import { useCallback, useEffect, useRef, useState } from 'react' -import { Check, ChevronDown, RefreshCw, X } from 'lucide-react' +import { Check, ChevronDown, X } from 'lucide-react' import { WealthboxIcon } from '@/components/icons' import { Button } from '@/components/ui/button' import { @@ -20,6 +20,7 @@ import { type OAuthProvider, } from '@/lib/oauth' import { OAuthRequiredModal } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/credential-selector/components/oauth-required-modal' +import { useDisplayNamesStore } from '@/stores/display-names/store' const logger = createLogger('WealthboxFileSelector') @@ -73,6 +74,18 @@ export function WealthboxFileSelector({ const [credentialsLoaded, setCredentialsLoaded] = useState(false) const initialFetchRef = useRef(false) + // Get cached display name + const cachedItemName = useDisplayNamesStore( + useCallback( + (state) => { + const effectiveCredentialId = credentialId || selectedCredentialId + if (!effectiveCredentialId || !value) return null + return state.cache.files[effectiveCredentialId]?.[value] || null + }, + [credentialId, selectedCredentialId, value] + ) + ) + // Determine the appropriate service ID based on provider and scopes const getServiceId = (): string => { if (serviceId) return serviceId @@ -135,6 +148,18 @@ export function WealthboxFileSelector({ if (response.ok) { const data = await response.json() setAvailableItems(data.items || []) + + // Cache item names in display names store + if (selectedCredentialId && data.items) { + const itemMap = data.items.reduce( + (acc: Record, item: WealthboxItemInfo) => { + acc[item.id] = item.name + return acc + }, + {} + ) + useDisplayNamesStore.getState().setDisplayNames('files', selectedCredentialId, itemMap) + } } else { logger.error('Error fetching available items:', { error: await response.text(), @@ -209,26 +234,6 @@ export function WealthboxFileSelector({ }, [selectedCredentialId, open, fetchAvailableItems]) // Fetch the selected item metadata only once when needed - useEffect(() => { - if ( - value && - value !== selectedItemId && - selectedCredentialId && - credentialsLoaded && - !selectedItem && - !isLoadingSelectedItem - ) { - fetchItemById(value) - } - }, [ - value, - selectedItemId, - selectedCredentialId, - credentialsLoaded, - selectedItem, - isLoadingSelectedItem, - fetchItemById, - ]) // Handle search input changes with debouncing const handleSearchChange = useCallback( @@ -281,7 +286,6 @@ export function WealthboxFileSelector({ // Clear selection const handleClearSelection = () => { setSelectedItemId('') - setSelectedItem(null) onChange('', undefined) onFileInfoChange?.(null) } @@ -319,15 +323,10 @@ export function WealthboxFileSelector({ className='w-full justify-between' disabled={disabled} > - {selectedItem ? ( + {cachedItemName ? (
- {selectedItem.name} -
- ) : selectedItemId && isLoadingSelectedItem && selectedCredentialId ? ( -
- - Loading... + {cachedItemName}
) : (
diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/folder-selector/folder-selector.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/folder-selector/folder-selector.tsx index ee025e0319..925a40d3ee 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/folder-selector/folder-selector.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/folder-selector/folder-selector.tsx @@ -16,6 +16,7 @@ import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover import { createLogger } from '@/lib/logs/console/logger' import { type Credential, getProviderIdFromServiceId, getServiceIdFromScopes } from '@/lib/oauth' import { OAuthRequiredModal } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/credential-selector/components/oauth-required-modal' +import { useDisplayNamesStore } from '@/stores/display-names/store' const logger = createLogger('FolderSelector') @@ -65,12 +66,24 @@ export function FolderSelector({ credentialId || '' ) const [selectedFolderId, setSelectedFolderId] = useState('') - const [selectedFolder, setSelectedFolder] = useState(null) const [isLoading, setIsLoading] = useState(false) const [isLoadingSelectedFolder, setIsLoadingSelectedFolder] = useState(false) const [showOAuthModal, setShowOAuthModal] = useState(false) const initialFetchRef = useRef(false) + // Get cached display name + const cachedFolderName = useDisplayNamesStore( + useCallback( + (state) => { + const effectiveCredentialId = credentialId || selectedCredentialId + const effectiveValue = isPreview && previewValue !== undefined ? previewValue : value + if (!effectiveCredentialId || !effectiveValue) return null + return state.cache.folders[effectiveCredentialId]?.[effectiveValue] || null + }, + [credentialId, selectedCredentialId, value, isPreview, previewValue] + ) + ) + // Initialize selectedFolderId with the effective value useEffect(() => { if (isPreview && previewValue !== undefined) { @@ -168,7 +181,6 @@ export function FolderSelector({ messagesTotal: folder.totalItemCount, messagesUnread: folder.unreadItemCount, } - setSelectedFolder(folderInfo) onFolderInfoChange?.(folderInfo) return folderInfo } @@ -181,7 +193,6 @@ export function FolderSelector({ if (response.ok) { const data = await response.json() if (data.label) { - setSelectedFolder(data.label) onFolderInfoChange?.(data.label) return data.label } @@ -239,14 +250,27 @@ export function FolderSelector({ const folderList = provider === 'outlook' ? data.folders : data.labels setFolders(folderList || []) - // If we have a selected folder ID, find the folder info - if (selectedFolderId) { + // Cache folder names in display names store + if (selectedCredentialId && folderList) { + const folderMap = folderList.reduce( + (acc: Record, folder: FolderInfo) => { + acc[folder.id] = folder.name + return acc + }, + {} + ) + useDisplayNamesStore + .getState() + .setDisplayNames('folders', selectedCredentialId, folderMap) + } + + // Only notify parent if callback exists + if (selectedFolderId && onFolderInfoChange) { const folderInfo = folderList.find( (folder: FolderInfo) => folder.id === selectedFolderId ) if (folderInfo) { - setSelectedFolder(folderInfo) - onFolderInfoChange?.(folderInfo) + onFolderInfoChange(folderInfo) } else if (!searchQuery && provider !== 'outlook') { // Only try to fetch by ID for Gmail if this is not a search query // and we couldn't find the folder in the list @@ -303,33 +327,11 @@ export function FolderSelector({ if (currentValue !== selectedFolderId) { setSelectedFolderId(currentValue || '') } - }, [value, isPreview, previewValue, disabled]) - - // Fetch the selected folder metadata once credentials are ready or value changes - useEffect(() => { - if (disabled) return - const currentValue = isPreview ? (previewValue as string) : (value as string) - if ( - currentValue && - selectedCredentialId && - (!selectedFolder || selectedFolder.id !== currentValue) - ) { - fetchFolderById(currentValue) - } - }, [ - value, - selectedCredentialId, - selectedFolder, - fetchFolderById, - isPreview, - previewValue, - disabled, - ]) + }, [value, isPreview, previewValue, disabled, selectedFolderId]) // Handle folder selection const handleSelectFolder = (folder: FolderInfo) => { setSelectedFolderId(folder.id) - setSelectedFolder(folder) if (!isPreview) { onChange(folder.id, folder) } @@ -385,10 +387,10 @@ export function FolderSelector({ className='w-full justify-between' disabled={disabled || isForeignCredential} > - {selectedFolder ? ( + {cachedFolderName ? (
{getFolderIcon('sm')} - {selectedFolder.name} + {cachedFolderName}
) : (
diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/knowledge-base-selector/knowledge-base-selector.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/knowledge-base-selector/knowledge-base-selector.tsx index ccc34ae339..1ce8b0184a 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/knowledge-base-selector/knowledge-base-selector.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/knowledge-base-selector/knowledge-base-selector.tsx @@ -1,6 +1,6 @@ 'use client' -import { useCallback, useEffect, useMemo, useState } from 'react' +import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { X } from 'lucide-react' import { useParams } from 'next/navigation' import { Combobox, type ComboboxOption } from '@/components/emcn/components/combobox/combobox' @@ -29,12 +29,14 @@ export function KnowledgeBaseSelector({ const params = useParams() const workspaceId = params.workspaceId as string - const { loadingKnowledgeBasesList } = useKnowledgeStore() + const knowledgeBasesList = useKnowledgeStore((state) => state.knowledgeBasesList) + const knowledgeBasesMap = useKnowledgeStore((state) => state.knowledgeBases) + const loadingKnowledgeBasesList = useKnowledgeStore((state) => state.loadingKnowledgeBasesList) + const getKnowledgeBasesList = useKnowledgeStore((state) => state.getKnowledgeBasesList) + const getKnowledgeBase = useKnowledgeStore((state) => state.getKnowledgeBase) - const [knowledgeBases, setKnowledgeBases] = useState([]) - const [loading, setLoading] = useState(false) const [error, setError] = useState(null) - const [initialFetchDone, setInitialFetchDone] = useState(false) + const hasRequestedListRef = useRef(false) // Use the proper hook to get the current value and setter - this prevents infinite loops const [storeValue, setStoreValue] = useSubBlockValue(blockId, subBlock.id) @@ -47,13 +49,24 @@ export function KnowledgeBaseSelector({ /** * Convert knowledge bases to combobox options format */ + const combinedKnowledgeBases = useMemo(() => { + const merged = new Map() + knowledgeBasesList.forEach((kb) => { + merged.set(kb.id, kb) + }) + Object.values(knowledgeBasesMap).forEach((kb) => { + merged.set(kb.id, kb) + }) + return Array.from(merged.values()) + }, [knowledgeBasesList, knowledgeBasesMap]) + const options = useMemo(() => { - return knowledgeBases.map((kb) => ({ + return combinedKnowledgeBases.map((kb) => ({ label: kb.name, value: kb.id, icon: PackageSearchIcon, })) - }, [knowledgeBases]) + }, [combinedKnowledgeBases]) /** * Parse value into array of selected IDs @@ -74,51 +87,18 @@ export function KnowledgeBaseSelector({ /** * Compute selected knowledge bases for tag display */ - const selectedKnowledgeBases = useMemo(() => { - if (selectedIds.length > 0 && knowledgeBases.length > 0) { - return knowledgeBases.filter((kb) => selectedIds.includes(kb.id)) - } - return [] - }, [selectedIds, knowledgeBases]) + const selectedKnowledgeBases = useMemo(() => { + if (selectedIds.length === 0) return [] - /** - * Fetch knowledge bases directly from API - */ - const fetchKnowledgeBases = useCallback(async () => { - setLoading(true) - setError(null) - - try { - const url = workspaceId ? `/api/knowledge?workspaceId=${workspaceId}` : '/api/knowledge' - const response = await fetch(url, { - headers: { - 'Content-Type': 'application/json', - }, - }) - - if (!response.ok) { - throw new Error( - `Failed to fetch knowledge bases: ${response.status} ${response.statusText}` - ) - } - - const result = await response.json() - - if (!result.success) { - throw new Error(result.error || 'Failed to fetch knowledge bases') - } + const lookup = new Map() + combinedKnowledgeBases.forEach((kb) => { + lookup.set(kb.id, kb) + }) - const data = result.data || [] - setKnowledgeBases(data) - setInitialFetchDone(true) - } catch (err) { - if ((err as Error).name === 'AbortError') return - setError((err as Error).message) - setKnowledgeBases([]) - } finally { - setLoading(false) - } - }, [workspaceId]) + return selectedIds + .map((id) => lookup.get(id)) + .filter((kb): kb is KnowledgeBaseData => Boolean(kb)) + }, [selectedIds, combinedKnowledgeBases]) /** * Handle single selection @@ -168,10 +148,39 @@ export function KnowledgeBaseSelector({ * Fetch knowledge bases on initial mount */ useEffect(() => { - if (!initialFetchDone && !loading && !isPreview) { - fetchKnowledgeBases() + if (hasRequestedListRef.current) return + + let cancelled = false + hasRequestedListRef.current = true + setError(null) + getKnowledgeBasesList(workspaceId).catch((err) => { + if (cancelled) return + setError(err instanceof Error ? err.message : 'Failed to load knowledge bases') + }) + + return () => { + cancelled = true } - }, [initialFetchDone, loading, isPreview, fetchKnowledgeBases]) + }, [workspaceId, getKnowledgeBasesList]) + + /** + * Ensure selected knowledge bases are cached + */ + useEffect(() => { + if (selectedIds.length === 0) return + + selectedIds.forEach((id) => { + const isKnown = + Boolean(knowledgeBasesMap[id]) || + knowledgeBasesList.some((knowledgeBase) => knowledgeBase.id === id) + + if (!isKnown) { + void getKnowledgeBase(id).catch(() => { + // Ignore fetch errors here; they will surface via display hooks if needed + }) + } + }) + }, [selectedIds, knowledgeBasesList, knowledgeBasesMap, getKnowledgeBase]) const label = subBlock.placeholder || (isMultiSelect ? 'Select knowledge bases' : 'Select knowledge base') @@ -212,7 +221,7 @@ export function KnowledgeBaseSelector({ onMultiSelectChange={handleMultiSelectChange} placeholder={label} disabled={disabled || isPreview} - isLoading={loading || loadingKnowledgeBasesList} + isLoading={loadingKnowledgeBasesList} error={error} />
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 9cd648443b..0640ab9778 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, ExternalLink, RefreshCw, X } from 'lucide-react' +import { Check, ChevronDown, RefreshCw } from 'lucide-react' import { JiraIcon } from '@/components/icons' import { Button } from '@/components/ui/button' import { @@ -21,6 +21,7 @@ import { type OAuthProvider, } from '@/lib/oauth' import { OAuthRequiredModal } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/credential-selector/components/oauth-required-modal' +import { useDisplayNamesStore } from '@/stores/display-names/store' const logger = createLogger('JiraProjectSelector') @@ -73,13 +74,24 @@ 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) const [error, setError] = useState(null) const [cloudId, setCloudId] = useState(null) + // Get cached display name + const cachedProjectName = useDisplayNamesStore( + useCallback( + (state) => { + const effectiveCredentialId = credentialId || selectedCredentialId + if (!effectiveCredentialId || !value) return null + return state.cache.projects[`jira-${effectiveCredentialId}`]?.[value] || null + }, + [credentialId, selectedCredentialId, value] + ) + ) + // Handle search with debounce const searchTimeoutRef = useRef(null) @@ -198,10 +210,8 @@ export function JiraProjectSelector({ } if (projectInfo) { - setSelectedProject(projectInfo) onProjectInfoChange?.(projectInfo) } else { - setSelectedProject(null) onProjectInfoChange?.(null) } } catch (error) { @@ -292,13 +302,26 @@ export function JiraProjectSelector({ logger.info(`Received ${foundProjects.length} projects from API`) setProjects(foundProjects) + // Cache project names in display names store + if (selectedCredentialId && foundProjects.length > 0) { + const projectMap = foundProjects.reduce( + (acc: Record, proj: JiraProjectInfo) => { + acc[proj.id] = proj.name + return acc + }, + {} + ) + useDisplayNamesStore + .getState() + .setDisplayNames('projects', `jira-${selectedCredentialId}`, projectMap) + } + // If we have a selected project ID, find the project info if (selectedProjectId) { const projectInfo = foundProjects.find( (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 @@ -337,26 +360,16 @@ export function JiraProjectSelector({ } }, [credentialId, selectedCredentialId]) - // Fetch the selected project metadata once credentials are ready or changed - useEffect(() => { - if (value && selectedCredentialId && domain && domain.includes('.')) { - if (!selectedProject || selectedProject.id !== value) { - fetchProjectInfo(value) - } - } - }, [value, selectedCredentialId, domain, fetchProjectInfo, selectedProject]) - // Keep internal selectedProjectId in sync with the value prop useEffect(() => { if (value !== selectedProjectId) { setSelectedProjectId(value) } - }, [value]) + }, [value, selectedProjectId]) - // Clear local preview when value is cleared remotely or via collaborator + // Clear callback when value is cleared useEffect(() => { if (!value) { - setSelectedProject(null) onProjectInfoChange?.(null) } }, [value, onProjectInfoChange]) @@ -373,7 +386,6 @@ export function JiraProjectSelector({ // Handle project selection const handleSelectProject = (project: JiraProjectInfo) => { setSelectedProjectId(project.id) - setSelectedProject(project) onChange(project.id, project) onProjectInfoChange?.(project) setOpen(false) @@ -389,14 +401,11 @@ export function JiraProjectSelector({ // Clear selection const handleClearSelection = () => { setSelectedProjectId('') - setSelectedProject(null) setError(null) onChange('', undefined) onProjectInfoChange?.(null) } - const canShowPreview = !!(showPreview && selectedProject && value && selectedProject.id === value) - return ( <>
@@ -409,15 +418,10 @@ export function JiraProjectSelector({ className='w-full justify-between' disabled={disabled || !domain || !selectedCredentialId || isForeignCredential} > - {canShowPreview ? ( -
- - {selectedProject.name} -
- ) : selectedProjectId ? ( + {cachedProjectName ? (
- {selectedProjectId} + {cachedProjectName}
) : (
@@ -554,55 +558,6 @@ export function JiraProjectSelector({ )} - - {/* Project preview */} - {canShowPreview && ( -
-
- -
-
-
- {selectedProject.avatarUrl ? ( - {selectedProject.name} - ) : ( - - )} -
-
-
-

{selectedProject.name}

- - {selectedProject.key} - -
- {selectedProject.url && ( - e.stopPropagation()} - > - Open in Jira - - - )} -
-
-
- )}
{showOAuthModal && ( diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/project-selector/components/linear-project-selector.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/project-selector/components/linear-project-selector.tsx index 7f4136a529..21fbdaada9 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/project-selector/components/linear-project-selector.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/project-selector/components/linear-project-selector.tsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from 'react' +import { useCallback, useEffect, useState } from 'react' import { Check, ChevronDown, RefreshCw } from 'lucide-react' import { LinearIcon } from '@/components/icons' import { Button } from '@/components/ui/button' @@ -11,6 +11,7 @@ import { CommandList, } from '@/components/ui/command' import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover' +import { useDisplayNamesStore } from '@/stores/display-names/store' export interface LinearProjectInfo { id: string @@ -40,7 +41,17 @@ export function LinearProjectSelector({ const [loading, setLoading] = useState(false) const [error, setError] = useState(null) const [open, setOpen] = useState(false) - const [selectedProject, setSelectedProject] = useState(null) + + // Get cached display name + const cachedProjectName = useDisplayNamesStore( + useCallback( + (state) => { + if (!credential || !value) return null + return state.cache.projects[`linear-${credential}`]?.[value] || null + }, + [credential, value] + ) + ) useEffect(() => { if (!credential || !teamId) return @@ -68,10 +79,18 @@ export function LinearProjectSelector({ } else { setProjects(data.projects) - // Find selected project info if we have a value - if (value) { - const projectInfo = data.projects.find((p: LinearProjectInfo) => p.id === value) - setSelectedProject(projectInfo || null) + // Cache project names in display names store + if (credential && data.projects) { + const projectMap = data.projects.reduce( + (acc: Record, proj: LinearProjectInfo) => { + acc[proj.id] = proj.name + return acc + }, + {} + ) + useDisplayNamesStore + .getState() + .setDisplayNames('projects', `linear-${credential}`, projectMap) } } }) @@ -84,18 +103,7 @@ export function LinearProjectSelector({ return () => controller.abort() }, [credential, teamId, value, workflowId]) - // Sync selected project with value prop - useEffect(() => { - if (value && projects.length > 0) { - const projectInfo = projects.find((p) => p.id === value) - setSelectedProject(projectInfo || null) - } else if (!value) { - setSelectedProject(null) - } - }, [value, projects]) - const handleSelectProject = (project: LinearProjectInfo) => { - setSelectedProject(project) onChange(project.id, project) setOpen(false) } @@ -114,10 +122,10 @@ export function LinearProjectSelector({ className='w-full justify-between' disabled={disabled || !credential || !teamId} > - {selectedProject ? ( + {cachedProjectName ? (
- {selectedProject.name} + {cachedProjectName}
) : (
diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/project-selector/components/linear-team-selector.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/project-selector/components/linear-team-selector.tsx index 1c6ca71155..e472476700 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/project-selector/components/linear-team-selector.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/project-selector/components/linear-team-selector.tsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from 'react' +import { useCallback, useEffect, useState } from 'react' import { Check, ChevronDown, RefreshCw } from 'lucide-react' import { LinearIcon } from '@/components/icons' import { Button } from '@/components/ui/button' @@ -11,6 +11,7 @@ import { CommandList, } from '@/components/ui/command' import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover' +import { useDisplayNamesStore } from '@/stores/display-names/store' export interface LinearTeamInfo { id: string @@ -39,7 +40,17 @@ export function LinearTeamSelector({ const [loading, setLoading] = useState(false) const [error, setError] = useState(null) const [open, setOpen] = useState(false) - const [selectedTeam, setSelectedTeam] = useState(null) + + // Get cached display name + const cachedTeamName = useDisplayNamesStore( + useCallback( + (state) => { + if (!credential || !value) return null + return state.cache.projects[`linear-${credential}`]?.[value] || null + }, + [credential, value] + ) + ) useEffect(() => { if (!credential) return @@ -64,10 +75,18 @@ export function LinearTeamSelector({ } else { setTeams(data.teams) - // Find selected team info if we have a value - if (value) { - const teamInfo = data.teams.find((t: LinearTeamInfo) => t.id === value) - setSelectedTeam(teamInfo || null) + // Cache team names in display names store + if (credential && data.teams) { + const teamMap = data.teams.reduce( + (acc: Record, team: LinearTeamInfo) => { + acc[team.id] = team.name + return acc + }, + {} + ) + useDisplayNamesStore + .getState() + .setDisplayNames('projects', `linear-${credential}`, teamMap) } } }) @@ -80,18 +99,7 @@ export function LinearTeamSelector({ return () => controller.abort() }, [credential, value, workflowId]) - // Sync selected team with value prop - useEffect(() => { - if (value && teams.length > 0) { - const teamInfo = teams.find((t) => t.id === value) - setSelectedTeam(teamInfo || null) - } else if (!value) { - setSelectedTeam(null) - } - }, [value, teams]) - const handleSelectTeam = (team: LinearTeamInfo) => { - setSelectedTeam(team) onChange(team.id, team) setOpen(false) } @@ -110,10 +118,10 @@ export function LinearTeamSelector({ className='w-full justify-between' disabled={disabled || !credential} > - {selectedTeam ? ( + {cachedTeamName ? (
- {selectedTeam.name} + {cachedTeamName}
) : (
diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/tool-input/components/tool-credential-selector.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/tool-input/components/tool-credential-selector.tsx index 5225cbc9a8..f014b09235 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/tool-input/components/tool-credential-selector.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/tool-input/components/tool-credential-selector.tsx @@ -21,6 +21,7 @@ import { } from '@/lib/oauth' import { OAuthRequiredModal } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/credential-selector/components/oauth-required-modal' import { getMissingRequiredScopes } from '@/hooks/use-oauth-scope-status' +import { useDisplayNamesStore } from '@/stores/display-names/store' import { useWorkflowRegistry } from '@/stores/workflows/registry/store' const logger = createLogger('ToolCredentialSelector') @@ -87,6 +88,18 @@ export function ToolCredentialSelector({ const data = await response.json() setCredentials(data.credentials || []) + // Cache credential names for block previews + if (provider) { + const credentialMap = (data.credentials || []).reduce( + (acc: Record, cred: Credential) => { + acc[cred.id] = cred.name + return acc + }, + {} + ) + useDisplayNamesStore.getState().setDisplayNames('credentials', provider, credentialMap) + } + if ( value && !(data.credentials || []).some((cred: Credential) => cred.id === value) && @@ -99,7 +112,19 @@ export function ToolCredentialSelector({ if (metaResp.ok) { const meta = await metaResp.json() if (meta.credentials?.length) { - setCredentials([meta.credentials[0], ...(data.credentials || [])]) + const combinedCredentials = [meta.credentials[0], ...(data.credentials || [])] + setCredentials(combinedCredentials) + + const credentialMap = combinedCredentials.reduce( + (acc: Record, cred: Credential) => { + acc[cred.id] = cred.name + return acc + }, + {} + ) + useDisplayNamesStore + .getState() + .setDisplayNames('credentials', provider, credentialMap) } } } catch { diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/workflow-block.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/workflow-block.tsx index b93e52481e..d617b8079a 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/workflow-block.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/workflow-block.tsx @@ -9,6 +9,8 @@ import { cn } from '@/lib/utils' import { useUserPermissionsContext } from '@/app/workspace/[workspaceId]/providers/workspace-permissions-provider' import type { SubBlockConfig } from '@/blocks/types' import { useCollaborativeWorkflow } from '@/hooks/use-collaborative-workflow' +import { useCredentialDisplay } from '@/hooks/use-credential-display' +import { useDisplayName } from '@/hooks/use-display-name' import { usePanelEditorStore } from '@/stores/panel-new/editor/store' import { useWorkflowRegistry } from '@/stores/workflows/registry/store' import { useSubBlockStore } from '@/stores/workflows/subblock/store' @@ -85,7 +87,6 @@ const isPlainObject = (value: unknown): value is Record => { const getDisplayValue = (value: unknown): string => { if (value == null || value === '') return '-' - // Handle table row arrays (from table component) if (isTableRowArray(value)) { const nonEmptyRows = value.filter((row) => { const cellValues = Object.values(row.cells) @@ -106,7 +107,6 @@ const getDisplayValue = (value: unknown): string => { return `${nonEmptyRows.length} rows` } - // Handle field format arrays (from input-format, response-format) if (isFieldFormatArray(value)) { const namedFields = value.filter((field) => field.name && field.name.trim() !== '') if (namedFields.length === 0) return '-' @@ -115,7 +115,6 @@ const getDisplayValue = (value: unknown): string => { return `${namedFields[0].name}, ${namedFields[1].name} +${namedFields.length - 2}` } - // Handle input mapping objects (from input-mapping component) if (isPlainObject(value)) { const entries = Object.entries(value).filter( ([, val]) => val !== null && val !== undefined && val !== '' @@ -134,19 +133,27 @@ const getDisplayValue = (value: unknown): string => { return entries.length > 2 ? `${preview} +${entries.length - 2}` : preview } - // Handle arrays of primitives if (Array.isArray(value)) { const nonEmptyItems = value.filter((item) => item !== null && item !== undefined && item !== '') if (nonEmptyItems.length === 0) return '-' - if (nonEmptyItems.length === 1) return String(nonEmptyItems[0]) - if (nonEmptyItems.length === 2) return `${nonEmptyItems[0]}, ${nonEmptyItems[1]}` - return `${nonEmptyItems[0]}, ${nonEmptyItems[1]} +${nonEmptyItems.length - 2}` + + const getItemDisplayValue = (item: unknown): string => { + if (typeof item === 'object' && item !== null) { + const obj = item as Record + return String(obj.title || obj.name || obj.label || obj.id || JSON.stringify(item)) + } + return String(item) + } + + if (nonEmptyItems.length === 1) return getItemDisplayValue(nonEmptyItems[0]) + if (nonEmptyItems.length === 2) { + return `${getItemDisplayValue(nonEmptyItems[0])}, ${getItemDisplayValue(nonEmptyItems[1])}` + } + return `${getItemDisplayValue(nonEmptyItems[0])}, ${getItemDisplayValue(nonEmptyItems[1])} +${nonEmptyItems.length - 2}` } - // Handle primitive values const stringValue = String(value) if (stringValue === '[object Object]') { - // Fallback for unhandled object types - try to show something useful try { const json = JSON.stringify(value) if (json.length <= 40) return json @@ -161,19 +168,97 @@ const getDisplayValue = (value: unknown): string => { /** * Renders a single subblock row with title and optional value. + * Automatically hydrates IDs to display names for all selector types. */ -const SubBlockRow = ({ title, value }: { title: string; value?: string }) => ( -
- - {title} - - {value !== undefined && ( - - {value} +const SubBlockRow = ({ + title, + value, + subBlock, + rawValue, + workspaceId, + allSubBlockValues, +}: { + title: string + value?: string + subBlock?: SubBlockConfig + rawValue?: unknown + workspaceId?: string + allSubBlockValues?: Record +}) => { + const getStringValue = useCallback( + (key?: string): string | undefined => { + if (!key || !allSubBlockValues) return undefined + const candidate = allSubBlockValues[key]?.value + return typeof candidate === 'string' && candidate.length > 0 ? candidate : undefined + }, + [allSubBlockValues] + ) + + const dependencyValues = useMemo(() => { + if (!subBlock?.dependsOn?.length) return {} + return subBlock.dependsOn.reduce>((accumulator, dependency) => { + const dependencyValue = getStringValue(dependency) + if (dependencyValue) { + accumulator[dependency] = dependencyValue + } + return accumulator + }, {}) + }, [getStringValue, subBlock?.dependsOn]) + + const { displayName: credentialName } = useCredentialDisplay( + subBlock?.type === 'oauth-input' && typeof rawValue === 'string' ? rawValue : undefined, + subBlock?.provider + ) + + const credentialId = dependencyValues.credential + const knowledgeBaseId = dependencyValues.knowledgeBaseId + + const dropdownLabel = useMemo(() => { + if (!subBlock || (subBlock.type !== 'dropdown' && subBlock.type !== 'combobox')) return null + if (!rawValue || typeof rawValue !== 'string') return null + + const options = typeof subBlock.options === 'function' ? subBlock.options() : subBlock.options + if (!options) return null + + const option = options.find((opt) => + typeof opt === 'string' ? opt === rawValue : opt.id === rawValue + ) + + if (!option) return null + return typeof option === 'string' ? option : option.label + }, [subBlock, rawValue]) + + const genericDisplayName = useDisplayName(subBlock, rawValue, { + workspaceId, + provider: subBlock?.provider, + credentialId: typeof credentialId === 'string' ? credentialId : undefined, + knowledgeBaseId: typeof knowledgeBaseId === 'string' ? knowledgeBaseId : undefined, + domain: getStringValue('domain'), + teamId: getStringValue('teamId'), + projectId: getStringValue('projectId'), + planId: getStringValue('planId'), + }) + + const isPasswordField = subBlock?.password === true + const maskedValue = isPasswordField && value && value !== '-' ? '•••' : null + const displayValue = maskedValue || credentialName || dropdownLabel || genericDisplayName || value + + return ( +
+ + {title} - )} -
-) + {displayValue !== undefined && ( + + {displayValue} + + )} +
+ ) +} export const WorkflowBlock = memo(function WorkflowBlock({ id, @@ -186,6 +271,7 @@ export const WorkflowBlock = memo(function WorkflowBlock({ const params = useParams() const currentWorkflowId = params.workflowId as string + const workspaceId = params.workspaceId as string const currentWorkflow = useCurrentWorkflow() const currentBlock = currentWorkflow.getBlockById(id) @@ -345,6 +431,7 @@ export const WorkflowBlock = memo(function WorkflowBlock({ const visibleSubBlocks = config.subBlocks.filter((block) => { if (block.hidden) return false + if (block.hideFromPreview) return false if (block.requiresFeature && !isTruthy(getEnv(block.requiresFeature))) { return false @@ -547,7 +634,6 @@ export const WorkflowBlock = memo(function WorkflowBlock({ typeof currentStoreBlock?.height === 'number' ? currentStoreBlock.height : undefined const prevWidth = 250 // fixed across the app for workflow blocks - // Only update store if something actually changed to prevent unnecessary reflows if (prevHeight !== calculatedHeight || prevWidth !== FIXED_WIDTH) { updateBlockLayoutMetrics(id, { width: FIXED_WIDTH, height: calculatedHeight }) updateNodeInternals(id) @@ -791,13 +877,20 @@ export const WorkflowBlock = memo(function WorkflowBlock({ /> )) : subBlockRows.map((row, rowIndex) => - row.map((subBlock) => ( - - )) + row.map((subBlock) => { + const rawValue = subBlockState[subBlock.id]?.value + return ( + + ) + }) )} {shouldShowDefaultHandles && }
diff --git a/apps/sim/blocks/types.ts b/apps/sim/blocks/types.ts index 847ca39157..75d4f4b690 100644 --- a/apps/sim/blocks/types.ts +++ b/apps/sim/blocks/types.ts @@ -144,6 +144,7 @@ export interface SubBlockConfig { showCopyButton?: boolean connectionDroppable?: boolean hidden?: boolean + hideFromPreview?: boolean // Hide this subblock from the workflow block preview requiresFeature?: string // Environment variable name that must be truthy for this subblock to be visible description?: string value?: (params: Record) => string diff --git a/apps/sim/hooks/use-credential-display.ts b/apps/sim/hooks/use-credential-display.ts new file mode 100644 index 0000000000..2f19fdc01b --- /dev/null +++ b/apps/sim/hooks/use-credential-display.ts @@ -0,0 +1,54 @@ +import { useCallback, useEffect, useState } from 'react' +import { useDisplayNamesStore } from '@/stores/display-names/store' + +/** + * Hook to get display name for a credential ID + * Automatically fetches if not cached + */ +export function useCredentialDisplay(credentialId: string | undefined, provider?: string) { + const [isLoading, setIsLoading] = useState(false) + + // Select the actual cached value from the store (not just the getter) + // This ensures the component re-renders when the cache is populated + const displayName = useDisplayNamesStore( + useCallback( + (state) => { + if (!credentialId || !provider) return null + return state.cache.credentials[provider]?.[credentialId] || null + }, + [credentialId, provider] + ) + ) + + // Fetch if not cached + useEffect(() => { + if (!credentialId || !provider || displayName || isLoading) return + + setIsLoading(true) + fetch(`/api/auth/oauth/credentials?provider=${encodeURIComponent(provider)}`) + .then((res) => res.json()) + .then((data) => { + if (data.credentials) { + const credentialMap = data.credentials.reduce( + (acc: Record, cred: { id: string; name: string }) => { + acc[cred.id] = cred.name + return acc + }, + {} + ) + useDisplayNamesStore.getState().setDisplayNames('credentials', provider, credentialMap) + } + }) + .catch(() => { + // Silently fail + }) + .finally(() => { + setIsLoading(false) + }) + }, [credentialId, provider, displayName, isLoading]) + + return { + displayName, + isLoading, + } +} diff --git a/apps/sim/hooks/use-display-name.ts b/apps/sim/hooks/use-display-name.ts new file mode 100644 index 0000000000..b0d9ed9dad --- /dev/null +++ b/apps/sim/hooks/use-display-name.ts @@ -0,0 +1,555 @@ +import { useCallback, useEffect, useState } from 'react' +import type { SubBlockConfig } from '@/blocks/types' +import { useDisplayNamesStore } from '@/stores/display-names/store' +import { useKnowledgeStore } from '@/stores/knowledge/store' +import { useWorkflowRegistry } from '@/stores/workflows/registry/store' + +/** + * Generic hook to get display name for any selector value + * Automatically fetches if not cached + */ +export function useDisplayName( + subBlock: SubBlockConfig | undefined, + value: unknown, + context?: { + workspaceId?: string + credentialId?: string + provider?: string + knowledgeBaseId?: string + domain?: string + teamId?: string + projectId?: string + planId?: string + } +): string | null { + const getCachedKnowledgeBase = useKnowledgeStore((state) => state.getCachedKnowledgeBase) + const getKnowledgeBase = useKnowledgeStore((state) => state.getKnowledgeBase) + const getDocuments = useKnowledgeStore((state) => state.getDocuments) + const [isFetching, setIsFetching] = useState(false) + + const cachedDisplayName = useDisplayNamesStore( + useCallback( + (state) => { + if (!subBlock || !value || typeof value !== 'string') return null + + // Channels + if (subBlock.type === 'channel-selector' && context?.credentialId) { + return state.cache.channels[context.credentialId]?.[value] || null + } + + // Workflows + if (subBlock.id === 'workflowId') { + return state.cache.workflows.global?.[value] || null + } + + // Files + if (subBlock.type === 'file-selector' && context?.credentialId) { + return state.cache.files[context.credentialId]?.[value] || null + } + + // Folders + if (subBlock.type === 'folder-selector' && context?.credentialId) { + return state.cache.folders[context.credentialId]?.[value] || null + } + + // Projects + if (subBlock.type === 'project-selector' && context?.provider && context?.credentialId) { + const projectContext = `${context.provider}-${context.credentialId}` + return state.cache.projects[projectContext]?.[value] || null + } + + // Documents + if (subBlock.type === 'document-selector' && context?.knowledgeBaseId) { + return state.cache.documents[context.knowledgeBaseId]?.[value] || null + } + + return null + }, + [subBlock, value, context?.credentialId, context?.provider, context?.knowledgeBaseId] + ) + ) + + // Auto-fetch knowledge bases if needed + useEffect(() => { + if ( + subBlock?.type === 'knowledge-base-selector' && + typeof value === 'string' && + value && + !isFetching + ) { + const kb = getCachedKnowledgeBase(value) + if (!kb) { + setIsFetching(true) + getKnowledgeBase(value) + .catch(() => { + // Silently fail + }) + .finally(() => { + setIsFetching(false) + }) + } + } + }, [subBlock?.type, value, isFetching, getCachedKnowledgeBase, getKnowledgeBase]) + + // Auto-fetch documents if needed + useEffect(() => { + if ( + subBlock?.type === 'document-selector' && + context?.knowledgeBaseId && + typeof value === 'string' && + value && + !cachedDisplayName && + !isFetching + ) { + setIsFetching(true) + getDocuments(context.knowledgeBaseId) + .then((docs) => { + if (docs.length > 0) { + const documentMap = docs.reduce>((acc, doc) => { + acc[doc.id] = doc.filename + return acc + }, {}) + useDisplayNamesStore + .getState() + .setDisplayNames('documents', context.knowledgeBaseId!, documentMap) + } + }) + .catch(() => { + // Silently fail + }) + .finally(() => { + setIsFetching(false) + }) + } + }, [subBlock?.type, value, context?.knowledgeBaseId, cachedDisplayName, isFetching, getDocuments]) + + // Auto-fetch workflows if needed + useEffect(() => { + if (subBlock?.id !== 'workflowId' || typeof value !== 'string' || !value) return + if (cachedDisplayName || isFetching) return + + const workflows = useWorkflowRegistry.getState().workflows + if (!workflows[value]) return + + const workflowMap = Object.entries(workflows).reduce>( + (acc, [id, workflow]) => { + acc[id] = workflow.name || `Workflow ${id.slice(0, 8)}` + return acc + }, + {} + ) + + useDisplayNamesStore.getState().setDisplayNames('workflows', 'global', workflowMap) + }, [subBlock?.id, value, cachedDisplayName, isFetching]) + + // Auto-fetch channels if needed + useEffect(() => { + if (subBlock?.type !== 'channel-selector' || !context?.credentialId || !value) return + if (cachedDisplayName || isFetching) return + + setIsFetching(true) + fetch('/api/tools/slack/channels', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ credential: context.credentialId }), + }) + .then((res) => res.json()) + .then((data) => { + if (data.channels) { + const channelMap = data.channels.reduce( + (acc: Record, ch: { id: string; name: string }) => { + acc[ch.id] = ch.name + return acc + }, + {} + ) + useDisplayNamesStore + .getState() + .setDisplayNames('channels', context.credentialId!, channelMap) + } + }) + .catch(() => { + // Silently fail + }) + .finally(() => { + setIsFetching(false) + }) + }, [subBlock?.type, value, context?.credentialId, cachedDisplayName, isFetching]) + + // Auto-fetch folders if needed (Gmail/Outlook) + useEffect(() => { + if (subBlock?.type !== 'folder-selector' || !context?.credentialId || !value) return + if (cachedDisplayName || isFetching) return + + setIsFetching(true) + const provider = subBlock.provider || 'gmail' + const apiEndpoint = + provider === 'outlook' + ? `/api/tools/outlook/folders?credentialId=${context.credentialId}` + : `/api/tools/gmail/labels?credentialId=${context.credentialId}` + + fetch(apiEndpoint) + .then((res) => res.json()) + .then((data) => { + const folderList = provider === 'outlook' ? data.folders : data.labels + if (folderList) { + const folderMap = folderList.reduce( + (acc: Record, folder: { id: string; name: string }) => { + acc[folder.id] = folder.name + return acc + }, + {} + ) + useDisplayNamesStore + .getState() + .setDisplayNames('folders', context.credentialId!, folderMap) + } + }) + .catch(() => { + // Silently fail + }) + .finally(() => { + setIsFetching(false) + }) + }, [ + subBlock?.type, + subBlock?.provider, + value, + context?.credentialId, + cachedDisplayName, + isFetching, + ]) + + // Auto-fetch projects if needed (Jira, Linear) + useEffect(() => { + if ( + subBlock?.type !== 'project-selector' || + !context?.credentialId || + !context?.provider || + !value + ) + return + if (cachedDisplayName || isFetching) return + + const projectContext = `${context.provider}-${context.credentialId}` + setIsFetching(true) + + if (context.provider === 'jira' && context.domain) { + fetch('/api/tools/jira/projects', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ credentialId: context.credentialId, domain: context.domain }), + }) + .then((res) => res.json()) + .then((data) => { + if (data.projects) { + const projectMap = data.projects.reduce( + (acc: Record, proj: { id: string; name: string }) => { + acc[proj.id] = proj.name + return acc + }, + {} + ) + useDisplayNamesStore.getState().setDisplayNames('projects', projectContext, projectMap) + } + }) + .catch(() => {}) + .finally(() => setIsFetching(false)) + } else if (context.provider === 'linear' && context.teamId) { + fetch('/api/tools/linear/projects', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ credential: context.credentialId, teamId: context.teamId }), + }) + .then((res) => res.json()) + .then((data) => { + if (data.projects) { + const projectMap = data.projects.reduce( + (acc: Record, proj: { id: string; name: string }) => { + acc[proj.id] = proj.name + return acc + }, + {} + ) + useDisplayNamesStore.getState().setDisplayNames('projects', projectContext, projectMap) + } + }) + .catch(() => {}) + .finally(() => setIsFetching(false)) + } else { + setIsFetching(false) + } + }, [ + subBlock?.type, + value, + context?.credentialId, + context?.provider, + context?.domain, + context?.teamId, + cachedDisplayName, + isFetching, + ]) + + // Auto-fetch files if needed (provider-specific) + useEffect(() => { + if (subBlock?.type !== 'file-selector' || !context?.credentialId || !value) return + if (cachedDisplayName || isFetching) return + + setIsFetching(true) + const provider = subBlock.provider || context.provider + const serviceId = subBlock.serviceId + + // Google Calendar + if (provider === 'google-calendar') { + fetch(`/api/tools/google_calendar/calendars?credentialId=${context.credentialId}`) + .then((res) => res.json()) + .then((data) => { + if (data.calendars) { + const calendarMap = data.calendars.reduce( + (acc: Record, cal: { id: string; summary: string }) => { + acc[cal.id] = cal.summary + return acc + }, + {} + ) + useDisplayNamesStore + .getState() + .setDisplayNames('files', context.credentialId!, calendarMap) + } + }) + .catch(() => {}) + .finally(() => setIsFetching(false)) + } + // Jira issues + else if (provider === 'jira' && context.domain && context.projectId) { + fetch('/api/tools/jira/issues', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + credentialId: context.credentialId, + domain: context.domain, + projectId: context.projectId, + }), + }) + .then((res) => res.json()) + .then((data) => { + if (data.issues) { + const issueMap = data.issues.reduce( + (acc: Record, issue: { id: string; name: string }) => { + acc[issue.id] = issue.name + return acc + }, + {} + ) + useDisplayNamesStore + .getState() + .setDisplayNames('files', context.credentialId!, issueMap) + } + }) + .catch(() => {}) + .finally(() => setIsFetching(false)) + } + // Confluence pages + else if (provider === 'confluence' && context.domain) { + fetch('/api/tools/confluence/pages', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ credentialId: context.credentialId, domain: context.domain }), + }) + .then((res) => res.json()) + .then((data) => { + if (data.files) { + const fileMap = data.files.reduce( + (acc: Record, file: { id: string; name: string }) => { + acc[file.id] = file.name + return acc + }, + {} + ) + useDisplayNamesStore.getState().setDisplayNames('files', context.credentialId!, fileMap) + } + }) + .catch(() => {}) + .finally(() => setIsFetching(false)) + } + // Microsoft Teams + else if (provider === 'microsoft-teams' && context.teamId) { + fetch('/api/tools/microsoft_teams/teams', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ credentialId: context.credentialId }), + }) + .then((res) => res.json()) + .then((data) => { + if (data.teams) { + const teamMap = data.teams.reduce( + (acc: Record, team: { id: string; displayName: string }) => { + acc[team.id] = team.displayName + return acc + }, + {} + ) + useDisplayNamesStore.getState().setDisplayNames('files', context.credentialId!, teamMap) + } + }) + .catch(() => {}) + .finally(() => setIsFetching(false)) + } + // Wealthbox + else if (provider === 'wealthbox') { + fetch('/api/tools/wealthbox/contacts', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ credentialId: context.credentialId }), + }) + .then((res) => res.json()) + .then((data) => { + if (data.contacts) { + const contactMap = data.contacts.reduce( + (acc: Record, contact: { id: string; name: string }) => { + acc[contact.id] = contact.name + return acc + }, + {} + ) + useDisplayNamesStore + .getState() + .setDisplayNames('files', context.credentialId!, contactMap) + } + }) + .catch(() => {}) + .finally(() => setIsFetching(false)) + } + // OneDrive files + else if (serviceId === 'onedrive' && subBlock.mimeType === 'file') { + fetch(`/api/tools/onedrive/files?credentialId=${context.credentialId}`) + .then((res) => res.json()) + .then((data) => { + if (data.files) { + const fileMap = data.files.reduce( + (acc: Record, file: { id: string; name: string }) => { + acc[file.id] = file.name + return acc + }, + {} + ) + useDisplayNamesStore.getState().setDisplayNames('files', context.credentialId!, fileMap) + } + }) + .catch(() => {}) + .finally(() => setIsFetching(false)) + } + // OneDrive folders + else if (serviceId === 'onedrive' && subBlock.mimeType !== 'file') { + fetch(`/api/tools/onedrive/folders?credentialId=${context.credentialId}`) + .then((res) => res.json()) + .then((data) => { + if (data.files) { + const fileMap = data.files.reduce( + (acc: Record, file: { id: string; name: string }) => { + acc[file.id] = file.name + return acc + }, + {} + ) + useDisplayNamesStore.getState().setDisplayNames('files', context.credentialId!, fileMap) + } + }) + .catch(() => {}) + .finally(() => setIsFetching(false)) + } + // SharePoint sites + else if (serviceId === 'sharepoint') { + fetch(`/api/tools/sharepoint/sites?credentialId=${context.credentialId}`) + .then((res) => res.json()) + .then((data) => { + if (data.files) { + const fileMap = data.files.reduce( + (acc: Record, file: { id: string; name: string }) => { + acc[file.id] = file.name + return acc + }, + {} + ) + useDisplayNamesStore.getState().setDisplayNames('files', context.credentialId!, fileMap) + } + }) + .catch(() => {}) + .finally(() => setIsFetching(false)) + } + // Microsoft Excel/Word + else if (provider === 'microsoft-excel' || provider === 'microsoft-word') { + fetch(`/api/auth/oauth/microsoft/files?credentialId=${context.credentialId}`) + .then((res) => res.json()) + .then((data) => { + if (data.files) { + const fileMap = data.files.reduce( + (acc: Record, file: { id: string; name: string }) => { + acc[file.id] = file.name + return acc + }, + {} + ) + useDisplayNamesStore.getState().setDisplayNames('files', context.credentialId!, fileMap) + } + }) + .catch(() => {}) + .finally(() => setIsFetching(false)) + } + // Microsoft Planner tasks + else if (provider === 'microsoft-planner' && context.planId) { + fetch( + `/api/tools/microsoft_planner/tasks?credentialId=${context.credentialId}&planId=${context.planId}` + ) + .then((res) => res.json()) + .then((data) => { + if (data.tasks) { + const taskMap = data.tasks.reduce( + (acc: Record, task: { id: string; title: string }) => { + acc[task.id] = task.title + return acc + }, + {} + ) + useDisplayNamesStore.getState().setDisplayNames('files', context.credentialId!, taskMap) + } + }) + .catch(() => {}) + .finally(() => setIsFetching(false)) + } else { + setIsFetching(false) + } + }, [ + subBlock?.type, + subBlock?.provider, + subBlock?.serviceId, + subBlock?.mimeType, + value, + context?.credentialId, + context?.provider, + context?.domain, + context?.projectId, + context?.teamId, + context?.planId, + cachedDisplayName, + isFetching, + ]) + + if (!subBlock || !value || typeof value !== 'string') { + return null + } + + // Credentials - handled separately by useCredentialDisplay + if (subBlock.type === 'oauth-input') { + return null + } + + // Knowledge Bases - use existing knowledge store + if (subBlock.type === 'knowledge-base-selector') { + const kb = getCachedKnowledgeBase(value) + return kb?.name || null + } + + // Return the cached display name (which triggers re-render when populated) + return cachedDisplayName +} diff --git a/apps/sim/stores/display-names/store.ts b/apps/sim/stores/display-names/store.ts new file mode 100644 index 0000000000..a792e012d4 --- /dev/null +++ b/apps/sim/stores/display-names/store.ts @@ -0,0 +1,122 @@ +import { create } from 'zustand' +import { createLogger } from '@/lib/logs/console/logger' + +const logger = createLogger('DisplayNamesStore') + +/** + * Generic cache for ID-to-name mappings for all selector types + * Structure: { type: { context: { id: name } } } + * + */ +interface DisplayNamesCache { + credentials: Record> // provider -> id -> name + channels: Record> // credentialContext -> id -> name + knowledgeBases: Record> // workspaceId -> id -> name + workflows: Record> // always 'global' -> id -> name + files: Record> // credentialContext -> id -> name + folders: Record> // credentialContext -> id -> name + projects: Record> // provider-credential -> id -> name + documents: Record> // knowledgeBaseId -> id -> name +} + +interface DisplayNamesStore { + cache: DisplayNamesCache + + /** + * Set a display name for an ID + */ + setDisplayName: (type: keyof DisplayNamesCache, context: string, id: string, name: string) => void + + /** + * Set multiple display names at once + */ + setDisplayNames: ( + type: keyof DisplayNamesCache, + context: string, + items: Record + ) => void + + /** + * Get a display name for an ID + */ + getDisplayName: (type: keyof DisplayNamesCache, context: string, id: string) => string | null + + /** + * Clear all cached display names for a type/context + */ + clearContext: (type: keyof DisplayNamesCache, context: string) => void + + /** + * Clear all cached display names + */ + clearAll: () => void +} + +const initialCache: DisplayNamesCache = { + credentials: {}, + channels: {}, + knowledgeBases: {}, + workflows: {}, + files: {}, + folders: {}, + projects: {}, + documents: {}, +} + +export const useDisplayNamesStore = create((set, get) => ({ + cache: initialCache, + + setDisplayName: (type, context, id, name) => { + set((state) => ({ + cache: { + ...state.cache, + [type]: { + ...state.cache[type], + [context]: { + ...state.cache[type][context], + [id]: name, + }, + }, + }, + })) + }, + + setDisplayNames: (type, context, items) => { + set((state) => ({ + cache: { + ...state.cache, + [type]: { + ...state.cache[type], + [context]: { + ...state.cache[type][context], + ...items, + }, + }, + }, + })) + + logger.info(`Cached ${Object.keys(items).length} display names`, { type, context }) + }, + + getDisplayName: (type, context, id) => { + const contextCache = get().cache[type][context] + return contextCache?.[id] || null + }, + + clearContext: (type, context) => { + set((state) => { + const newTypeCache = { ...state.cache[type] } + delete newTypeCache[context] + return { + cache: { + ...state.cache, + [type]: newTypeCache, + }, + } + }) + }, + + clearAll: () => { + set({ cache: initialCache }) + }, +})) diff --git a/apps/sim/triggers/airtable/webhook.ts b/apps/sim/triggers/airtable/webhook.ts index 8c071ade0c..c79ca70ba9 100644 --- a/apps/sim/triggers/airtable/webhook.ts +++ b/apps/sim/triggers/airtable/webhook.ts @@ -50,6 +50,7 @@ export const airtableWebhookTrigger: TriggerConfig = { { id: 'triggerInstructions', title: 'Setup Instructions', + hideFromPreview: true, type: 'text', defaultValue: [ 'Connect your Airtable account using the "Select Airtable credential" button above.', @@ -70,6 +71,7 @@ export const airtableWebhookTrigger: TriggerConfig = { id: 'triggerSave', title: '', type: 'trigger-save', + hideFromPreview: true, mode: 'trigger', triggerId: 'airtable_webhook', }, diff --git a/apps/sim/triggers/generic/webhook.ts b/apps/sim/triggers/generic/webhook.ts index 2fb59d6d5f..7a4be9e1ad 100644 --- a/apps/sim/triggers/generic/webhook.ts +++ b/apps/sim/triggers/generic/webhook.ts @@ -59,6 +59,7 @@ export const genericWebhookTrigger: TriggerConfig = { { id: 'triggerInstructions', title: 'Setup Instructions', + hideFromPreview: true, type: 'text', defaultValue: [ 'Copy the webhook URL and use it in your external service or API.', @@ -79,6 +80,7 @@ export const genericWebhookTrigger: TriggerConfig = { id: 'triggerSave', title: '', type: 'trigger-save', + hideFromPreview: true, mode: 'trigger', triggerId: 'generic_webhook', }, diff --git a/apps/sim/triggers/github/issue_closed.ts b/apps/sim/triggers/github/issue_closed.ts index b4d1efeec5..3af72c1064 100644 --- a/apps/sim/triggers/github/issue_closed.ts +++ b/apps/sim/triggers/github/issue_closed.ts @@ -78,6 +78,7 @@ export const githubIssueClosedTrigger: TriggerConfig = { { id: 'triggerInstructions', title: 'Setup Instructions', + hideFromPreview: true, type: 'text', defaultValue: [ 'Go to your GitHub Repository > Settings > Webhooks.', @@ -104,6 +105,7 @@ export const githubIssueClosedTrigger: TriggerConfig = { id: 'triggerSave', title: '', type: 'trigger-save', + hideFromPreview: true, mode: 'trigger', triggerId: 'github_issue_closed', condition: { diff --git a/apps/sim/triggers/github/issue_comment.ts b/apps/sim/triggers/github/issue_comment.ts index fd88d591b6..d834f6185d 100644 --- a/apps/sim/triggers/github/issue_comment.ts +++ b/apps/sim/triggers/github/issue_comment.ts @@ -78,6 +78,7 @@ export const githubIssueCommentTrigger: TriggerConfig = { { id: 'triggerInstructions', title: 'Setup Instructions', + hideFromPreview: true, type: 'text', defaultValue: [ 'Go to your GitHub Repository > Settings > Webhooks.', @@ -105,6 +106,7 @@ export const githubIssueCommentTrigger: TriggerConfig = { id: 'triggerSave', title: '', type: 'trigger-save', + hideFromPreview: true, mode: 'trigger', triggerId: 'github_issue_comment', condition: { diff --git a/apps/sim/triggers/github/issue_opened.ts b/apps/sim/triggers/github/issue_opened.ts index 49f45dcad8..d5e71fe70f 100644 --- a/apps/sim/triggers/github/issue_opened.ts +++ b/apps/sim/triggers/github/issue_opened.ts @@ -99,6 +99,7 @@ export const githubIssueOpenedTrigger: TriggerConfig = { { id: 'triggerInstructions', title: 'Setup Instructions', + hideFromPreview: true, type: 'text', defaultValue: [ 'Go to your GitHub Repository > Settings > Webhooks.', @@ -125,6 +126,7 @@ export const githubIssueOpenedTrigger: TriggerConfig = { id: 'triggerSave', title: '', type: 'trigger-save', + hideFromPreview: true, mode: 'trigger', triggerId: 'github_issue_opened', condition: { diff --git a/apps/sim/triggers/github/pr_closed.ts b/apps/sim/triggers/github/pr_closed.ts index e652f05610..ad9a8013c7 100644 --- a/apps/sim/triggers/github/pr_closed.ts +++ b/apps/sim/triggers/github/pr_closed.ts @@ -79,6 +79,7 @@ export const githubPRClosedTrigger: TriggerConfig = { { id: 'triggerInstructions', title: 'Setup Instructions', + hideFromPreview: true, type: 'text', defaultValue: [ 'Go to your GitHub Repository > Settings > Webhooks.', @@ -105,6 +106,7 @@ export const githubPRClosedTrigger: TriggerConfig = { id: 'triggerSave', title: '', type: 'trigger-save', + hideFromPreview: true, mode: 'trigger', triggerId: 'github_pr_closed', condition: { diff --git a/apps/sim/triggers/github/pr_comment.ts b/apps/sim/triggers/github/pr_comment.ts index 04582a65ab..4cc968a170 100644 --- a/apps/sim/triggers/github/pr_comment.ts +++ b/apps/sim/triggers/github/pr_comment.ts @@ -78,6 +78,7 @@ export const githubPRCommentTrigger: TriggerConfig = { { id: 'triggerInstructions', title: 'Setup Instructions', + hideFromPreview: true, type: 'text', defaultValue: [ 'Go to your GitHub Repository > Settings > Webhooks.', @@ -105,6 +106,7 @@ export const githubPRCommentTrigger: TriggerConfig = { id: 'triggerSave', title: '', type: 'trigger-save', + hideFromPreview: true, mode: 'trigger', triggerId: 'github_pr_comment', condition: { diff --git a/apps/sim/triggers/github/pr_merged.ts b/apps/sim/triggers/github/pr_merged.ts index 2f20c90e97..1b0b0f9c76 100644 --- a/apps/sim/triggers/github/pr_merged.ts +++ b/apps/sim/triggers/github/pr_merged.ts @@ -78,6 +78,7 @@ export const githubPRMergedTrigger: TriggerConfig = { { id: 'triggerInstructions', title: 'Setup Instructions', + hideFromPreview: true, type: 'text', defaultValue: [ 'Go to your GitHub Repository > Settings > Webhooks.', @@ -104,6 +105,7 @@ export const githubPRMergedTrigger: TriggerConfig = { id: 'triggerSave', title: '', type: 'trigger-save', + hideFromPreview: true, mode: 'trigger', triggerId: 'github_pr_merged', condition: { diff --git a/apps/sim/triggers/github/pr_opened.ts b/apps/sim/triggers/github/pr_opened.ts index 03b523c769..6ea5741b20 100644 --- a/apps/sim/triggers/github/pr_opened.ts +++ b/apps/sim/triggers/github/pr_opened.ts @@ -78,6 +78,7 @@ export const githubPROpenedTrigger: TriggerConfig = { { id: 'triggerInstructions', title: 'Setup Instructions', + hideFromPreview: true, type: 'text', defaultValue: [ 'Go to your GitHub Repository > Settings > Webhooks.', @@ -104,6 +105,7 @@ export const githubPROpenedTrigger: TriggerConfig = { id: 'triggerSave', title: '', type: 'trigger-save', + hideFromPreview: true, mode: 'trigger', triggerId: 'github_pr_opened', condition: { diff --git a/apps/sim/triggers/github/pr_reviewed.ts b/apps/sim/triggers/github/pr_reviewed.ts index 76ade66d07..4c77a1add4 100644 --- a/apps/sim/triggers/github/pr_reviewed.ts +++ b/apps/sim/triggers/github/pr_reviewed.ts @@ -79,6 +79,7 @@ export const githubPRReviewedTrigger: TriggerConfig = { { id: 'triggerInstructions', title: 'Setup Instructions', + hideFromPreview: true, type: 'text', defaultValue: [ 'Go to your GitHub Repository > Settings > Webhooks.', @@ -105,6 +106,7 @@ export const githubPRReviewedTrigger: TriggerConfig = { id: 'triggerSave', title: '', type: 'trigger-save', + hideFromPreview: true, mode: 'trigger', triggerId: 'github_pr_reviewed', condition: { diff --git a/apps/sim/triggers/github/push.ts b/apps/sim/triggers/github/push.ts index 4d5df8e7f9..1dfae2dc39 100644 --- a/apps/sim/triggers/github/push.ts +++ b/apps/sim/triggers/github/push.ts @@ -78,6 +78,7 @@ export const githubPushTrigger: TriggerConfig = { { id: 'triggerInstructions', title: 'Setup Instructions', + hideFromPreview: true, type: 'text', defaultValue: [ 'Go to your GitHub Repository > Settings > Webhooks.', @@ -104,6 +105,7 @@ export const githubPushTrigger: TriggerConfig = { id: 'triggerSave', title: '', type: 'trigger-save', + hideFromPreview: true, mode: 'trigger', triggerId: 'github_push', condition: { diff --git a/apps/sim/triggers/github/release_published.ts b/apps/sim/triggers/github/release_published.ts index 8c3d5ef2d9..57def978cb 100644 --- a/apps/sim/triggers/github/release_published.ts +++ b/apps/sim/triggers/github/release_published.ts @@ -78,6 +78,7 @@ export const githubReleasePublishedTrigger: TriggerConfig = { { id: 'triggerInstructions', title: 'Setup Instructions', + hideFromPreview: true, type: 'text', defaultValue: [ 'Go to your GitHub Repository > Settings > Webhooks.', @@ -104,6 +105,7 @@ export const githubReleasePublishedTrigger: TriggerConfig = { id: 'triggerSave', title: '', type: 'trigger-save', + hideFromPreview: true, mode: 'trigger', triggerId: 'github_release_published', condition: { diff --git a/apps/sim/triggers/github/webhook.ts b/apps/sim/triggers/github/webhook.ts index 11022f8f66..c1e2b57452 100644 --- a/apps/sim/triggers/github/webhook.ts +++ b/apps/sim/triggers/github/webhook.ts @@ -75,6 +75,7 @@ export const githubWebhookTrigger: TriggerConfig = { { id: 'triggerInstructions', title: 'Setup Instructions', + hideFromPreview: true, type: 'text', defaultValue: [ 'Go to your GitHub Repository > Settings > Webhooks.', @@ -101,6 +102,7 @@ export const githubWebhookTrigger: TriggerConfig = { id: 'triggerSave', title: '', type: 'trigger-save', + hideFromPreview: true, mode: 'trigger', triggerId: 'github_webhook', condition: { diff --git a/apps/sim/triggers/github/workflow_run.ts b/apps/sim/triggers/github/workflow_run.ts index 9e6940ec0f..84c78bd707 100644 --- a/apps/sim/triggers/github/workflow_run.ts +++ b/apps/sim/triggers/github/workflow_run.ts @@ -79,6 +79,7 @@ export const githubWorkflowRunTrigger: TriggerConfig = { { id: 'triggerInstructions', title: 'Setup Instructions', + hideFromPreview: true, type: 'text', defaultValue: [ 'Go to your GitHub Repository > Settings > Webhooks.', @@ -105,6 +106,7 @@ export const githubWorkflowRunTrigger: TriggerConfig = { id: 'triggerSave', title: '', type: 'trigger-save', + hideFromPreview: true, mode: 'trigger', triggerId: 'github_workflow_run', condition: { diff --git a/apps/sim/triggers/gmail/poller.ts b/apps/sim/triggers/gmail/poller.ts index e8d6dcf2d5..0691944320 100644 --- a/apps/sim/triggers/gmail/poller.ts +++ b/apps/sim/triggers/gmail/poller.ts @@ -105,6 +105,7 @@ export const gmailPollingTrigger: TriggerConfig = { { id: 'triggerInstructions', title: 'Setup Instructions', + hideFromPreview: true, type: 'text', defaultValue: [ 'Connect your Gmail account using OAuth credentials', @@ -122,6 +123,7 @@ export const gmailPollingTrigger: TriggerConfig = { id: 'triggerSave', title: '', type: 'trigger-save', + hideFromPreview: true, mode: 'trigger', triggerId: 'gmail_poller', }, diff --git a/apps/sim/triggers/googleforms/webhook.ts b/apps/sim/triggers/googleforms/webhook.ts index 832bfcb8b9..7dd717098b 100644 --- a/apps/sim/triggers/googleforms/webhook.ts +++ b/apps/sim/triggers/googleforms/webhook.ts @@ -62,6 +62,7 @@ export const googleFormsWebhookTrigger: TriggerConfig = { { id: 'triggerInstructions', title: 'Setup Instructions', + hideFromPreview: true, type: 'text', defaultValue: [ 'Open your Google Form → More (⋮) → Script editor.', @@ -148,6 +149,7 @@ export const googleFormsWebhookTrigger: TriggerConfig = { id: 'triggerSave', title: '', type: 'trigger-save', + hideFromPreview: true, mode: 'trigger', triggerId: 'google_forms_webhook', }, diff --git a/apps/sim/triggers/index.ts b/apps/sim/triggers/index.ts index 7f508523e2..88e7653fdc 100644 --- a/apps/sim/triggers/index.ts +++ b/apps/sim/triggers/index.ts @@ -32,6 +32,7 @@ export function getTrigger(triggerId: string): TriggerConfig { readOnly: true, collapsible: true, defaultCollapsed: true, + hideFromPreview: true, mode: 'trigger', condition: { field: 'selectedTriggerId', diff --git a/apps/sim/triggers/jira/issue_commented.ts b/apps/sim/triggers/jira/issue_commented.ts index 05b1929c7d..6888fd3c76 100644 --- a/apps/sim/triggers/jira/issue_commented.ts +++ b/apps/sim/triggers/jira/issue_commented.ts @@ -59,6 +59,7 @@ export const jiraIssueCommentedTrigger: TriggerConfig = { { id: 'triggerInstructions', title: 'Setup Instructions', + hideFromPreview: true, type: 'text', defaultValue: jiraSetupInstructions('comment_created'), mode: 'trigger', @@ -71,6 +72,7 @@ export const jiraIssueCommentedTrigger: TriggerConfig = { id: 'triggerSave', title: '', type: 'trigger-save', + hideFromPreview: true, mode: 'trigger', triggerId: 'jira_issue_commented', condition: { diff --git a/apps/sim/triggers/jira/issue_created.ts b/apps/sim/triggers/jira/issue_created.ts index 54bcf8aa54..7c852e4036 100644 --- a/apps/sim/triggers/jira/issue_created.ts +++ b/apps/sim/triggers/jira/issue_created.ts @@ -68,6 +68,7 @@ export const jiraIssueCreatedTrigger: TriggerConfig = { { id: 'triggerInstructions', title: 'Setup Instructions', + hideFromPreview: true, type: 'text', defaultValue: jiraSetupInstructions('jira:issue_created'), mode: 'trigger', @@ -80,6 +81,7 @@ export const jiraIssueCreatedTrigger: TriggerConfig = { id: 'triggerSave', title: '', type: 'trigger-save', + hideFromPreview: true, mode: 'trigger', triggerId: 'jira_issue_created', condition: { diff --git a/apps/sim/triggers/jira/issue_deleted.ts b/apps/sim/triggers/jira/issue_deleted.ts index be9bd8aac8..3a8b1c7ffa 100644 --- a/apps/sim/triggers/jira/issue_deleted.ts +++ b/apps/sim/triggers/jira/issue_deleted.ts @@ -59,6 +59,7 @@ export const jiraIssueDeletedTrigger: TriggerConfig = { { id: 'triggerInstructions', title: 'Setup Instructions', + hideFromPreview: true, type: 'text', defaultValue: jiraSetupInstructions('jira:issue_deleted'), mode: 'trigger', @@ -71,6 +72,7 @@ export const jiraIssueDeletedTrigger: TriggerConfig = { id: 'triggerSave', title: '', type: 'trigger-save', + hideFromPreview: true, mode: 'trigger', triggerId: 'jira_issue_deleted', condition: { diff --git a/apps/sim/triggers/jira/issue_updated.ts b/apps/sim/triggers/jira/issue_updated.ts index e6b2767aeb..4dbe3ea889 100644 --- a/apps/sim/triggers/jira/issue_updated.ts +++ b/apps/sim/triggers/jira/issue_updated.ts @@ -73,6 +73,7 @@ export const jiraIssueUpdatedTrigger: TriggerConfig = { { id: 'triggerInstructions', title: 'Setup Instructions', + hideFromPreview: true, type: 'text', defaultValue: jiraSetupInstructions('jira:issue_updated'), mode: 'trigger', @@ -85,6 +86,7 @@ export const jiraIssueUpdatedTrigger: TriggerConfig = { id: 'triggerSave', title: '', type: 'trigger-save', + hideFromPreview: true, mode: 'trigger', triggerId: 'jira_issue_updated', condition: { diff --git a/apps/sim/triggers/jira/webhook.ts b/apps/sim/triggers/jira/webhook.ts index a4a9e8f57c..1e4fc862cc 100644 --- a/apps/sim/triggers/jira/webhook.ts +++ b/apps/sim/triggers/jira/webhook.ts @@ -46,6 +46,7 @@ export const jiraWebhookTrigger: TriggerConfig = { { id: 'triggerInstructions', title: 'Setup Instructions', + hideFromPreview: true, type: 'text', defaultValue: jiraSetupInstructions('All Events'), mode: 'trigger', @@ -58,6 +59,7 @@ export const jiraWebhookTrigger: TriggerConfig = { id: 'triggerSave', title: '', type: 'trigger-save', + hideFromPreview: true, mode: 'trigger', triggerId: 'jira_webhook', condition: { diff --git a/apps/sim/triggers/jira/worklog_created.ts b/apps/sim/triggers/jira/worklog_created.ts index 3cc46f14ea..3e9ed1f691 100644 --- a/apps/sim/triggers/jira/worklog_created.ts +++ b/apps/sim/triggers/jira/worklog_created.ts @@ -59,6 +59,7 @@ export const jiraWorklogCreatedTrigger: TriggerConfig = { { id: 'triggerInstructions', title: 'Setup Instructions', + hideFromPreview: true, type: 'text', defaultValue: jiraSetupInstructions('worklog_created'), mode: 'trigger', @@ -71,6 +72,7 @@ export const jiraWorklogCreatedTrigger: TriggerConfig = { id: 'triggerSave', title: '', type: 'trigger-save', + hideFromPreview: true, mode: 'trigger', triggerId: 'jira_worklog_created', condition: { diff --git a/apps/sim/triggers/linear/comment_created.ts b/apps/sim/triggers/linear/comment_created.ts index 9655a8a6c4..7b14d8e04a 100644 --- a/apps/sim/triggers/linear/comment_created.ts +++ b/apps/sim/triggers/linear/comment_created.ts @@ -42,6 +42,7 @@ export const linearCommentCreatedTrigger: TriggerConfig = { { id: 'triggerInstructions', title: 'Setup Instructions', + hideFromPreview: true, type: 'text', defaultValue: linearSetupInstructions('Comment (create)'), mode: 'trigger', @@ -54,6 +55,7 @@ export const linearCommentCreatedTrigger: TriggerConfig = { id: 'triggerSave', title: '', type: 'trigger-save', + hideFromPreview: true, mode: 'trigger', triggerId: 'linear_comment_created', condition: { diff --git a/apps/sim/triggers/linear/comment_updated.ts b/apps/sim/triggers/linear/comment_updated.ts index 3e9e7cad7f..f9e9e50a0e 100644 --- a/apps/sim/triggers/linear/comment_updated.ts +++ b/apps/sim/triggers/linear/comment_updated.ts @@ -42,6 +42,7 @@ export const linearCommentUpdatedTrigger: TriggerConfig = { { id: 'triggerInstructions', title: 'Setup Instructions', + hideFromPreview: true, type: 'text', defaultValue: linearSetupInstructions('Comment (update)'), mode: 'trigger', @@ -54,6 +55,7 @@ export const linearCommentUpdatedTrigger: TriggerConfig = { id: 'triggerSave', title: '', type: 'trigger-save', + hideFromPreview: true, mode: 'trigger', triggerId: 'linear_comment_updated', condition: { diff --git a/apps/sim/triggers/linear/customer_request_created.ts b/apps/sim/triggers/linear/customer_request_created.ts index a84c6f2564..c879f73ddf 100644 --- a/apps/sim/triggers/linear/customer_request_created.ts +++ b/apps/sim/triggers/linear/customer_request_created.ts @@ -42,6 +42,7 @@ export const linearCustomerRequestCreatedTrigger: TriggerConfig = { { id: 'triggerInstructions', title: 'Setup Instructions', + hideFromPreview: true, type: 'text', defaultValue: linearSetupInstructions('Customer Requests'), mode: 'trigger', @@ -54,6 +55,7 @@ export const linearCustomerRequestCreatedTrigger: TriggerConfig = { id: 'triggerSave', title: '', type: 'trigger-save', + hideFromPreview: true, mode: 'trigger', triggerId: 'linear_customer_request_created', condition: { diff --git a/apps/sim/triggers/linear/customer_request_updated.ts b/apps/sim/triggers/linear/customer_request_updated.ts index b1fddab272..5ca9d28fa3 100644 --- a/apps/sim/triggers/linear/customer_request_updated.ts +++ b/apps/sim/triggers/linear/customer_request_updated.ts @@ -42,6 +42,7 @@ export const linearCustomerRequestUpdatedTrigger: TriggerConfig = { { id: 'triggerInstructions', title: 'Setup Instructions', + hideFromPreview: true, type: 'text', defaultValue: linearSetupInstructions('CustomerNeed (update)'), mode: 'trigger', @@ -54,6 +55,7 @@ export const linearCustomerRequestUpdatedTrigger: TriggerConfig = { id: 'triggerSave', title: '', type: 'trigger-save', + hideFromPreview: true, mode: 'trigger', triggerId: 'linear_customer_request_updated', condition: { diff --git a/apps/sim/triggers/linear/cycle_created.ts b/apps/sim/triggers/linear/cycle_created.ts index 9347c7e54f..0d0a3396aa 100644 --- a/apps/sim/triggers/linear/cycle_created.ts +++ b/apps/sim/triggers/linear/cycle_created.ts @@ -42,6 +42,7 @@ export const linearCycleCreatedTrigger: TriggerConfig = { { id: 'triggerInstructions', title: 'Setup Instructions', + hideFromPreview: true, type: 'text', defaultValue: linearSetupInstructions('Cycle (create)'), mode: 'trigger', @@ -54,6 +55,7 @@ export const linearCycleCreatedTrigger: TriggerConfig = { id: 'triggerSave', title: '', type: 'trigger-save', + hideFromPreview: true, mode: 'trigger', triggerId: 'linear_cycle_created', condition: { diff --git a/apps/sim/triggers/linear/cycle_updated.ts b/apps/sim/triggers/linear/cycle_updated.ts index e48b4bdd6d..d9772e59a9 100644 --- a/apps/sim/triggers/linear/cycle_updated.ts +++ b/apps/sim/triggers/linear/cycle_updated.ts @@ -42,6 +42,7 @@ export const linearCycleUpdatedTrigger: TriggerConfig = { { id: 'triggerInstructions', title: 'Setup Instructions', + hideFromPreview: true, type: 'text', defaultValue: linearSetupInstructions('Cycle (update)'), mode: 'trigger', @@ -54,6 +55,7 @@ export const linearCycleUpdatedTrigger: TriggerConfig = { id: 'triggerSave', title: '', type: 'trigger-save', + hideFromPreview: true, mode: 'trigger', triggerId: 'linear_cycle_updated', condition: { diff --git a/apps/sim/triggers/linear/issue_created.ts b/apps/sim/triggers/linear/issue_created.ts index fea50aa4aa..857f89a482 100644 --- a/apps/sim/triggers/linear/issue_created.ts +++ b/apps/sim/triggers/linear/issue_created.ts @@ -51,6 +51,7 @@ export const linearIssueCreatedTrigger: TriggerConfig = { { id: 'triggerInstructions', title: 'Setup Instructions', + hideFromPreview: true, type: 'text', defaultValue: linearSetupInstructions('Issue (create)'), mode: 'trigger', @@ -63,6 +64,7 @@ export const linearIssueCreatedTrigger: TriggerConfig = { id: 'triggerSave', title: '', type: 'trigger-save', + hideFromPreview: true, mode: 'trigger', triggerId: 'linear_issue_created', condition: { diff --git a/apps/sim/triggers/linear/issue_removed.ts b/apps/sim/triggers/linear/issue_removed.ts index 2afde1a008..c864d6c4f0 100644 --- a/apps/sim/triggers/linear/issue_removed.ts +++ b/apps/sim/triggers/linear/issue_removed.ts @@ -42,6 +42,7 @@ export const linearIssueRemovedTrigger: TriggerConfig = { { id: 'triggerInstructions', title: 'Setup Instructions', + hideFromPreview: true, type: 'text', defaultValue: linearSetupInstructions('Issue (remove)'), mode: 'trigger', @@ -54,6 +55,7 @@ export const linearIssueRemovedTrigger: TriggerConfig = { id: 'triggerSave', title: '', type: 'trigger-save', + hideFromPreview: true, mode: 'trigger', triggerId: 'linear_issue_removed', condition: { diff --git a/apps/sim/triggers/linear/issue_updated.ts b/apps/sim/triggers/linear/issue_updated.ts index d963a5c9e1..50c36a3573 100644 --- a/apps/sim/triggers/linear/issue_updated.ts +++ b/apps/sim/triggers/linear/issue_updated.ts @@ -42,6 +42,7 @@ export const linearIssueUpdatedTrigger: TriggerConfig = { { id: 'triggerInstructions', title: 'Setup Instructions', + hideFromPreview: true, type: 'text', defaultValue: linearSetupInstructions('Issue (update)'), mode: 'trigger', @@ -54,6 +55,7 @@ export const linearIssueUpdatedTrigger: TriggerConfig = { id: 'triggerSave', title: '', type: 'trigger-save', + hideFromPreview: true, mode: 'trigger', triggerId: 'linear_issue_updated', condition: { diff --git a/apps/sim/triggers/linear/label_created.ts b/apps/sim/triggers/linear/label_created.ts index f82637788f..ac918d86e7 100644 --- a/apps/sim/triggers/linear/label_created.ts +++ b/apps/sim/triggers/linear/label_created.ts @@ -42,6 +42,7 @@ export const linearLabelCreatedTrigger: TriggerConfig = { { id: 'triggerInstructions', title: 'Setup Instructions', + hideFromPreview: true, type: 'text', defaultValue: linearSetupInstructions('IssueLabel (create)'), mode: 'trigger', @@ -54,6 +55,7 @@ export const linearLabelCreatedTrigger: TriggerConfig = { id: 'triggerSave', title: '', type: 'trigger-save', + hideFromPreview: true, mode: 'trigger', triggerId: 'linear_label_created', condition: { diff --git a/apps/sim/triggers/linear/label_updated.ts b/apps/sim/triggers/linear/label_updated.ts index b9fac41dd7..6315a8724e 100644 --- a/apps/sim/triggers/linear/label_updated.ts +++ b/apps/sim/triggers/linear/label_updated.ts @@ -42,6 +42,7 @@ export const linearLabelUpdatedTrigger: TriggerConfig = { { id: 'triggerInstructions', title: 'Setup Instructions', + hideFromPreview: true, type: 'text', defaultValue: linearSetupInstructions('IssueLabel (update)'), mode: 'trigger', @@ -54,6 +55,7 @@ export const linearLabelUpdatedTrigger: TriggerConfig = { id: 'triggerSave', title: '', type: 'trigger-save', + hideFromPreview: true, mode: 'trigger', triggerId: 'linear_label_updated', condition: { diff --git a/apps/sim/triggers/linear/project_created.ts b/apps/sim/triggers/linear/project_created.ts index 1ef8c7c9bb..fd0be51333 100644 --- a/apps/sim/triggers/linear/project_created.ts +++ b/apps/sim/triggers/linear/project_created.ts @@ -42,6 +42,7 @@ export const linearProjectCreatedTrigger: TriggerConfig = { { id: 'triggerInstructions', title: 'Setup Instructions', + hideFromPreview: true, type: 'text', defaultValue: linearSetupInstructions('Project (create)'), mode: 'trigger', @@ -54,6 +55,7 @@ export const linearProjectCreatedTrigger: TriggerConfig = { id: 'triggerSave', title: '', type: 'trigger-save', + hideFromPreview: true, mode: 'trigger', triggerId: 'linear_project_created', condition: { diff --git a/apps/sim/triggers/linear/project_update_created.ts b/apps/sim/triggers/linear/project_update_created.ts index 0f3bccca8d..a4b0a04fde 100644 --- a/apps/sim/triggers/linear/project_update_created.ts +++ b/apps/sim/triggers/linear/project_update_created.ts @@ -42,6 +42,7 @@ export const linearProjectUpdateCreatedTrigger: TriggerConfig = { { id: 'triggerInstructions', title: 'Setup Instructions', + hideFromPreview: true, type: 'text', defaultValue: linearSetupInstructions('ProjectUpdate (create)'), mode: 'trigger', @@ -54,6 +55,7 @@ export const linearProjectUpdateCreatedTrigger: TriggerConfig = { id: 'triggerSave', title: '', type: 'trigger-save', + hideFromPreview: true, mode: 'trigger', triggerId: 'linear_project_update_created', condition: { diff --git a/apps/sim/triggers/linear/project_updated.ts b/apps/sim/triggers/linear/project_updated.ts index 01db635e38..6cc017820b 100644 --- a/apps/sim/triggers/linear/project_updated.ts +++ b/apps/sim/triggers/linear/project_updated.ts @@ -42,6 +42,7 @@ export const linearProjectUpdatedTrigger: TriggerConfig = { { id: 'triggerInstructions', title: 'Setup Instructions', + hideFromPreview: true, type: 'text', defaultValue: linearSetupInstructions('Project (update)'), mode: 'trigger', @@ -54,6 +55,7 @@ export const linearProjectUpdatedTrigger: TriggerConfig = { id: 'triggerSave', title: '', type: 'trigger-save', + hideFromPreview: true, mode: 'trigger', triggerId: 'linear_project_updated', condition: { diff --git a/apps/sim/triggers/linear/webhook.ts b/apps/sim/triggers/linear/webhook.ts index 755c705529..5c9eda4b6a 100644 --- a/apps/sim/triggers/linear/webhook.ts +++ b/apps/sim/triggers/linear/webhook.ts @@ -42,6 +42,7 @@ export const linearWebhookTrigger: TriggerConfig = { { id: 'triggerInstructions', title: 'Setup Instructions', + hideFromPreview: true, type: 'text', defaultValue: linearSetupInstructions( 'all events', @@ -57,6 +58,7 @@ export const linearWebhookTrigger: TriggerConfig = { id: 'triggerSave', title: '', type: 'trigger-save', + hideFromPreview: true, mode: 'trigger', triggerId: 'linear_webhook', condition: { diff --git a/apps/sim/triggers/microsoftteams/chat_webhook.ts b/apps/sim/triggers/microsoftteams/chat_webhook.ts index fb037c3598..4ee16dbdaf 100644 --- a/apps/sim/triggers/microsoftteams/chat_webhook.ts +++ b/apps/sim/triggers/microsoftteams/chat_webhook.ts @@ -54,6 +54,7 @@ export const microsoftTeamsChatSubscriptionTrigger: TriggerConfig = { { id: 'triggerInstructions', title: 'Setup Instructions', + hideFromPreview: true, type: 'text', defaultValue: [ 'Connect your Microsoft Teams account and grant the required permissions.', @@ -75,6 +76,7 @@ export const microsoftTeamsChatSubscriptionTrigger: TriggerConfig = { id: 'triggerSave', title: '', type: 'trigger-save', + hideFromPreview: true, mode: 'trigger', triggerId: 'microsoftteams_chat_subscription', condition: { diff --git a/apps/sim/triggers/microsoftteams/webhook.ts b/apps/sim/triggers/microsoftteams/webhook.ts index 0b69d5e04d..b21a407dcc 100644 --- a/apps/sim/triggers/microsoftteams/webhook.ts +++ b/apps/sim/triggers/microsoftteams/webhook.ts @@ -54,6 +54,7 @@ export const microsoftTeamsWebhookTrigger: TriggerConfig = { { id: 'triggerInstructions', title: 'Setup Instructions', + hideFromPreview: true, type: 'text', defaultValue: [ 'Open Microsoft Teams and go to the team where you want to add the webhook.', @@ -79,6 +80,7 @@ export const microsoftTeamsWebhookTrigger: TriggerConfig = { id: 'triggerSave', title: '', type: 'trigger-save', + hideFromPreview: true, mode: 'trigger', triggerId: 'microsoftteams_webhook', condition: { diff --git a/apps/sim/triggers/outlook/poller.ts b/apps/sim/triggers/outlook/poller.ts index 908125e5eb..9163eaef1b 100644 --- a/apps/sim/triggers/outlook/poller.ts +++ b/apps/sim/triggers/outlook/poller.ts @@ -95,6 +95,7 @@ export const outlookPollingTrigger: TriggerConfig = { { id: 'triggerInstructions', title: 'Setup Instructions', + hideFromPreview: true, type: 'text', defaultValue: [ 'Connect your Microsoft account using OAuth credentials', @@ -112,6 +113,7 @@ export const outlookPollingTrigger: TriggerConfig = { id: 'triggerSave', title: '', type: 'trigger-save', + hideFromPreview: true, mode: 'trigger', triggerId: 'outlook_poller', }, diff --git a/apps/sim/triggers/slack/webhook.ts b/apps/sim/triggers/slack/webhook.ts index 9e3d2d9ab7..0a6f0cf1d8 100644 --- a/apps/sim/triggers/slack/webhook.ts +++ b/apps/sim/triggers/slack/webhook.ts @@ -48,12 +48,14 @@ export const slackWebhookTrigger: TriggerConfig = { `
${index + 1}. ${instruction}
` ) .join(''), + hideFromPreview: true, mode: 'trigger', }, { id: 'triggerSave', title: '', type: 'trigger-save', + hideFromPreview: true, mode: 'trigger', triggerId: 'slack_webhook', }, diff --git a/apps/sim/triggers/stripe/webhook.ts b/apps/sim/triggers/stripe/webhook.ts index c14848f03a..e4fabd2c6e 100644 --- a/apps/sim/triggers/stripe/webhook.ts +++ b/apps/sim/triggers/stripe/webhook.ts @@ -168,6 +168,7 @@ export const stripeWebhookTrigger: TriggerConfig = { { id: 'triggerInstructions', title: 'Setup Instructions', + hideFromPreview: true, type: 'text', defaultValue: [ 'Go to your Stripe Dashboard at https://dashboard.stripe.com/webhooks', @@ -190,6 +191,7 @@ export const stripeWebhookTrigger: TriggerConfig = { id: 'triggerSave', title: '', type: 'trigger-save', + hideFromPreview: true, mode: 'trigger', triggerId: 'stripe_webhook', }, diff --git a/apps/sim/triggers/telegram/webhook.ts b/apps/sim/triggers/telegram/webhook.ts index 2bf6e39792..fdf53a2623 100644 --- a/apps/sim/triggers/telegram/webhook.ts +++ b/apps/sim/triggers/telegram/webhook.ts @@ -33,6 +33,7 @@ export const telegramWebhookTrigger: TriggerConfig = { { id: 'triggerInstructions', title: 'Setup Instructions', + hideFromPreview: true, type: 'text', defaultValue: [ 'Message "/newbot" to @BotFather in Telegram to create a bot and copy its token.', @@ -50,6 +51,7 @@ export const telegramWebhookTrigger: TriggerConfig = { id: 'triggerSave', title: '', type: 'trigger-save', + hideFromPreview: true, mode: 'trigger', triggerId: 'telegram_webhook', }, diff --git a/apps/sim/triggers/twilio_voice/webhook.ts b/apps/sim/triggers/twilio_voice/webhook.ts index 9cae18e187..ef18acd5fe 100644 --- a/apps/sim/triggers/twilio_voice/webhook.ts +++ b/apps/sim/triggers/twilio_voice/webhook.ts @@ -52,6 +52,7 @@ export const twilioVoiceWebhookTrigger: TriggerConfig = { { id: 'triggerInstructions', title: 'Setup Instructions', + hideFromPreview: true, type: 'text', defaultValue: [ 'Enter a TwiML Response above - this tells Twilio what to do when a call comes in (e.g., play a message, record, gather input). Note: Use square brackets [Tag] instead of angle brackets for TwiML tags', @@ -72,6 +73,7 @@ export const twilioVoiceWebhookTrigger: TriggerConfig = { id: 'triggerSave', title: '', type: 'trigger-save', + hideFromPreview: true, mode: 'trigger', triggerId: 'twilio_voice_webhook', }, diff --git a/apps/sim/triggers/typeform/webhook.ts b/apps/sim/triggers/typeform/webhook.ts index 389c3fa039..34f1a94c04 100644 --- a/apps/sim/triggers/typeform/webhook.ts +++ b/apps/sim/triggers/typeform/webhook.ts @@ -64,6 +64,7 @@ export const typeformWebhookTrigger: TriggerConfig = { { id: 'triggerInstructions', title: 'Setup Instructions', + hideFromPreview: true, type: 'text', defaultValue: [ 'Get your Typeform Personal Access Token from https://admin.typeform.com/account#/section/tokens', @@ -84,6 +85,7 @@ export const typeformWebhookTrigger: TriggerConfig = { id: 'triggerSave', title: '', type: 'trigger-save', + hideFromPreview: true, mode: 'trigger', triggerId: 'typeform_webhook', }, diff --git a/apps/sim/triggers/webflow/collection_item_changed.ts b/apps/sim/triggers/webflow/collection_item_changed.ts index d86d7a8c09..0257dc7f4e 100644 --- a/apps/sim/triggers/webflow/collection_item_changed.ts +++ b/apps/sim/triggers/webflow/collection_item_changed.ts @@ -56,6 +56,7 @@ export const webflowCollectionItemChangedTrigger: TriggerConfig = { { id: 'triggerInstructions', title: 'Setup Instructions', + hideFromPreview: true, type: 'text', defaultValue: [ 'Connect your Webflow account using the "Select Webflow credential" button above.', @@ -80,6 +81,7 @@ export const webflowCollectionItemChangedTrigger: TriggerConfig = { id: 'triggerSave', title: '', type: 'trigger-save', + hideFromPreview: true, mode: 'trigger', triggerId: 'webflow_collection_item_changed', condition: { diff --git a/apps/sim/triggers/webflow/collection_item_created.ts b/apps/sim/triggers/webflow/collection_item_created.ts index 26187f8bd7..41538110dc 100644 --- a/apps/sim/triggers/webflow/collection_item_created.ts +++ b/apps/sim/triggers/webflow/collection_item_created.ts @@ -69,6 +69,7 @@ export const webflowCollectionItemCreatedTrigger: TriggerConfig = { { id: 'triggerInstructions', title: 'Setup Instructions', + hideFromPreview: true, type: 'text', defaultValue: [ 'Connect your Webflow account using the "Select Webflow credential" button above.', @@ -93,6 +94,7 @@ export const webflowCollectionItemCreatedTrigger: TriggerConfig = { id: 'triggerSave', title: '', type: 'trigger-save', + hideFromPreview: true, mode: 'trigger', triggerId: 'webflow_collection_item_created', condition: { diff --git a/apps/sim/triggers/webflow/collection_item_deleted.ts b/apps/sim/triggers/webflow/collection_item_deleted.ts index 424b1dc444..7743ee1ba3 100644 --- a/apps/sim/triggers/webflow/collection_item_deleted.ts +++ b/apps/sim/triggers/webflow/collection_item_deleted.ts @@ -56,6 +56,7 @@ export const webflowCollectionItemDeletedTrigger: TriggerConfig = { { id: 'triggerInstructions', title: 'Setup Instructions', + hideFromPreview: true, type: 'text', defaultValue: [ 'Connect your Webflow account using the "Select Webflow credential" button above.', @@ -81,6 +82,7 @@ export const webflowCollectionItemDeletedTrigger: TriggerConfig = { id: 'triggerSave', title: '', type: 'trigger-save', + hideFromPreview: true, mode: 'trigger', triggerId: 'webflow_collection_item_deleted', condition: { diff --git a/apps/sim/triggers/webflow/form_submission.ts b/apps/sim/triggers/webflow/form_submission.ts index 496c461fbf..0b48ae3617 100644 --- a/apps/sim/triggers/webflow/form_submission.ts +++ b/apps/sim/triggers/webflow/form_submission.ts @@ -43,6 +43,7 @@ export const webflowFormSubmissionTrigger: TriggerConfig = { { id: 'triggerInstructions', title: 'Setup Instructions', + hideFromPreview: true, type: 'text', defaultValue: [ 'Connect your Webflow account using the "Select Webflow credential" button above.', @@ -64,6 +65,7 @@ export const webflowFormSubmissionTrigger: TriggerConfig = { id: 'triggerSave', title: '', type: 'trigger-save', + hideFromPreview: true, mode: 'trigger', triggerId: 'webflow_form_submission', }, diff --git a/apps/sim/triggers/whatsapp/webhook.ts b/apps/sim/triggers/whatsapp/webhook.ts index 832ec0a39b..49a308d2e5 100644 --- a/apps/sim/triggers/whatsapp/webhook.ts +++ b/apps/sim/triggers/whatsapp/webhook.ts @@ -34,6 +34,7 @@ export const whatsappWebhookTrigger: TriggerConfig = { { id: 'triggerInstructions', title: 'Setup Instructions', + hideFromPreview: true, type: 'text', defaultValue: [ 'Go to your Meta for Developers Apps page and navigate to the "Build with us" --> "App Events" section.', @@ -56,6 +57,7 @@ export const whatsappWebhookTrigger: TriggerConfig = { id: 'triggerSave', title: '', type: 'trigger-save', + hideFromPreview: true, mode: 'trigger', triggerId: 'whatsapp_webhook', },