Skip to content

Commit ec430ab

Browse files
improvement(tanstack): migrate multiple stores (#1994)
* improvement(tanstack): migrate folders, knowledge to tanstack * fix types
1 parent 4b4060f commit ec430ab

File tree

38 files changed

+1387
-1978
lines changed

38 files changed

+1387
-1978
lines changed

apps/sim/app/workspace/[workspaceId]/knowledge/[id]/base.tsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,12 @@ import {
4444
SearchInput,
4545
} from '@/app/workspace/[workspaceId]/knowledge/components'
4646
import { useUserPermissionsContext } from '@/app/workspace/[workspaceId]/providers/workspace-permissions-provider'
47-
import { useKnowledgeBase, useKnowledgeBaseDocuments } from '@/hooks/use-knowledge'
48-
import { type DocumentData, useKnowledgeStore } from '@/stores/knowledge/store'
47+
import {
48+
useKnowledgeBase,
49+
useKnowledgeBaseDocuments,
50+
useKnowledgeBasesList,
51+
} from '@/hooks/use-knowledge'
52+
import type { DocumentData } from '@/stores/knowledge/store'
4953

5054
const logger = createLogger('KnowledgeBase')
5155

@@ -125,10 +129,10 @@ export function KnowledgeBase({
125129
id,
126130
knowledgeBaseName: passedKnowledgeBaseName,
127131
}: KnowledgeBaseProps) {
128-
const { removeKnowledgeBase } = useKnowledgeStore()
129-
const userPermissions = useUserPermissionsContext()
130132
const params = useParams()
131133
const workspaceId = params.workspaceId as string
134+
const { removeKnowledgeBase } = useKnowledgeBasesList(workspaceId, { enabled: false })
135+
const userPermissions = useUserPermissionsContext()
132136

133137
const [searchQuery, setSearchQuery] = useState('')
134138

apps/sim/app/workspace/[workspaceId]/logs/components/filters/components/folder.tsx

Lines changed: 27 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useEffect, useMemo, useState } from 'react'
1+
import { useMemo, useState } from 'react'
22
import { Check, ChevronDown } from 'lucide-react'
33
import { useParams } from 'next/navigation'
44
import { Button } from '@/components/emcn'
@@ -22,7 +22,8 @@ import {
2222
filterButtonClass,
2323
folderDropdownListStyle,
2424
} from '@/app/workspace/[workspaceId]/logs/components/filters/components/shared'
25-
import { useFolderStore } from '@/stores/folders/store'
25+
import { useFolders } from '@/hooks/queries/folders'
26+
import { type FolderTreeNode, useFolderStore } from '@/stores/folders/store'
2627
import { useFilterStore } from '@/stores/logs/filters/store'
2728

2829
const logger = createLogger('LogsFolderFilter')
@@ -36,56 +37,37 @@ interface FolderOption {
3637

3738
export default function FolderFilter() {
3839
const { folderIds, toggleFolderId, setFolderIds } = useFilterStore()
39-
const { getFolderTree, fetchFolders } = useFolderStore()
40+
const { getFolderTree } = useFolderStore()
4041
const params = useParams()
4142
const workspaceId = params.workspaceId as string
42-
const [folders, setFolders] = useState<FolderOption[]>([])
43-
const [loading, setLoading] = useState(true)
4443
const [search, setSearch] = useState('')
44+
const { isLoading: foldersLoading } = useFolders(workspaceId)
4545

46-
// Fetch all available folders from the API
47-
useEffect(() => {
48-
const fetchFoldersData = async () => {
49-
try {
50-
setLoading(true)
51-
if (workspaceId) {
52-
await fetchFolders(workspaceId)
53-
const folderTree = getFolderTree(workspaceId)
46+
const folderTree = workspaceId ? getFolderTree(workspaceId) : []
5447

55-
// Flatten the folder tree and create options with full paths
56-
const flattenFolders = (nodes: any[], parentPath = ''): FolderOption[] => {
57-
const result: FolderOption[] = []
48+
const folders: FolderOption[] = useMemo(() => {
49+
const flattenFolders = (nodes: FolderTreeNode[], parentPath = ''): FolderOption[] => {
50+
const result: FolderOption[] = []
5851

59-
for (const node of nodes) {
60-
const currentPath = parentPath ? `${parentPath} / ${node.name}` : node.name
61-
result.push({
62-
id: node.id,
63-
name: node.name,
64-
color: node.color || '#6B7280',
65-
path: currentPath,
66-
})
52+
for (const node of nodes) {
53+
const currentPath = parentPath ? `${parentPath} / ${node.name}` : node.name
54+
result.push({
55+
id: node.id,
56+
name: node.name,
57+
color: node.color || '#6B7280',
58+
path: currentPath,
59+
})
6760

68-
// Add children recursively
69-
if (node.children && node.children.length > 0) {
70-
result.push(...flattenFolders(node.children, currentPath))
71-
}
72-
}
73-
74-
return result
75-
}
76-
77-
const folderOptions = flattenFolders(folderTree)
78-
setFolders(folderOptions)
61+
if (node.children && node.children.length > 0) {
62+
result.push(...flattenFolders(node.children, currentPath))
7963
}
80-
} catch (error) {
81-
logger.error('Failed to fetch folders', { error })
82-
} finally {
83-
setLoading(false)
8464
}
65+
66+
return result
8567
}
8668

87-
fetchFoldersData()
88-
}, [workspaceId, fetchFolders, getFolderTree])
69+
return flattenFolders(folderTree)
70+
}, [folderTree])
8971

9072
// Get display text for the dropdown button
9173
const getSelectedFoldersText = () => {
@@ -111,7 +93,7 @@ export default function FolderFilter() {
11193
<DropdownMenu>
11294
<DropdownMenuTrigger asChild>
11395
<Button variant='outline' className={filterButtonClass}>
114-
{loading ? 'Loading folders...' : getSelectedFoldersText()}
96+
{foldersLoading ? 'Loading folders...' : getSelectedFoldersText()}
11597
<ChevronDown className='ml-2 h-4 w-4 text-muted-foreground' />
11698
</Button>
11799
</DropdownMenuTrigger>
@@ -125,7 +107,9 @@ export default function FolderFilter() {
125107
<Command>
126108
<CommandInput placeholder='Search folders...' onValueChange={(v) => setSearch(v)} />
127109
<CommandList className={commandListClass} style={folderDropdownListStyle}>
128-
<CommandEmpty>{loading ? 'Loading folders...' : 'No folders found.'}</CommandEmpty>
110+
<CommandEmpty>
111+
{foldersLoading ? 'Loading folders...' : 'No folders found.'}
112+
</CommandEmpty>
129113
<CommandGroup>
130114
<CommandItem
131115
value='all-folders'

apps/sim/app/workspace/[workspaceId]/logs/logs.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { AutocompleteSearch } from '@/app/workspace/[workspaceId]/logs/component
1212
import { Sidebar } from '@/app/workspace/[workspaceId]/logs/components/sidebar/sidebar'
1313
import Dashboard from '@/app/workspace/[workspaceId]/logs/dashboard'
1414
import { formatDate } from '@/app/workspace/[workspaceId]/logs/utils'
15+
import { useFolders } from '@/hooks/queries/folders'
1516
import { useDebounce } from '@/hooks/use-debounce'
1617
import { useFolderStore } from '@/stores/folders/store'
1718
import { useFilterStore } from '@/stores/logs/filters/store'
@@ -120,7 +121,8 @@ export default function Logs() {
120121
setSearchQuery(storeSearchQuery)
121122
}, [storeSearchQuery])
122123

123-
const { fetchFolders, getFolderTree } = useFolderStore()
124+
const foldersQuery = useFolders(workspaceId)
125+
const { getFolderTree } = useFolderStore()
124126

125127
useEffect(() => {
126128
let cancelled = false
@@ -138,7 +140,6 @@ export default function Logs() {
138140
if (!cancelled) setAvailableWorkflows([])
139141
}
140142

141-
await fetchFolders(workspaceId)
142143
const tree = getFolderTree(workspaceId)
143144

144145
const flatten = (nodes: any[], parentPath = ''): string[] => {
@@ -168,7 +169,7 @@ export default function Logs() {
168169
return () => {
169170
cancelled = true
170171
}
171-
}, [workspaceId, fetchFolders, getFolderTree])
172+
}, [workspaceId, getFolderTree, foldersQuery.data])
172173

173174
useEffect(() => {
174175
if (isInitialized.current && debouncedSearchQuery !== storeSearchQuery) {
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
'use client'
2+
3+
import { useEffect } from 'react'
4+
import { createLogger } from '@/lib/logs/console/logger'
5+
import { useProviderModels } from '@/hooks/queries/providers'
6+
import { updateOllamaProviderModels, updateOpenRouterProviderModels } from '@/providers/utils'
7+
import { useProvidersStore } from '@/stores/providers/store'
8+
import type { ProviderName } from '@/stores/providers/types'
9+
10+
const logger = createLogger('ProviderModelsLoader')
11+
12+
function useSyncProvider(provider: ProviderName) {
13+
const setProviderModels = useProvidersStore((state) => state.setProviderModels)
14+
const setProviderLoading = useProvidersStore((state) => state.setProviderLoading)
15+
const { data, isLoading, isFetching, error } = useProviderModels(provider)
16+
17+
useEffect(() => {
18+
setProviderLoading(provider, isLoading || isFetching)
19+
}, [provider, isLoading, isFetching, setProviderLoading])
20+
21+
useEffect(() => {
22+
if (!data) return
23+
24+
try {
25+
if (provider === 'ollama') {
26+
updateOllamaProviderModels(data)
27+
} else if (provider === 'openrouter') {
28+
void updateOpenRouterProviderModels(data)
29+
}
30+
} catch (syncError) {
31+
logger.warn(`Failed to sync provider definitions for ${provider}`, syncError as Error)
32+
}
33+
34+
setProviderModels(provider, data)
35+
}, [provider, data, setProviderModels])
36+
37+
useEffect(() => {
38+
if (error) {
39+
logger.error(`Failed to load ${provider} models`, error)
40+
}
41+
}, [provider, error])
42+
}
43+
44+
export function ProviderModelsLoader() {
45+
useSyncProvider('base')
46+
useSyncProvider('ollama')
47+
useSyncProvider('openrouter')
48+
return null
49+
}

apps/sim/app/workspace/[workspaceId]/providers/providers.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import React from 'react'
44
import { Tooltip } from '@/components/emcn'
55
import { GlobalCommandsProvider } from '@/app/workspace/[workspaceId]/providers/global-commands-provider'
66
import { WorkspacePermissionsProvider } from '@/app/workspace/[workspaceId]/providers/workspace-permissions-provider'
7+
import { ProviderModelsLoader } from './provider-models-loader'
78
import { SettingsLoader } from './settings-loader'
89

910
interface ProvidersProps {
@@ -14,6 +15,7 @@ const Providers = React.memo<ProvidersProps>(({ children }) => {
1415
return (
1516
<>
1617
<SettingsLoader />
18+
<ProviderModelsLoader />
1719
<GlobalCommandsProvider>
1820
<Tooltip.Provider delayDuration={600} skipDelayDuration={0}>
1921
<WorkspacePermissionsProvider>{children}</WorkspacePermissionsProvider>

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

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

3-
import { useCallback, useEffect, useMemo, useState } from 'react'
3+
import { useCallback, useEffect, useState } from 'react'
44
import { Check, ChevronDown, FileText, RefreshCw } from 'lucide-react'
55
import { Button } from '@/components/ui/button'
66
import {
@@ -15,8 +15,9 @@ 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+
import { useKnowledgeBaseDocuments } from '@/hooks/use-knowledge'
1819
import { useDisplayNamesStore } from '@/stores/display-names/store'
19-
import { type DocumentData, useKnowledgeStore } from '@/stores/knowledge/store'
20+
import type { DocumentData } from '@/stores/knowledge/store'
2021

2122
interface DocumentSelectorProps {
2223
blockId: string
@@ -45,68 +46,29 @@ export function DocumentSelector({
4546
? knowledgeBaseId
4647
: null
4748

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)
65-
6649
const value = isPreview ? previewValue : storeValue
6750

6851
const { finalDisabled } = useDependsOnGate(blockId, subBlock, { disabled, isPreview })
6952
const isDisabled = finalDisabled
7053

71-
const documents = useMemo<DocumentData[]>(() => {
72-
if (!documentsCache) return []
73-
return documentsCache.documents ?? []
74-
}, [documentsCache])
75-
76-
const loadDocuments = useCallback(async () => {
77-
if (!normalizedKnowledgeBaseId) {
78-
setError('No knowledge base selected')
79-
return
80-
}
81-
82-
setError(null)
83-
84-
try {
85-
const fetchedDocuments = await getDocuments(normalizedKnowledgeBaseId)
86-
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-
}, {})
92-
93-
useDisplayNamesStore
94-
.getState()
95-
.setDisplayNames('documents', normalizedKnowledgeBaseId, documentMap)
96-
}
97-
} catch (err) {
98-
if (err instanceof Error && err.name === 'AbortError') return
99-
setError(err instanceof Error ? err.message : 'Failed to fetch documents')
100-
}
101-
}, [normalizedKnowledgeBaseId, getDocuments])
54+
const {
55+
documents,
56+
isLoading: documentsLoading,
57+
error: documentsError,
58+
refreshDocuments,
59+
} = useKnowledgeBaseDocuments(normalizedKnowledgeBaseId ?? '', {
60+
limit: 500,
61+
offset: 0,
62+
enabled: open && Boolean(normalizedKnowledgeBaseId),
63+
})
10264

10365
const handleOpenChange = (isOpen: boolean) => {
10466
if (isPreview || isDisabled) return
10567

10668
setOpen(isOpen)
10769

108-
if (isOpen && (!documentsCache || !documentsCache.documents.length)) {
109-
void loadDocuments()
70+
if (isOpen && normalizedKnowledgeBaseId) {
71+
void refreshDocuments()
11072
}
11173
}
11274

@@ -119,9 +81,15 @@ export function DocumentSelector({
11981
}
12082

12183
useEffect(() => {
122-
setError(null)
84+
if (!normalizedKnowledgeBaseId) {
85+
setError(null)
86+
}
12387
}, [normalizedKnowledgeBaseId])
12488

89+
useEffect(() => {
90+
setError(documentsError)
91+
}, [documentsError])
92+
12593
useEffect(() => {
12694
if (!normalizedKnowledgeBaseId || documents.length === 0) return
12795

@@ -152,7 +120,7 @@ export function DocumentSelector({
152120
}
153121

154122
const label = subBlock.placeholder || 'Select document'
155-
const isLoading = isDocumentsLoading && !error
123+
const isLoading = documentsLoading && !error
156124

157125
// Always use cached display name
158126
const displayName = useDisplayNamesStore(

0 commit comments

Comments
 (0)