11'use client'
22
3- import { useCallback , useEffect , useState } from 'react'
3+ import { useCallback , useEffect , useMemo , useState } from 'react'
44import { Check , ChevronDown , FileText , RefreshCw } from 'lucide-react'
55import { Button } from '@/components/ui/button'
66import {
@@ -15,24 +15,8 @@ import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover
1515import { useDependsOnGate } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/hooks/use-depends-on-gate'
1616import { useSubBlockValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/hooks/use-sub-block-value'
1717import 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
3721interface 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