Skip to content

Commit 69614d2

Browse files
authored
improvement(kb): migrate manual fetches in kb module to use reactquery (#2894)
* improvement(kb): migrate manual fetches in kb module to use reactquery * converted remaining manual kb fetches * unwrap kb tags before API call, added more query invalidation for chunks * added resetMutation calls after modal closes
1 parent 6cbadd7 commit 69614d2

File tree

11 files changed

+994
-736
lines changed

11 files changed

+994
-736
lines changed

apps/sim/app/workspace/[workspaceId]/knowledge/[id]/[documentId]/components/create-chunk-modal/create-chunk-modal.tsx

Lines changed: 28 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import { useRef, useState } from 'react'
44
import { createLogger } from '@sim/logger'
5-
import { useQueryClient } from '@tanstack/react-query'
65
import {
76
Button,
87
Label,
@@ -14,7 +13,7 @@ import {
1413
Textarea,
1514
} from '@/components/emcn'
1615
import type { DocumentData } from '@/lib/knowledge/types'
17-
import { knowledgeKeys } from '@/hooks/queries/knowledge'
16+
import { useCreateChunk } from '@/hooks/queries/knowledge'
1817

1918
const logger = createLogger('CreateChunkModal')
2019

@@ -31,74 +30,53 @@ export function CreateChunkModal({
3130
document,
3231
knowledgeBaseId,
3332
}: CreateChunkModalProps) {
34-
const queryClient = useQueryClient()
33+
const {
34+
mutate: createChunk,
35+
isPending: isCreating,
36+
error: mutationError,
37+
reset: resetMutation,
38+
} = useCreateChunk()
3539
const [content, setContent] = useState('')
36-
const [isCreating, setIsCreating] = useState(false)
37-
const [error, setError] = useState<string | null>(null)
3840
const [showUnsavedChangesAlert, setShowUnsavedChangesAlert] = useState(false)
3941
const isProcessingRef = useRef(false)
4042

43+
const error = mutationError?.message ?? null
4144
const hasUnsavedChanges = content.trim().length > 0
4245

43-
const handleCreateChunk = async () => {
46+
const handleCreateChunk = () => {
4447
if (!document || content.trim().length === 0 || isProcessingRef.current) {
4548
if (isProcessingRef.current) {
4649
logger.warn('Chunk creation already in progress, ignoring duplicate request')
4750
}
4851
return
4952
}
5053

51-
try {
52-
isProcessingRef.current = true
53-
setIsCreating(true)
54-
setError(null)
55-
56-
const response = await fetch(
57-
`/api/knowledge/${knowledgeBaseId}/documents/${document.id}/chunks`,
58-
{
59-
method: 'POST',
60-
headers: {
61-
'Content-Type': 'application/json',
62-
},
63-
body: JSON.stringify({
64-
content: content.trim(),
65-
enabled: true,
66-
}),
67-
}
68-
)
69-
70-
if (!response.ok) {
71-
const result = await response.json()
72-
throw new Error(result.error || 'Failed to create chunk')
54+
isProcessingRef.current = true
55+
56+
createChunk(
57+
{
58+
knowledgeBaseId,
59+
documentId: document.id,
60+
content: content.trim(),
61+
enabled: true,
62+
},
63+
{
64+
onSuccess: () => {
65+
isProcessingRef.current = false
66+
onClose()
67+
},
68+
onError: () => {
69+
isProcessingRef.current = false
70+
},
7371
}
74-
75-
const result = await response.json()
76-
77-
if (result.success && result.data) {
78-
logger.info('Chunk created successfully:', result.data.id)
79-
80-
await queryClient.invalidateQueries({
81-
queryKey: knowledgeKeys.detail(knowledgeBaseId),
82-
})
83-
84-
onClose()
85-
} else {
86-
throw new Error(result.error || 'Failed to create chunk')
87-
}
88-
} catch (err) {
89-
logger.error('Error creating chunk:', err)
90-
setError(err instanceof Error ? err.message : 'An error occurred')
91-
} finally {
92-
isProcessingRef.current = false
93-
setIsCreating(false)
94-
}
72+
)
9573
}
9674

9775
const onClose = () => {
9876
onOpenChange(false)
9977
setContent('')
100-
setError(null)
10178
setShowUnsavedChangesAlert(false)
79+
resetMutation()
10280
}
10381

10482
const handleCloseAttempt = () => {

apps/sim/app/workspace/[workspaceId]/knowledge/[id]/[documentId]/components/delete-chunk-modal/delete-chunk-modal.tsx

Lines changed: 4 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,8 @@
11
'use client'
22

3-
import { useState } from 'react'
4-
import { createLogger } from '@sim/logger'
5-
import { useQueryClient } from '@tanstack/react-query'
63
import { Button, Modal, ModalBody, ModalContent, ModalFooter, ModalHeader } from '@/components/emcn'
74
import type { ChunkData } from '@/lib/knowledge/types'
8-
import { knowledgeKeys } from '@/hooks/queries/knowledge'
9-
10-
const logger = createLogger('DeleteChunkModal')
5+
import { useDeleteChunk } from '@/hooks/queries/knowledge'
116

127
interface DeleteChunkModalProps {
138
chunk: ChunkData | null
@@ -24,44 +19,12 @@ export function DeleteChunkModal({
2419
isOpen,
2520
onClose,
2621
}: DeleteChunkModalProps) {
27-
const queryClient = useQueryClient()
28-
const [isDeleting, setIsDeleting] = useState(false)
22+
const { mutate: deleteChunk, isPending: isDeleting } = useDeleteChunk()
2923

30-
const handleDeleteChunk = async () => {
24+
const handleDeleteChunk = () => {
3125
if (!chunk || isDeleting) return
3226

33-
try {
34-
setIsDeleting(true)
35-
36-
const response = await fetch(
37-
`/api/knowledge/${knowledgeBaseId}/documents/${documentId}/chunks/${chunk.id}`,
38-
{
39-
method: 'DELETE',
40-
}
41-
)
42-
43-
if (!response.ok) {
44-
throw new Error('Failed to delete chunk')
45-
}
46-
47-
const result = await response.json()
48-
49-
if (result.success) {
50-
logger.info('Chunk deleted successfully:', chunk.id)
51-
52-
await queryClient.invalidateQueries({
53-
queryKey: knowledgeKeys.detail(knowledgeBaseId),
54-
})
55-
56-
onClose()
57-
} else {
58-
throw new Error(result.error || 'Failed to delete chunk')
59-
}
60-
} catch (err) {
61-
logger.error('Error deleting chunk:', err)
62-
} finally {
63-
setIsDeleting(false)
64-
}
27+
deleteChunk({ knowledgeBaseId, documentId, chunkId: chunk.id }, { onSuccess: onClose })
6528
}
6629

6730
if (!chunk) return null

apps/sim/app/workspace/[workspaceId]/knowledge/[id]/[documentId]/components/document-tags-modal/document-tags-modal.tsx

Lines changed: 27 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import {
2525
} from '@/hooks/kb/use-knowledge-base-tag-definitions'
2626
import { useNextAvailableSlot } from '@/hooks/kb/use-next-available-slot'
2727
import { type TagDefinitionInput, useTagDefinitions } from '@/hooks/kb/use-tag-definitions'
28+
import { useUpdateDocumentTags } from '@/hooks/queries/knowledge'
2829

2930
const logger = createLogger('DocumentTagsModal')
3031

@@ -58,8 +59,6 @@ function formatValueForDisplay(value: string, fieldType: string): string {
5859
try {
5960
const date = new Date(value)
6061
if (Number.isNaN(date.getTime())) return value
61-
// For UTC dates, display the UTC date to prevent timezone shifts
62-
// e.g., 2002-05-16T00:00:00.000Z should show as "May 16, 2002" not "May 15, 2002"
6362
if (typeof value === 'string' && (value.endsWith('Z') || /[+-]\d{2}:\d{2}$/.test(value))) {
6463
return new Date(
6564
date.getUTCFullYear(),
@@ -96,6 +95,7 @@ export function DocumentTagsModal({
9695
const documentTagHook = useTagDefinitions(knowledgeBaseId, documentId)
9796
const kbTagHook = useKnowledgeBaseTagDefinitions(knowledgeBaseId)
9897
const { getNextAvailableSlot: getServerNextSlot } = useNextAvailableSlot(knowledgeBaseId)
98+
const { mutateAsync: updateDocumentTags } = useUpdateDocumentTags()
9999

100100
const { saveTagDefinitions, tagDefinitions, fetchTagDefinitions } = documentTagHook
101101
const { tagDefinitions: kbTagDefinitions, fetchTagDefinitions: refreshTagDefinitions } = kbTagHook
@@ -118,7 +118,6 @@ export function DocumentTagsModal({
118118
const definition = definitions.find((def) => def.tagSlot === slot)
119119

120120
if (rawValue !== null && rawValue !== undefined && definition) {
121-
// Convert value to string for storage
122121
const stringValue = String(rawValue).trim()
123122
if (stringValue) {
124123
tags.push({
@@ -142,41 +141,34 @@ export function DocumentTagsModal({
142141
async (tagsToSave: DocumentTag[]) => {
143142
if (!documentData) return
144143

145-
try {
146-
const tagData: Record<string, string> = {}
147-
148-
// Only include tags that have values (omit empty ones)
149-
// Use empty string for slots that should be cleared
150-
ALL_TAG_SLOTS.forEach((slot) => {
151-
const tag = tagsToSave.find((t) => t.slot === slot)
152-
if (tag?.value.trim()) {
153-
tagData[slot] = tag.value.trim()
154-
} else {
155-
// Use empty string to clear a tag (API schema expects string, not null)
156-
tagData[slot] = ''
157-
}
158-
})
159-
160-
const response = await fetch(`/api/knowledge/${knowledgeBaseId}/documents/${documentId}`, {
161-
method: 'PUT',
162-
headers: {
163-
'Content-Type': 'application/json',
164-
},
165-
body: JSON.stringify(tagData),
166-
})
167-
168-
if (!response.ok) {
169-
throw new Error('Failed to update document tags')
144+
const tagData: Record<string, string> = {}
145+
146+
ALL_TAG_SLOTS.forEach((slot) => {
147+
const tag = tagsToSave.find((t) => t.slot === slot)
148+
if (tag?.value.trim()) {
149+
tagData[slot] = tag.value.trim()
150+
} else {
151+
tagData[slot] = ''
170152
}
153+
})
171154

172-
onDocumentUpdate?.(tagData as Record<string, string>)
173-
await fetchTagDefinitions()
174-
} catch (error) {
175-
logger.error('Error updating document tags:', error)
176-
throw error
177-
}
155+
await updateDocumentTags({
156+
knowledgeBaseId,
157+
documentId,
158+
tags: tagData,
159+
})
160+
161+
onDocumentUpdate?.(tagData)
162+
await fetchTagDefinitions()
178163
},
179-
[documentData, knowledgeBaseId, documentId, fetchTagDefinitions, onDocumentUpdate]
164+
[
165+
documentData,
166+
knowledgeBaseId,
167+
documentId,
168+
updateDocumentTags,
169+
fetchTagDefinitions,
170+
onDocumentUpdate,
171+
]
180172
)
181173

182174
const handleRemoveTag = async (index: number) => {

0 commit comments

Comments
 (0)