Skip to content

Commit 8273185

Browse files
improvement(display-names): move display name mapping to central store + access pattern via hook (#1881)
* improvmenet(display-names-store): add displaynames store to map ids to human readable names for creds, etc * fix workflow in workflow * dot protection for secrets * hide from preview certain fields * fix rest of cases * fix confluence * fix type errors * remove redundant workflow dropdown code * remove comments * revert preview card * fix [object Object] bug * fix lint
1 parent 991b0e3 commit 8273185

File tree

72 files changed

+1469
-576
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

72 files changed

+1469
-576
lines changed

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/channel-selector/components/slack-channel-selector.tsx

Lines changed: 27 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useCallback, useEffect, useState } from 'react'
1+
import { useCallback, useState } from 'react'
22
import { Check, ChevronDown, Hash, Lock, RefreshCw } from 'lucide-react'
33
import { SlackIcon } from '@/components/icons'
44
import { Button } from '@/components/ui/button'
@@ -11,6 +11,7 @@ import {
1111
CommandList,
1212
} from '@/components/ui/command'
1313
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'
14+
import { useDisplayNamesStore } from '@/stores/display-names/store'
1415

1516
export interface SlackChannelInfo {
1617
id: string
@@ -41,9 +42,19 @@ export function SlackChannelSelector({
4142
const [loading, setLoading] = useState(false)
4243
const [error, setError] = useState<string | null>(null)
4344
const [open, setOpen] = useState(false)
44-
const [selectedChannel, setSelectedChannel] = useState<SlackChannelInfo | null>(null)
4545
const [initialFetchDone, setInitialFetchDone] = useState(false)
4646

47+
// Get cached display name
48+
const cachedChannelName = useDisplayNamesStore(
49+
useCallback(
50+
(state) => {
51+
if (!credential || !value) return null
52+
return state.cache.channels[credential]?.[value] || null
53+
},
54+
[credential, value]
55+
)
56+
)
57+
4758
// Fetch channels from Slack API
4859
const fetchChannels = useCallback(async () => {
4960
if (!credential) return
@@ -76,6 +87,18 @@ export function SlackChannelSelector({
7687
} else {
7788
setChannels(data.channels)
7889
setInitialFetchDone(true)
90+
91+
// Cache channel names in display names store
92+
if (credential) {
93+
const channelMap = data.channels.reduce(
94+
(acc: Record<string, string>, ch: SlackChannelInfo) => {
95+
acc[ch.id] = `#${ch.name}`
96+
return acc
97+
},
98+
{}
99+
)
100+
useDisplayNamesStore.getState().setDisplayNames('channels', credential, channelMap)
101+
}
79102
}
80103
} catch (err) {
81104
if ((err as Error).name === 'AbortError') return
@@ -97,27 +120,7 @@ export function SlackChannelSelector({
97120
}
98121
}
99122

100-
// Sync selected channel with value prop
101-
useEffect(() => {
102-
if (value && channels.length > 0) {
103-
const channelInfo = channels.find((c) => c.id === value)
104-
setSelectedChannel(channelInfo || null)
105-
} else if (!value) {
106-
setSelectedChannel(null)
107-
}
108-
}, [value, channels])
109-
110-
// If we have a value but no channel info and haven't fetched yet, get just that channel
111-
useEffect(() => {
112-
if (value && !selectedChannel && !loading && !initialFetchDone && credential) {
113-
// For now, we'll fetch all channels when needed
114-
// In the future, we could optimize to fetch just the selected channel
115-
fetchChannels()
116-
}
117-
}, [value, selectedChannel, loading, initialFetchDone, credential, fetchChannels])
118-
119123
const handleSelectChannel = (channel: SlackChannelInfo) => {
120-
setSelectedChannel(channel)
121124
onChange(channel.id, channel)
122125
setOpen(false)
123126
}
@@ -143,15 +146,10 @@ export function SlackChannelSelector({
143146
>
144147
<div className='flex max-w-[calc(100%-20px)] items-center gap-2 overflow-hidden'>
145148
<SlackIcon className='h-4 w-4 text-[#611f69]' />
146-
{selectedChannel ? (
147-
<>
148-
{getChannelIcon(selectedChannel)}
149-
<span className='truncate font-normal'>{formatChannelName(selectedChannel)}</span>
150-
</>
151-
) : value ? (
149+
{cachedChannelName ? (
152150
<>
153151
<Hash className='h-1.5 w-1.5' />
154-
<span className='truncate font-normal'>{value}</span>
152+
<span className='truncate font-normal'>{cachedChannelName}</span>
155153
</>
156154
) : (
157155
<span className='truncate text-muted-foreground'>{label}</span>

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/credential-selector/credential-selector.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import { useSubBlockValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/c
2727
import type { SubBlockConfig } from '@/blocks/types'
2828
import { useCollaborativeWorkflow } from '@/hooks/use-collaborative-workflow'
2929
import { getMissingRequiredScopes } from '@/hooks/use-oauth-scope-status'
30+
import { useDisplayNamesStore } from '@/stores/display-names/store'
3031
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
3132

3233
const logger = createLogger('CredentialSelector')
@@ -116,6 +117,17 @@ export function CredentialSelector({
116117
setHasForeignMeta(foreignMetaFound)
117118
setCredentials(creds)
118119

120+
// Cache credential names in display names store
121+
if (effectiveProviderId) {
122+
const credentialMap = creds.reduce((acc: Record<string, string>, cred: Credential) => {
123+
acc[cred.id] = cred.name
124+
return acc
125+
}, {})
126+
useDisplayNamesStore
127+
.getState()
128+
.setDisplayNames('credentials', effectiveProviderId, credentialMap)
129+
}
130+
119131
// Do not auto-select or reset. We only show what's persisted.
120132
}
121133
} catch (error) {

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/document-selector/document-selector.tsx

Lines changed: 76 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use client'
22

3-
import { useCallback, useEffect, useState } from 'react'
3+
import { useCallback, useEffect, useMemo, useState } from 'react'
44
import { Check, ChevronDown, FileText, RefreshCw } from 'lucide-react'
55
import { Button } from '@/components/ui/button'
66
import {
@@ -15,24 +15,8 @@ import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover
1515
import { useDependsOnGate } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/hooks/use-depends-on-gate'
1616
import { useSubBlockValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/hooks/use-sub-block-value'
1717
import type { SubBlockConfig } from '@/blocks/types'
18-
19-
interface DocumentData {
20-
id: string
21-
knowledgeBaseId: string
22-
filename: string
23-
fileUrl: string
24-
fileSize: number
25-
mimeType: string
26-
chunkCount: number
27-
tokenCount: number
28-
characterCount: number
29-
processingStatus: string
30-
processingStartedAt: Date | null
31-
processingCompletedAt: Date | null
32-
processingError: string | null
33-
enabled: boolean
34-
uploadedAt: Date
35-
}
18+
import { useDisplayNamesStore } from '@/stores/display-names/store'
19+
import { type DocumentData, useKnowledgeStore } from '@/stores/knowledge/store'
3620

3721
interface DocumentSelectorProps {
3822
blockId: string
@@ -51,110 +35,107 @@ export function DocumentSelector({
5135
isPreview = false,
5236
previewValue,
5337
}: DocumentSelectorProps) {
54-
const [documents, setDocuments] = useState<DocumentData[]>([])
5538
const [error, setError] = useState<string | null>(null)
5639
const [open, setOpen] = useState(false)
57-
const [selectedDocument, setSelectedDocument] = useState<DocumentData | null>(null)
58-
const [loading, setLoading] = useState(false)
5940

60-
// Use the proper hook to get the current value and setter
6141
const [storeValue, setStoreValue] = useSubBlockValue(blockId, subBlock.id)
62-
63-
// Get the knowledge base ID from the same block's knowledgeBaseId subblock
6442
const [knowledgeBaseId] = useSubBlockValue(blockId, 'knowledgeBaseId')
43+
const normalizedKnowledgeBaseId =
44+
typeof knowledgeBaseId === 'string' && knowledgeBaseId.trim().length > 0
45+
? knowledgeBaseId
46+
: null
47+
48+
const documentsCache = useKnowledgeStore(
49+
useCallback(
50+
(state) =>
51+
normalizedKnowledgeBaseId ? state.documents[normalizedKnowledgeBaseId] : undefined,
52+
[normalizedKnowledgeBaseId]
53+
)
54+
)
55+
56+
const isDocumentsLoading = useKnowledgeStore(
57+
useCallback(
58+
(state) =>
59+
normalizedKnowledgeBaseId ? state.isDocumentsLoading(normalizedKnowledgeBaseId) : false,
60+
[normalizedKnowledgeBaseId]
61+
)
62+
)
63+
64+
const getDocuments = useKnowledgeStore((state) => state.getDocuments)
6565

66-
// Use preview value when in preview mode, otherwise use store value
6766
const value = isPreview ? previewValue : storeValue
6867

6968
const { finalDisabled } = useDependsOnGate(blockId, subBlock, { disabled, isPreview })
7069
const isDisabled = finalDisabled
7170

72-
// Fetch documents for the selected knowledge base
73-
const fetchDocuments = useCallback(async () => {
74-
if (!knowledgeBaseId) {
75-
setDocuments([])
71+
const documents = useMemo<DocumentData[]>(() => {
72+
if (!documentsCache) return []
73+
return documentsCache.documents ?? []
74+
}, [documentsCache])
75+
76+
const loadDocuments = useCallback(async () => {
77+
if (!normalizedKnowledgeBaseId) {
7678
setError('No knowledge base selected')
7779
return
7880
}
7981

80-
setLoading(true)
8182
setError(null)
8283

8384
try {
84-
const response = await fetch(`/api/knowledge/${knowledgeBaseId}/documents`)
85-
86-
if (!response.ok) {
87-
throw new Error(`Failed to fetch documents: ${response.statusText}`)
88-
}
85+
const fetchedDocuments = await getDocuments(normalizedKnowledgeBaseId)
8986

90-
const result = await response.json()
87+
if (fetchedDocuments.length > 0) {
88+
const documentMap = fetchedDocuments.reduce<Record<string, string>>((acc, doc) => {
89+
acc[doc.id] = doc.filename
90+
return acc
91+
}, {})
9192

92-
if (!result.success) {
93-
throw new Error(result.error || 'Failed to fetch documents')
93+
useDisplayNamesStore
94+
.getState()
95+
.setDisplayNames('documents', normalizedKnowledgeBaseId, documentMap)
9496
}
95-
96-
const fetchedDocuments = result.data.documents || result.data || []
97-
setDocuments(fetchedDocuments)
9897
} catch (err) {
99-
if ((err as Error).name === 'AbortError') return
100-
setError((err as Error).message)
101-
setDocuments([])
102-
} finally {
103-
setLoading(false)
98+
if (err instanceof Error && err.name === 'AbortError') return
99+
setError(err instanceof Error ? err.message : 'Failed to fetch documents')
104100
}
105-
}, [knowledgeBaseId])
101+
}, [normalizedKnowledgeBaseId, getDocuments])
106102

107-
// Handle dropdown open/close - fetch documents when opening
108103
const handleOpenChange = (isOpen: boolean) => {
109-
if (isPreview) return
110-
if (isDisabled) return
104+
if (isPreview || isDisabled) return
111105

112106
setOpen(isOpen)
113107

114-
// Fetch fresh documents when opening the dropdown
115-
if (isOpen) {
116-
fetchDocuments()
108+
if (isOpen && (!documentsCache || !documentsCache.documents.length)) {
109+
void loadDocuments()
117110
}
118111
}
119112

120-
// Handle document selection
121113
const handleSelectDocument = (document: DocumentData) => {
122114
if (isPreview) return
123115

124-
setSelectedDocument(document)
125116
setStoreValue(document.id)
126117
onDocumentSelect?.(document.id)
127118
setOpen(false)
128119
}
129120

130-
// Sync selected document with value prop
131-
useEffect(() => {
132-
if (isDisabled) return
133-
if (value && documents.length > 0) {
134-
const docInfo = documents.find((doc) => doc.id === value)
135-
setSelectedDocument(docInfo || null)
136-
} else {
137-
setSelectedDocument(null)
138-
}
139-
}, [value, documents, isDisabled])
140-
141-
// Reset documents when knowledge base changes
142121
useEffect(() => {
143-
setDocuments([])
144-
setSelectedDocument(null)
145122
setError(null)
146-
}, [knowledgeBaseId])
123+
}, [normalizedKnowledgeBaseId])
147124

148-
// Fetch documents when knowledge base is available
149125
useEffect(() => {
150-
if (knowledgeBaseId && !isPreview && !isDisabled) {
151-
fetchDocuments()
152-
}
153-
}, [knowledgeBaseId, isPreview, isDisabled, fetchDocuments])
126+
if (!normalizedKnowledgeBaseId || documents.length === 0) return
154127

155-
const formatDocumentName = (document: DocumentData) => {
156-
return document.filename
157-
}
128+
const documentMap = documents.reduce<Record<string, string>>((acc, doc) => {
129+
acc[doc.id] = doc.filename
130+
return acc
131+
}, {})
132+
133+
useDisplayNamesStore
134+
.getState()
135+
.setDisplayNames('documents', normalizedKnowledgeBaseId as string, documentMap)
136+
}, [documents, normalizedKnowledgeBaseId])
137+
138+
const formatDocumentName = (document: DocumentData) => document.filename
158139

159140
const getDocumentDescription = (document: DocumentData) => {
160141
const statusMap: Record<string, string> = {
@@ -171,6 +152,18 @@ export function DocumentSelector({
171152
}
172153

173154
const label = subBlock.placeholder || 'Select document'
155+
const isLoading = isDocumentsLoading && !error
156+
157+
// Always use cached display name
158+
const displayName = useDisplayNamesStore(
159+
useCallback(
160+
(state) => {
161+
if (!normalizedKnowledgeBaseId || !value || typeof value !== 'string') return null
162+
return state.cache.documents[normalizedKnowledgeBaseId]?.[value] || null
163+
},
164+
[normalizedKnowledgeBaseId, value]
165+
)
166+
)
174167

175168
return (
176169
<div className='w-full'>
@@ -185,8 +178,8 @@ export function DocumentSelector({
185178
>
186179
<div className='flex max-w-[calc(100%-20px)] items-center gap-2 overflow-hidden'>
187180
<FileText className='h-4 w-4 text-muted-foreground' />
188-
{selectedDocument ? (
189-
<span className='truncate font-normal'>{formatDocumentName(selectedDocument)}</span>
181+
{displayName ? (
182+
<span className='truncate font-normal'>{displayName}</span>
190183
) : (
191184
<span className='truncate text-muted-foreground'>{label}</span>
192185
)}
@@ -199,7 +192,7 @@ export function DocumentSelector({
199192
<CommandInput placeholder='Search documents...' />
200193
<CommandList>
201194
<CommandEmpty>
202-
{loading ? (
195+
{isLoading ? (
203196
<div className='flex items-center justify-center p-4'>
204197
<RefreshCw className='h-4 w-4 animate-spin' />
205198
<span className='ml-2'>Loading documents...</span>
@@ -208,7 +201,7 @@ export function DocumentSelector({
208201
<div className='p-4 text-center'>
209202
<p className='text-destructive text-sm'>{error}</p>
210203
</div>
211-
) : !knowledgeBaseId ? (
204+
) : !normalizedKnowledgeBaseId ? (
212205
<div className='p-4 text-center'>
213206
<p className='font-medium text-sm'>No knowledge base selected</p>
214207
<p className='text-muted-foreground text-xs'>

0 commit comments

Comments
 (0)