Skip to content

Commit fdbedb4

Browse files
committed
improvement(org): settle Home's caches, URL key, and skeleton after review
Widget reads now refresh from every surface that changes an account, the search history keys leave the auth client's module so the server prefetch stays light, the day param moves to its own file, and the tests are aimed at the branches they claim to cover.
1 parent 4bc42a1 commit fdbedb4

26 files changed

Lines changed: 104 additions & 80 deletions

File tree

‎apps/sim/app/o/[organizationId]/components/organization-conversation-landing.test.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ afterEach(async () => {
134134
})
135135

136136
describe('an Assistant conversation on a surface with its own landing', () => {
137-
it('arranges the real composer and the mode’s shortcuts in the surface’s landing', async () => {
137+
it('hands its composer and the Assistant shortcuts to the surface’s landing, in place of the default heading', async () => {
138138
await render(
139139
<OrganizationConversation
140140
requestMode='assistant'

‎apps/sim/app/o/[organizationId]/home/page.test.tsx‎

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -100,10 +100,20 @@ describe('organization Search page gates', () => {
100100
await expect(OrganizationPage({ params })).rejects.toThrow('NEXT_REDIRECT:/o/org-1/home')
101101
})
102102

103-
it('gives a builder Home with the agent', async () => {
104-
expect(renderToStaticMarkup(await OrganizationHomePage({ params }))).toContain('Home for agent')
105-
await expect(OrganizationPage({ params })).rejects.toThrow('NEXT_REDIRECT:/o/org-1/home')
106-
})
103+
it.each([true, false])(
104+
'gives a builder Home with the agent (Search access: %s)',
105+
async (memberScoped) => {
106+
mocks.context.mockResolvedValue({
107+
mothershipAvailable: true,
108+
canBuild: true,
109+
searchAccess: { memberScoped },
110+
})
111+
expect(renderToStaticMarkup(await OrganizationHomePage({ params }))).toContain(
112+
'Home for agent'
113+
)
114+
await expect(OrganizationPage({ params })).rejects.toThrow('NEXT_REDIRECT:/o/org-1/home')
115+
}
116+
)
107117

108118
it('sends a member without the Assistant to Search, where Home has no conversation to hold', async () => {
109119
mocks.context.mockResolvedValue({ ...searchOnlyMember, mothershipAvailable: false })
@@ -116,7 +126,7 @@ describe('organization Search page gates', () => {
116126
await expect(OrganizationChatPage({ params })).rejects.toThrow('NEXT_REDIRECT:/o/org-1/search')
117127
})
118128

119-
it('routes chat URLs exactly where Home routes the same viewer, before loading the chat', async () => {
129+
it('sends a viewer with no conversation here away from chat URLs, before loading the chat', async () => {
120130
mocks.context.mockResolvedValue({
121131
mothershipAvailable: true,
122132
canBuild: false,

‎apps/sim/app/workspace/[workspaceId]/home/components/knowledge-search-results/search-transitions.test.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,8 @@ import type { ResourceScope } from '@/lib/core/resource-scope'
7171
import { SearchLandingHistory } from '@/app/o/[organizationId]/components/search-landing-history'
7272
import { OrganizationSearch } from '@/app/o/[organizationId]/search/search'
7373
import { KnowledgeSearchResults } from '@/app/workspace/[workspaceId]/home/components/knowledge-search-results/knowledge-search-results'
74-
import { searchHistoryKeys } from '@/hooks/queries/search-history'
7574
import { knowledgeKeys } from '@/hooks/queries/utils/knowledge-keys'
75+
import { searchHistoryKeys } from '@/hooks/queries/utils/search-history-keys'
7676
import { useMothershipDraftsStore } from '@/stores/mothership-drafts/store'
7777

7878
nextNavigationMockFns.mockUsePathname.mockReturnValue('/o/organization/search')

‎apps/sim/app/workspace/[workspaceId]/home/hooks/use-chat.dom.test.tsx‎

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,8 @@ let queryClient: QueryClient
323323

324324
function renderUseChat(
325325
owner: string | { organizationId: string } = 'ws-1',
326-
requestMode?: 'agent' | 'assistant'
326+
requestMode?: 'agent' | 'assistant',
327+
getChatUrl?: (chatId: string) => string
327328
): {
328329
getResult: () => ReturnType<typeof useChat>
329330
unmount: () => void
@@ -337,7 +338,7 @@ function renderUseChat(
337338
let result: ReturnType<typeof useChat> | undefined
338339

339340
function Probe() {
340-
result = useChat(owner, undefined, { requestMode })
341+
result = useChat(owner, undefined, { requestMode, getChatUrl })
341342
return null
342343
}
343344

@@ -1165,7 +1166,11 @@ describe('useChat remount send recovery', () => {
11651166
mockUsePathname.mockReturnValue('/o/org-1/home')
11661167
state.postBehavior = 'task'
11671168
const replace = vi.spyOn(window.history, 'replaceState')
1168-
const { getResult, selectMode } = renderUseChat({ organizationId: 'org-1' }, 'assistant')
1169+
const { getResult, selectMode } = renderUseChat(
1170+
{ organizationId: 'org-1' },
1171+
'assistant',
1172+
(id) => `/o/org-1/search/chat/${id}?mode=assistant`
1173+
)
11691174
await act(async () => {
11701175
void getResult().sendMessage('What changed in the renewal policy?', undefined, undefined, {
11711176
requestMode: 'assistant',
@@ -1174,7 +1179,7 @@ describe('useChat remount send recovery', () => {
11741179
await waitFor(() => getResult().resolvedChatId === DEDUPED_CHAT_ID)
11751180
expect(state.postBodies[0]).toMatchObject({ mode: 'assistant' })
11761181
const chatPath = `/o/org-1/search/chat/${DEDUPED_CHAT_ID}`
1177-
expect(replace).toHaveBeenCalledWith(null, '', chatPath)
1182+
expect(replace).toHaveBeenCalledWith(null, '', `${chatPath}?mode=assistant`)
11781183

11791184
mockUsePathname.mockReturnValue(chatPath)
11801185
selectMode('assistant')

‎apps/sim/hooks/queries/organization-accounts.ts‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import { connectCredentialGroupInPopup } from '@/lib/credential-groups/oauth-pop
4747
import { isDesktopApp } from '@/lib/desktop'
4848
import { connectDesktopSource } from '@/lib/desktop/source-connect'
4949
import { assistantActionKeys } from '@/hooks/queries/assistant-actions'
50+
import { organizationHomeKeys } from '@/hooks/queries/organization-home'
5051
import { personalCredentialKeys } from '@/hooks/queries/personal-credentials'
5152
import { mcpKeys } from '@/hooks/queries/utils/mcp-keys'
5253
import {
@@ -154,6 +155,9 @@ export function useDisconnectPersonalOrganizationAccount() {
154155
queryKey: organizationAccountsKeys.detail(organizationId),
155156
}),
156157
queryClient.invalidateQueries({ queryKey: organizationAccountsKeys.personal() }),
158+
queryClient.invalidateQueries({
159+
queryKey: organizationHomeKeys.organization(organizationId),
160+
}),
157161
])
158162
router.refresh()
159163
},
@@ -251,13 +255,17 @@ export function useUpdateOrganizationAccounts() {
251255
queryKey: slackSearchKeys.organizationManifests(organizationId),
252256
}),
253257
queryClient.invalidateQueries({ queryKey: slackSearchKeys.list(organizationId) }),
258+
queryClient.invalidateQueries({
259+
queryKey: organizationHomeKeys.organization(organizationId),
260+
}),
254261
]),
255262
})
256263
}
257264

258265
async function refreshAccounts(client: ReturnType<typeof useQueryClient>) {
259266
await Promise.all([
260267
client.invalidateQueries({ queryKey: organizationAccountsKeys.all }),
268+
client.invalidateQueries({ queryKey: organizationHomeKeys.all }),
261269
client.invalidateQueries({ queryKey: assistantActionKeys.permissions() }),
262270
client.invalidateQueries({ queryKey: personalCredentialKeys.lists() }),
263271
client.invalidateQueries({ queryKey: mcpKeys.managedCatalog() }),

‎apps/sim/hooks/queries/organization-home.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export function useOrganizationHomeEmail(
3232
queryKey: organizationHomeKeys.email(organizationId),
3333
queryFn: ({ signal }) =>
3434
requestJson(getOrganizationHomeEmailContract, { params: { id: organizationId }, signal }),
35-
enabled,
35+
enabled: Boolean(organizationId) && enabled,
3636
staleTime: EMAIL_STALE_TIME,
3737
refetchOnWindowFocus: true,
3838
})
@@ -52,7 +52,7 @@ export function useOrganizationHomeCalendar(
5252
query: { date, timeZone },
5353
signal,
5454
}),
55-
enabled,
55+
enabled: Boolean(organizationId && date) && enabled,
5656
staleTime: CALENDAR_STALE_TIME,
5757
refetchOnWindowFocus: true,
5858
})

‎apps/sim/hooks/queries/search-history.ts‎

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,10 @@ import {
99
type SearchHistoryResponse,
1010
} from '@/lib/api/contracts/knowledge/search-history'
1111
import { useSession } from '@/lib/auth/client'
12-
13-
export const SEARCH_HISTORY_STALE_TIME = 30_000
14-
export const searchHistoryKeys = {
15-
all: ['search-history'] as const,
16-
lists: () => [...searchHistoryKeys.all, 'list'] as const,
17-
list: (organizationId: string | undefined, userId: string | undefined) =>
18-
[...searchHistoryKeys.lists(), organizationId ?? '', userId ?? ''] as const,
19-
}
12+
import {
13+
SEARCH_HISTORY_STALE_TIME,
14+
searchHistoryKeys,
15+
} from '@/hooks/queries/utils/search-history-keys'
2016

2117
export function useSearchHistory(organizationId: string | undefined) {
2218
const { data: session } = useSession()
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export const SEARCH_HISTORY_STALE_TIME = 30_000
2+
3+
export const searchHistoryKeys = {
4+
all: ['search-history'] as const,
5+
lists: () => [...searchHistoryKeys.all, 'list'] as const,
6+
list: (organizationId: string | undefined, userId: string | undefined) =>
7+
[...searchHistoryKeys.lists(), organizationId ?? '', userId ?? ''] as const,
8+
}

‎apps/sim/lib/organizations/application/home-widgets.integration.ts‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -337,16 +337,18 @@ describe('whose mail a widget reads', () => {
337337
describe('what a widget reports', () => {
338338
it('refuses a member with no integrations surface, before any provider', async () => {
339339
serveProviders()
340+
const real = await organizationSurface.getOrganizationSurfaceContext(organizationId, memberId)
341+
if (!real) throw new Error('Fixture member has no organization surface')
340342
const surface = vi.spyOn(organizationSurface, 'getOrganizationSurfaceContext')
341-
surface.mockResolvedValueOnce(null)
343+
surface.mockResolvedValueOnce({ ...real, connectedAccountsAvailable: false })
342344
await expect(
343345
readOrganizationHomeEmail.execute({ principal: session(memberId), input })
344346
).rejects.toMatchObject({ code: 'forbidden' })
345347
expect(provider).not.toHaveBeenCalled()
346348
surface.mockRestore()
347349
})
348350

349-
it('asks the provider once more with a fresh token when two reads race a rejection', async () => {
351+
it('settles both reads on reconnect when they race a token the provider refuses', async () => {
350352
provider.mockImplementation(async () => providerResponse({ error: 'provider text' }, 401))
351353
const [email, calendar] = await Promise.all([
352354
readOrganizationHomeEmail.execute({ principal: session(memberId), input }),

‎apps/sim/lib/organizations/application/home-widgets.ts‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ import { getOrganizationSurfaceContext } from '@/lib/organizations/surface'
5050

5151
const logger = createLogger('OrganizationHomeWidgets')
5252

53-
/** A widget gives up on a slow provider rather than holding its request open. */
5453
const READ_TIMEOUT_MS = 20_000
5554

5655
/**

0 commit comments

Comments
 (0)