Skip to content

Commit 967e363

Browse files
committed
feat(search): move assistant into organization search
1 parent 77dfe17 commit 967e363

37 files changed

Lines changed: 606 additions & 690 deletions

‎apps/sim/app/o/[organizationId]/chat/[chatId]/page.tsx‎

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ import type { Metadata } from 'next'
33
import { notFound, redirect } from 'next/navigation'
44
import { getSession } from '@/lib/auth'
55
import { getAccessibleCopilotChatAuth } from '@/lib/mothership/chat/lifecycle'
6-
import { WORKSPACE_SETTINGS_PATH } from '@/lib/navigation/paths'
6+
import { organizationRoutes, WORKSPACE_SETTINGS_PATH } from '@/lib/navigation/paths'
77
import { getOrganizationSurfaceContext } from '@/lib/organizations/surface'
88
import OrganizationChatLoading from '@/app/o/[organizationId]/chat/[chatId]/loading'
9+
import { OrganizationConversation } from '@/app/o/[organizationId]/components/organization-conversation'
910
import { getOrganizationHomeRedirect } from '@/app/o/[organizationId]/home/home-redirect'
10-
import { OrganizationHome } from '@/app/o/[organizationId]/home/organization-home'
1111

1212
export const metadata: Metadata = { title: 'Chat' }
1313

