Skip to content

Commit 78804c9

Browse files
Bill LeoutsakosBill Leoutsakos
authored andcommitted
fix(kb): show spinner for pending connector sync
1 parent 71129cd commit 78804c9

2 files changed

Lines changed: 79 additions & 4 deletions

File tree

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1091,7 +1091,7 @@ export function KnowledgeBase({
10911091
className={cn(chipVariants({ variant: 'filled' }), 'max-w-[180px]')}
10921092
>
10931093
<span className='relative flex size-[14px] flex-shrink-0 items-center justify-center'>
1094-
{connector.status === 'syncing' ? (
1094+
{isConnectorSyncingOrPending(connector) ? (
10951095
<Loader className='size-[14px]' animate />
10961096
) : (
10971097
ConnectorIcon && <BrandIcon icon={ConnectorIcon} className='size-[14px]' />

apps/sim/hooks/queries/kb/connectors.test.ts

Lines changed: 78 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* @vitest-environment node
33
*/
44

5-
import { beforeEach, describe, expect, it, vi } from 'vitest'
5+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
66

77
const mocks = vi.hoisted(() => ({
88
requestJson: vi.fn(),
@@ -21,9 +21,37 @@ vi.mock('@/lib/api/client/request', () => ({
2121
requestJson: mocks.requestJson,
2222
}))
2323

24-
import { listKnowledgeConnectorDocumentsContract } from '@/lib/api/contracts/knowledge'
24+
import {
25+
type ConnectorData,
26+
listKnowledgeConnectorDocumentsContract,
27+
} from '@/lib/api/contracts/knowledge'
2528
import { MAX_KNOWLEDGE_CONNECTOR_DOCUMENT_PAGE_SIZE } from '@/lib/knowledge/constants'
26-
import { useConnectorDocuments } from '@/hooks/queries/kb/connectors'
29+
import { isConnectorSyncingOrPending, useConnectorDocuments } from '@/hooks/queries/kb/connectors'
30+
31+
const NOW_MS = new Date('2026-08-21T12:00:00.000Z').getTime()
32+
33+
function makeConnector(overrides: Partial<ConnectorData> = {}): ConnectorData {
34+
const createdAt = new Date(NOW_MS - 60_000).toISOString()
35+
36+
return {
37+
id: 'connector-1',
38+
knowledgeBaseId: 'knowledge-1',
39+
connectorType: 'hubspot',
40+
credentialId: 'credential-1',
41+
sourceConfig: {},
42+
syncMode: 'full',
43+
syncIntervalMinutes: 1440,
44+
status: 'active',
45+
lastSyncAt: null,
46+
lastSyncError: null,
47+
lastSyncDocCount: null,
48+
nextSyncAt: null,
49+
consecutiveFailures: 0,
50+
createdAt,
51+
updatedAt: createdAt,
52+
...overrides,
53+
}
54+
}
2755

2856
interface ConnectorDocumentsPage {
2957
documents: Array<{ id: string }>
@@ -39,6 +67,53 @@ interface ConnectorDocumentsQueryOptions {
3967
) => number | undefined
4068
}
4169

70+
describe('isConnectorSyncingOrPending', () => {
71+
beforeEach(() => {
72+
vi.spyOn(Date, 'now').mockReturnValue(NOW_MS)
73+
})
74+
75+
afterEach(() => {
76+
vi.restoreAllMocks()
77+
})
78+
79+
it('treats a recently created active connector without a completed sync as pending', () => {
80+
expect(isConnectorSyncingOrPending(makeConnector())).toBe(true)
81+
})
82+
83+
it('treats a syncing connector as syncing regardless of its age or sync history', () => {
84+
const connector = makeConnector({
85+
status: 'syncing',
86+
createdAt: new Date(NOW_MS - 24 * 60 * 60 * 1000).toISOString(),
87+
lastSyncAt: new Date(NOW_MS - 60 * 60 * 1000).toISOString(),
88+
})
89+
90+
expect(isConnectorSyncingOrPending(connector)).toBe(true)
91+
})
92+
93+
it('does not treat an active connector with a completed sync as pending', () => {
94+
const connector = makeConnector({
95+
lastSyncAt: new Date(NOW_MS - 30_000).toISOString(),
96+
})
97+
98+
expect(isConnectorSyncingOrPending(connector)).toBe(false)
99+
})
100+
101+
it('stops treating an active connector as pending at the two-minute boundary', () => {
102+
const connector = makeConnector({
103+
createdAt: new Date(NOW_MS - 2 * 60 * 1000).toISOString(),
104+
})
105+
106+
expect(isConnectorSyncingOrPending(connector)).toBe(false)
107+
})
108+
109+
it.each(['error', 'paused', 'disabled'] as const)(
110+
'does not treat a recent %s connector as pending',
111+
(status) => {
112+
expect(isConnectorSyncingOrPending(makeConnector({ status }))).toBe(false)
113+
}
114+
)
115+
})
116+
42117
describe('useConnectorDocuments', () => {
43118
beforeEach(() => {
44119
vi.clearAllMocks()

0 commit comments

Comments
 (0)