Skip to content

Commit 5ee3cdd

Browse files
committed
Merge branch 'wip/rv-search' into feat/org-home-redesign
2 parents 89a575f + ce82c6e commit 5ee3cdd

27 files changed

Lines changed: 1669 additions & 475 deletions

File tree

‎apps/sim/app/o/[organizationId]/components/search-landing-history.tsx‎

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

3-
import { type ReactNode, useState } from 'react'
4-
import { Chip, cn, toast } from '@sim/emcn'
3+
import type { ReactNode } from 'react'
4+
import { Chip, cn } from '@sim/emcn'
55
import { Clock } from '@sim/emcn/icons'
66
import { inter } from '@/app/_styles/fonts/inter/inter'
7+
import { useSearchLandingHistory } from '@/app/o/[organizationId]/components/use-search-landing-history'
78
import { SourceCard } from '@/app/workspace/[workspaceId]/home/components/message-content/components/source-card'
8-
import { useClearSearchHistory, useSearchHistory } from '@/hooks/queries/search-history'
99

1010
interface SearchLandingHistoryProps {
1111
organizationId: string
@@ -21,13 +21,8 @@ export function SearchLandingHistory({
2121
onSearch,
2222
children,
2323
}: SearchLandingHistoryProps) {
24-
const history = useSearchHistory(organizationId)
25-
const clear = useClearSearchHistory(organizationId, userId)
26-
const [selection, setSelection] = useState<'sources' | 'queries' | null>(null)
27-
const data = history.isError ? undefined : history.data
28-
const sources = data?.sources.slice(0, 5) ?? []
29-
const queries = data?.queries.slice(0, 5) ?? []
30-
const selected = selection ?? (sources.length > 0 ? 'sources' : 'queries')
24+
const { failed, retrying, retry, sources, queries, selected, setSelection, clear, isClearing } =
25+
useSearchLandingHistory(organizationId, userId)
3126
return (
3227
<div className={cn('relative w-full min-w-0', inter.className)}>
3328
{children}
@@ -51,12 +46,7 @@ export function SearchLandingHistory({
5146
Recent searches
5247
</Chip>
5348
</div>
54-
<Chip
55-
disabled={clear.isPending}
56-
onClick={() =>
57-
clear.mutate(undefined, { onError: (error) => toast.error(error.message) })
58-
}
59-
>
49+
<Chip disabled={isClearing} onClick={clear}>
6050
Clear history
6151
</Chip>
6252
</div>
@@ -102,10 +92,10 @@ export function SearchLandingHistory({
10292
</div>
10393
</section>
10494
)}
105-
{history.isError && (
95+
{failed && (
10696
<div className='mt-6 flex items-center gap-2 px-4 text-[var(--text-tertiary)] text-small'>
10797
Recent activity couldn’t load.
108-
<Chip onClick={() => void history.refetch()} disabled={history.isFetching}>
98+
<Chip onClick={retry} disabled={retrying}>
10999
Try again
110100
</Chip>
111101
</div>
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { useState } from 'react'
2+
import { toast } from '@sim/emcn'
3+
import { useClearSearchHistory, useSearchHistory } from '@/hooks/queries/search-history'
4+
5+
const HISTORY_RECENT_LIMIT = 5
6+
7+
/** The viewer's private recent activity under the Search field, and which list is showing. */
8+
export function useSearchLandingHistory(organizationId: string, userId: string) {
9+
const history = useSearchHistory(organizationId)
10+
const clearHistory = useClearSearchHistory(organizationId, userId)
11+
const [selection, setSelection] = useState<'sources' | 'queries' | null>(null)
12+
const data = history.isError ? undefined : history.data
13+
const sources = data?.sources.slice(0, HISTORY_RECENT_LIMIT) ?? []
14+
const queries = data?.queries.slice(0, HISTORY_RECENT_LIMIT) ?? []
15+
return {
16+
failed: history.isError,
17+
retrying: history.isFetching,
18+
retry: () => void history.refetch(),
19+
sources,
20+
queries,
21+
selected: selection ?? (sources.length > 0 ? 'sources' : 'queries'),
22+
setSelection,
23+
isClearing: clearHistory.isPending,
24+
clear: () => clearHistory.mutate(undefined, { onError: (error) => toast.error(error.message) }),
25+
}
26+
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ vi.mock('@/lib/mothership/chat/lifecycle', () => mothershipChatLifecycleMock)
1616
vi.mock('@/app/o/[organizationId]/integrations/integrations', () => ({
1717
OrganizationIntegrations: () => <div>Integrations</div>,
1818
}))
19-
vi.mock('@/app/o/[organizationId]/search/search', () => ({
20-
OrganizationSearch: () => <div>Standalone Search</div>,
19+
vi.mock('@/platform/app/(shell)/search/_components/search-surface/search-surface', () => ({
20+
OrganizationSearchSurface: () => <div>Standalone Search</div>,
2121
}))
2222
vi.mock('@/app/o/[organizationId]/components/organization-conversation', () => ({
2323
OrganizationConversation: ({ requestMode }: { requestMode?: string }) => (

‎apps/sim/app/o/[organizationId]/links/sim-link-shortcut.tsx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ function useNestedSimLinkCreator(url: string, title: string): SimLinkCreator {
4040
}
4141

4242
/** The first link that already opens `url`, and a copy of its address. */
43-
function useExistingSimLink(organizationId: string, url: string) {
44-
const { data, isPending, isError } = useSimLinksForUrl(organizationId, url)
43+
export function useExistingSimLink(organizationId: string, url: string, enabled = true) {
44+
const { data, isPending, isError } = useSimLinksForUrl(organizationId, url, enabled)
4545
const { copied, copy } = useCopyToClipboard({ resetMs: 1500 })
4646
const existing = data?.at(0)
4747
const copyExisting = async () => {

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ import { getSession } from '@/lib/auth'
55
import { getAccessibleCopilotChatAuth } from '@/lib/mothership/chat/lifecycle'
66
import { organizationRoutes, WORKSPACE_SETTINGS_PATH } from '@/lib/navigation/paths'
77
import { getOrganizationSurfaceContext } from '@/lib/organizations/surface'
8-
import { OrganizationSearch } from '@/app/o/[organizationId]/search/search'
98
import { HomeFallback } from '@/app/workspace/[workspaceId]/home/home-fallback'
9+
import { OrganizationSearchSurface } from '@/platform/app/(shell)/search/_components/search-surface/search-surface'
1010

1111
export const metadata: Metadata = { title: 'Assistant Search' }
1212

@@ -29,7 +29,7 @@ export default async function OrganizationSearchChatPage({
2929
if (chat.mode !== 'assistant') redirect(organizationRoutes(organizationId).chat(chatId))
3030
return (
3131
<Suspense fallback={<HomeFallback />}>
32-
<OrganizationSearch userId={session.user.id} chatId={chatId} />
32+
<OrganizationSearchSurface userId={session.user.id} chatId={chatId} />
3333
</Suspense>
3434
)
3535
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ import { notFound, redirect } from 'next/navigation'
44
import { getSession } from '@/lib/auth'
55
import { WORKSPACE_SETTINGS_PATH } from '@/lib/navigation/paths'
66
import { getOrganizationSurfaceContext } from '@/lib/organizations/surface'
7-
import { OrganizationSearch } from '@/app/o/[organizationId]/search/search'
87
import { HomeFallback } from '@/app/workspace/[workspaceId]/home/home-fallback'
8+
import { OrganizationSearchSurface } from '@/platform/app/(shell)/search/_components/search-surface/search-surface'
99

1010
export const metadata: Metadata = { title: 'Search' }
1111

@@ -22,7 +22,7 @@ export default async function OrganizationSearchPage({
2222
if (!context.searchAccess.memberScoped) redirect(WORKSPACE_SETTINGS_PATH)
2323
return (
2424
<Suspense fallback={<HomeFallback />}>
25-
<OrganizationSearch userId={session.user.id} />
25+
<OrganizationSearchSurface userId={session.user.id} />
2626
</Suspense>
2727
)
2828
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
'use client'
2+
3+
import { lazy, type ReactNode, Suspense } from 'react'
4+
import { cn } from '@sim/emcn'
5+
import type { OrganizationSearchState } from '@/app/o/[organizationId]/search/use-organization-search'
6+
import { RESOURCE_HEADER_CLASSES } from '@/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-tabs/resource-tab-controls'
7+
import { HomeFallback } from '@/app/workspace/[workspaceId]/home/home-fallback'
8+
9+
const OrganizationConversation = lazy(() =>
10+
import('@/app/o/[organizationId]/components/organization-conversation').then((module) => ({
11+
default: module.OrganizationConversation,
12+
}))
13+
)
14+
15+
interface SearchFrameProps {
16+
search: OrganizationSearchState
17+
/** The view switch and New control, shown over both views. */
18+
header: ReactNode
19+
/** The Search view. It renders beside the conversation, never around it. */
20+
children: ReactNode
21+
}
22+
23+
/**
24+
* Hosts the two views of Search. Assistant stays mounted and `inert` while Search shows, so a running
25+
* answer keeps streaming and its draft survives a switch.
26+
*/
27+
export function SearchFrame({ search, header, children }: SearchFrameProps) {
28+
const { activeMode } = search
29+
return (
30+
<div className='h-full min-h-0 min-w-0 bg-[var(--bg)] pt-[var(--workspace-content-title-bar-inset)]'>
31+
<div className='flex h-full min-h-0 min-w-0 flex-col [--workspace-content-title-bar-inset:0px]'>
32+
{search.assistantMounted && (
33+
<div
34+
inert={activeMode !== 'assistant'}
35+
className={cn(
36+
'min-h-0 min-w-0 flex-1 flex-col',
37+
activeMode === 'assistant' ? 'flex' : 'hidden'
38+
)}
39+
>
40+
<Suspense fallback={<HomeFallback header={header} />}>
41+
<OrganizationConversation
42+
key={search.conversationInstance}
43+
requestMode='assistant'
44+
chatId={search.chatId}
45+
header={header}
46+
active={activeMode === 'assistant'}
47+
summaryRequest={search.pendingSummary}
48+
onSummaryConsumed={search.consumeSummary}
49+
/>
50+
</Suspense>
51+
</div>
52+
)}
53+
{!activeMode && <HomeFallback />}
54+
{activeMode === 'search' && (
55+
<>
56+
<div className={cn(RESOURCE_HEADER_CLASSES.layout, RESOURCE_HEADER_CLASSES.content)}>
57+
{header}
58+
</div>
59+
<div className='flex min-h-0 min-w-0 flex-1 flex-col'>{children}</div>
60+
</>
61+
)}
62+
</div>
63+
</div>
64+
)
65+
}

‎apps/sim/app/o/[organizationId]/search/search-results-view.tsx‎

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import {
77
useScrollEdges,
88
} from '@sim/emcn'
99
import type { WorkspaceSearchFilters } from '@/lib/api/contracts/knowledge'
10-
import type { SearchResource } from '@/lib/mothership/generated/resources'
1110
import { OrganizationLanding } from '@/app/o/[organizationId]/components/organization-landing'
1211
import { PAGE_COLUMN_CLASS } from '@/app/o/[organizationId]/components/organization-page'
1312
import { useOrganizationContext } from '@/app/o/[organizationId]/providers/organization-provider'
@@ -21,16 +20,10 @@ interface SearchResultsViewProps {
2120
composer: ReactNode
2221
query: string
2322
onSummarize?: (message: string, filters: WorkspaceSearchFilters) => void
24-
onSearchChange?: (search: SearchResource) => void
2523
}
2624

2725
/** Standalone Search uses the original centered field and docked results layout. */
28-
export function SearchResultsView({
29-
composer,
30-
query,
31-
onSummarize,
32-
onSearchChange,
33-
}: SearchResultsViewProps) {
26+
export function SearchResultsView({ composer, query, onSummarize }: SearchResultsViewProps) {
3427
const { organization } = useOrganizationContext()
3528
const scope = { kind: 'organization' as const, organizationId: organization.id }
3629
const scrollContainerRef = useRef<HTMLDivElement>(null)
@@ -61,12 +54,7 @@ export function SearchResultsView({
6154
{/* The rows carry their own `px-2`; this gutter brings each row's mark under the
6255
field's own search glyph, so results read as a column hanging from the field. */}
6356
<div ref={scrollContentRef} className={cn(PAGE_COLUMN_CLASS, 'px-8')}>
64-
<KnowledgeSearchResults
65-
scope={scope}
66-
query={query}
67-
onSummarize={onSummarize}
68-
onSearchChange={onSearchChange}
69-
/>
57+
<KnowledgeSearchResults scope={scope} query={query} onSummarize={onSummarize} />
7058
</div>
7159
</div>
7260
</>

0 commit comments

Comments
 (0)