@@ -21,27 +21,24 @@ export default async function OrganizationChatPage({
2121
if (!session?.user?.id) notFound()
2222
const context = await getOrganizationSurfaceContext(organizationId, session.user.id)
2323
if (!context) notFound()
24-
const homeRedirect = getOrganizationHomeRedirect(context, organizationId)
25-
if (homeRedirect) redirect(homeRedirect)
24+
if (!context.mothershipAvailable || (!context.canBuild && !context.searchAccess.memberScoped)) {
25+
const unavailableRedirect = getOrganizationHomeRedirect(context, organizationId)
26+
if (unavailableRedirect) redirect(unavailableRedirect)
27+
}
2628
const chat = await getAccessibleCopilotChatAuth(chatId, session.user.id, {
2729
principal: { kind: 'session', userId: session.user.id, sessionId: session.session.id },
2830
})
2931
if (!chat || chat.type !== 'mothership' || chat.organizationId !== organizationId) notFound()
3032
if (chat.mode === 'assistant') {
3133
if (!context.searchAccess.memberScoped) redirect(WORKSPACE_SETTINGS_PATH)
32-
return (
33-
<Suspense fallback={<OrganizationChatLoading />}>
34-
<OrganizationHome
35-
requestMode='assistant'
36-
userName={session.user.name ?? undefined}
37-
chatId={chatId}
38-
/>
39-
</Suspense>
40-
)
34+
redirect(organizationRoutes(organizationId).searchChat(chatId))
4135
}
36+
const homeRedirect = getOrganizationHomeRedirect(context, organizationId)
37+
if (homeRedirect) redirect(homeRedirect)
38+
4239
return (
4340
<Suspense fallback={<OrganizationChatLoading />}>
44-
<OrganizationHome
41+
<OrganizationConversation
4542
userName={session.user.name ?? undefined}
4643
chatId={chatId}
4744
requestMode={chat.mode}

apps/sim/app/o/[organizationId]/home/organization-home.test.tsx renamed to apps/sim/app/o/[organizationId]/components/organization-conversation.test.tsx

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,12 @@ import {
1515
organizationProviderMockFns,
1616
} from '@sim/testing/mocks/organization-provider.mock'
1717
import { reactQueryMock } from '@sim/testing/mocks/react-query.mock'
18+
import { toRecord } from '@sim/utils/object'
1819
import { NuqsTestingAdapter } from 'nuqs/adapters/testing'
1920
import { createRoot, type Root } from 'react-dom/client'
2021
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
2122
import type { AuthorizedAppsPage } from '@/lib/api/contracts/user'
2223
import { useMothershipDraftsStore } from '@/stores/mothership-drafts/store'
23-
import { useOrganizationChatModeStore } from '@/stores/organization-chat-mode/store'
2424

2525
const mocks = vi.hoisted(() => ({
2626
plan: false,
@@ -90,8 +90,8 @@ vi.mock('@/app/workspace/[workspaceId]/home/components/mothership-chat', () => (
9090
MothershipChat: mocks.renderer,
9191
}))
9292

93+
import { OrganizationConversation } from '@/app/o/[organizationId]/components/organization-conversation'
9394
import type { Composer } from '@/app/o/[organizationId]/home/components/composer'
94-
import { OrganizationHome } from '@/app/o/[organizationId]/home/organization-home'
9595

9696
const mockSession = authClientMockFns.mockUseSession
9797
const mockContext = organizationProviderMockFns.mockUseOrganizationContext
@@ -106,7 +106,6 @@ beforeEach(() => {
106106
mocks.plan = false
107107
mocks.activeResource = null
108108
mockSession.mockReturnValue({ data: { user: { id: 'reader' } } })
109-
useOrganizationChatModeStore.setState({ modes: {}, assistantSearchLevels: {} })
110109
mocks.resourcePanel.mockImplementation(({ children }: { children: ReactNode }) => children)
111110
vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT', true)
112111
vi.stubGlobal(
@@ -151,7 +150,9 @@ function renderHome(element: ReactNode, searchParams = '') {
151150
)
152151
}
153152
function composerProps(): ComponentProps<typeof Composer> {
154-
return mocks.composer.mock.lastCall![0]
153+
const call = mocks.composer.mock.lastCall
154+
if (!call) throw new Error('Composer has not rendered')
155+
return call[0]
155156
}
156157

157158
async function attachDraftImage() {
@@ -186,11 +187,17 @@ describe('organization home', () => {
186187
sendMessage: mocks.send,
187188
})
188189
mocks.renderer.mockImplementation(({ composer }: { composer: ReactNode }) => composer)
189-
await act(async () => renderHome(<OrganizationHome chatId='chat-a' />))
190+
await act(async () =>
191+
renderHome(<OrganizationConversation requestMode='agent' chatId='chat-a' />)
192+
)
190193
await act(async () => composerProps().onChange('A draft for chat A'))
191-
await act(async () => renderHome(<OrganizationHome chatId='chat-a' />))
194+
await act(async () =>
195+
renderHome(<OrganizationConversation requestMode='agent' chatId='chat-a' />)
196+
)
192197
expect(composerProps().value).toBe('A draft for chat A')
193-
await act(async () => renderHome(<OrganizationHome chatId='chat-b' />))
198+
await act(async () =>
199+
renderHome(<OrganizationConversation requestMode='agent' chatId='chat-b' />)
200+
)
194201
expect(composerProps().value).toBe('')
195202
expect(mocks.chat).toHaveBeenLastCalledWith(
196203
{ organizationId: 'organization-a' },
@@ -205,22 +212,32 @@ describe('organization home', () => {
205212

206213
it('isolates saved drafts by user, organization, and conversation', async () => {
207214
mocks.renderer.mockImplementation(({ composer }: { composer: ReactNode }) => composer)
208-
await act(async () => renderHome(<OrganizationHome chatId='chat-a' requestMode='assistant' />))
215+
await act(async () =>
216+
renderHome(<OrganizationConversation chatId='chat-a' requestMode='assistant' />)
217+
)
209218
await attachDraftImage()
210219
await act(async () => composerProps().onChange('Chat A follow-up'))
211-
await act(async () => renderHome(<OrganizationHome chatId='chat-b' requestMode='assistant' />))
220+
await act(async () =>
221+
renderHome(<OrganizationConversation chatId='chat-b' requestMode='assistant' />)
222+
)
212223
expect(composerProps().value).toBe('')
213-
await act(async () => renderHome(<OrganizationHome chatId='chat-a' requestMode='assistant' />))
224+
await act(async () =>
225+
renderHome(<OrganizationConversation chatId='chat-a' requestMode='assistant' />)
226+
)
214227
expect(composerProps().value).toBe('Chat A follow-up')
215228
expect(composerProps().files.attachedFiles[0]?.key).toBe('image-key')
216229
mockSession.mockReturnValue({ data: { user: { id: 'other-user' } } })
217-
await act(async () => renderHome(<OrganizationHome chatId='chat-a' requestMode='assistant' />))
230+
await act(async () =>
231+
renderHome(<OrganizationConversation chatId='chat-a' requestMode='assistant' />)
232+
)
218233
expect(composerProps().value).toBe('')
219234
mockSession.mockReturnValue({ data: { user: { id: 'reader' } } })
220235
mockContext.mockReturnValue({
221-
...mockContext(),
236+
...toRecord(mockContext()),
222237
organization: { id: 'other-org', name: 'Other' },
223238
})
224-
await act(async () => renderHome(<OrganizationHome chatId='chat-a' requestMode='assistant' />))
239+
await act(async () =>
240+
renderHome(<OrganizationConversation chatId='chat-a' requestMode='assistant' />)
241+
)
225242
expect(composerProps().value).toBe('')
226243
})

0 commit comments

Comments
 (0)