Skip to content

Commit 5216949

Browse files
committed
improvement(org): simplify the consolidation after review
1 parent c1758c7 commit 5216949

26 files changed

Lines changed: 205 additions & 273 deletions

File tree

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import type { ReactNode } from 'react'
2+
import { type DehydratedState, HydrationBoundary } from '@tanstack/react-query'
3+
4+
interface HydratedProps {
5+
state: Promise<DehydratedState>
6+
children: ReactNode
7+
}
8+
9+
/**
10+
* Children painted from a page's server reads. Put it inside a `Suspense`: it holds only that
11+
* boundary, so the page's arrangement paints before the reads settle.
12+
*/
13+
export async function Hydrated({ state, children }: HydratedProps) {
14+
return <HydrationBoundary state={await state}>{children}</HydrationBoundary>
15+
}

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ import { organizationRoutes, WORKSPACE_SETTINGS_PATH } from '@/lib/navigation/pa
77
import { getOrganizationSurfaceContext } from '@/lib/organizations/surface'
88
import OrganizationChatLoading from '@/app/o/[organizationId]/chat/[chatId]/loading'
99
import { OrganizationConversation } from '@/app/o/[organizationId]/components/organization-conversation'
10-
import { getOrganizationBuildRedirect } from '@/app/o/[organizationId]/home/home-redirect'
10+
import {
11+
canUseOrganizationChat,
12+
getOrganizationBuildRedirect,
13+
} from '@/app/o/[organizationId]/home/home-redirect'
1114

1215
export const metadata: Metadata = { title: 'Chat' }
1316

@@ -21,7 +24,7 @@ export default async function OrganizationChatPage({
2124
if (!session?.user?.id) notFound()
2225
const context = await getOrganizationSurfaceContext(organizationId, session.user.id)
2326
if (!context) notFound()
24-
if (!context.mothershipAvailable || (!context.canBuild && !context.searchAccess.memberScoped)) {
27+
if (!canUseOrganizationChat(context)) {
2528
const unavailableRedirect = getOrganizationBuildRedirect(context, organizationId)
2629
if (unavailableRedirect) redirect(unavailableRedirect)
2730
}

‎apps/sim/app/o/[organizationId]/home/components/get-started/get-started.tsx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ export function GetStarted() {
7777
animationsEnabled={animationsEnabled}
7878
onToggle={handleToggleExpanded}
7979
>
80-
{steps.map(({ id, label, href, complete }, i) => {
80+
{steps.map(({ id, title, href, complete }, i) => {
8181
return (
8282
<SettingsGuardedLink key={id} href={href} className={cn(ROW_CLASS, i > 0 && 'border-t')}>
8383
<StepMark complete={complete} />
@@ -87,7 +87,7 @@ export function GetStarted() {
8787
complete ? 'text-[var(--brand-blue)]' : 'text-[var(--text-body)]'
8888
)}
8989
>
90-
{label}
90+
{title}
9191
</span>
9292
<ArrowRight className='size-[16px] shrink-0 text-[var(--text-icon)]' />
9393
</SettingsGuardedLink>

‎apps/sim/app/o/[organizationId]/home/components/get-started/use-get-started-steps.ts‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ import { useHasSearchAuthorization } from '@/hooks/queries/oauth-provider'
55
import { useOrganizationAccounts } from '@/hooks/queries/organization-accounts'
66
import { useSearchIntegrations } from '@/hooks/queries/search-integrations'
77

8-
export type GetStartedStepId = 'connect-integration' | 'connect-sim-search'
9-
108
/**
119
* The onboarding steps this viewer has, in the order a new organization works through them, each
1210
* done or not from the organization's real state: a configured integration and an OAuth app
@@ -29,8 +27,9 @@ export function useGetStartedSteps(enabled = true) {
2927
...(canConnectIntegrations
3028
? [
3129
{
32-
id: 'connect-integration' as const,
33-
label: 'Connect an integration',
30+
id: 'connect-integration',
31+
title: 'Connect an integration',
32+
detail: 'Choose what Sim can search for you',
3433
href: viewer.isAdmin ? routes.settingsSection('integrations') : routes.integrations,
3534
complete: hasSearchIntegrationReady(accounts, integrations),
3635
},
@@ -39,8 +38,9 @@ export function useGetStartedSteps(enabled = true) {
3938
...(canUseSearchMcp
4039
? [
4140
{
42-
id: 'connect-sim-search' as const,
43-
label: 'Connect Sim Search MCP',
41+
id: 'connect-sim-search',
42+
title: 'Connect Sim Search MCP',
43+
detail: 'Use your company’s knowledge from your other AI tools',
4444
href: routes.settingsSection('search-mcp'),
4545
complete: hasSearchAuthorization,
4646
},

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import OrganizationSearchPage from '@/app/o/[organizationId]/search/page'
4242
const mockChat = mothershipChatLifecycleMockFns.mockGetAccessibleCopilotChatAuth
4343

4444
const params = Promise.resolve({ organizationId: 'org-1', chatId: 'chat-1' })
45+
const searchParams = Promise.resolve({})
4546
const session = { user: { id: 'viewer', name: 'Taylor' }, session: { id: 'session-1' } }
4647

4748
describe('organization Search page gates', () => {
@@ -57,7 +58,7 @@ describe('organization Search page gates', () => {
5758

5859
it.each([
5960
['Home', () => OrganizationHomePage({ params })],
60-
['Search', () => OrganizationSearchPage({ params })],
61+
['Search', () => OrganizationSearchPage({ params, searchParams })],
6162
['chat', () => OrganizationChatPage({ params })],
6263
['organization entry', () => OrganizationPage({ params })],
6364
] as const)('denies %s to nonmembers before loading content', async (_name, open) => {
Lines changed: 11 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,9 @@
11
import { Suspense } from 'react'
2-
import type { SessionPrincipal } from '@sim/auth/principal'
3-
import { HydrationBoundary } from '@tanstack/react-query'
42
import type { Metadata } from 'next'
53
import { notFound, redirect } from 'next/navigation'
64
import { getSession } from '@/lib/auth'
7-
import {
8-
getOrganizationSurfaceContext,
9-
type OrganizationSurfaceContext,
10-
} from '@/lib/organizations/surface'
5+
import { getOrganizationSurfaceContext } from '@/lib/organizations/surface'
6+
import { Hydrated } from '@/app/o/[organizationId]/_redesign/hydrated'
117
import { getOrganizationHomeRedirect } from '@/app/o/[organizationId]/home/home-redirect'
128
import { HomeConversation } from '@/platform/app/(shell)/home/_components/home-conversation/home-conversation'
139
import { HomeDashboardSkeleton } from '@/platform/app/(shell)/home/_components/home-dashboard/home-dashboard-skeleton'
@@ -17,21 +13,6 @@ export const metadata: Metadata = {
1713
title: 'Home',
1814
}
1915

20-
interface PrefetchedHomeProps {
21-
surface: OrganizationSurfaceContext
22-
principal: SessionPrincipal
23-
requestMode: 'agent' | 'assistant'
24-
}
25-
26-
/** Holds only its own boundary, so the page's arrangement paints before these reads settle. */
27-
async function PrefetchedHome({ surface, principal, requestMode }: PrefetchedHomeProps) {
28-
return (
29-
<HydrationBoundary state={await prefetchHome(surface, principal)}>
30-
<HomeConversation requestMode={requestMode} />
31-
</HydrationBoundary>
32-
)
33-
}
34-
3516
export default async function OrganizationHomePage({
3617
params,
3718
}: {
@@ -48,11 +29,15 @@ export default async function OrganizationHomePage({
4829
const requestMode = context.canBuild ? 'agent' : 'assistant'
4930
return (
5031
<Suspense fallback={<HomeDashboardSkeleton requestMode={requestMode} />}>
51-
<PrefetchedHome
52-
surface={context}
53-
principal={{ kind: 'session', userId: session.user.id, sessionId: session.session.id }}
54-
requestMode={requestMode}
55-
/>
32+
<Hydrated
33+
state={prefetchHome(context, {
34+
kind: 'session',
35+
userId: session.user.id,
36+
sessionId: session.session.id,
37+
})}
38+
>
39+
<HomeConversation requestMode={requestMode} />
40+
</Hydrated>
5641
</Suspense>
5742
)
5843
}
Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,30 @@
11
import { type ComponentProps, Suspense } from 'react'
22
import type { SessionPrincipal } from '@sim/auth/principal'
3-
import { HydrationBoundary } from '@tanstack/react-query'
43
import type { OrganizationSurfaceContext } from '@/lib/organizations/surface'
4+
import { Hydrated } from '@/app/o/[organizationId]/_redesign/hydrated'
55
import { OrganizationIntegrations } from '@/app/o/[organizationId]/integrations/integrations'
66
import { IntegrationsHost } from '@/app/o/[organizationId]/integrations/integrations-host'
7-
import { IntegrationsPageFallback } from '@/platform/app/(shell)/integrations/_components/integrations-page/integrations-page'
7+
import { IntegrationsRouteFallback } from '@/platform/app/(shell)/integrations/_components/integrations-page/integrations-route'
88
import { prefetchIntegrations } from '@/platform/lib/data/prefetch'
99

1010
interface HydratedIntegrationsProps extends ComponentProps<typeof OrganizationIntegrations> {
1111
surface: OrganizationSurfaceContext
1212
principal: SessionPrincipal
1313
}
1414

15-
async function PrefetchedIntegrations({ surface, principal, ...route }: HydratedIntegrationsProps) {
16-
return (
17-
<HydrationBoundary state={await prefetchIntegrations(surface, principal)}>
18-
<OrganizationIntegrations {...route} />
19-
</HydrationBoundary>
20-
)
21-
}
22-
23-
/** The route, painted from the server's reads. The fallback holds only this boundary. */
24-
export function HydratedIntegrations(props: HydratedIntegrationsProps) {
15+
/** The route, painted from the server's reads. */
16+
export function HydratedIntegrations({ surface, principal, ...route }: HydratedIntegrationsProps) {
2517
return (
2618
<Suspense
2719
fallback={
2820
<IntegrationsHost>
29-
<IntegrationsPageFallback detail={Boolean(props.section)} />
21+
<IntegrationsRouteFallback detail={Boolean(route.section)} />
3022
</IntegrationsHost>
3123
}
3224
>
33-
<PrefetchedIntegrations {...props} />
25+
<Hydrated state={prefetchIntegrations(surface, principal)}>
26+
<OrganizationIntegrations {...route} />
27+
</Hydrated>
3428
</Suspense>
3529
)
3630
}
Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,47 @@
11
import { Suspense } from 'react'
2-
import type { SessionPrincipal } from '@sim/auth/principal'
3-
import { HydrationBoundary } from '@tanstack/react-query'
42
import type { Metadata } from 'next'
53
import { notFound, redirect } from 'next/navigation'
64
import { getSession } from '@/lib/auth'
75
import { WORKSPACE_SETTINGS_PATH } from '@/lib/navigation/paths'
86
import { getOrganizationSurfaceContext } from '@/lib/organizations/surface'
7+
import { Hydrated } from '@/app/o/[organizationId]/_redesign/hydrated'
8+
import { loadOrganizationSearchView } from '@/app/o/[organizationId]/search/search-params'
99
import { HomeFallback } from '@/app/workspace/[workspaceId]/home/home-fallback'
1010
import { OrganizationSearchSurface } from '@/platform/app/(shell)/search/_components/search-surface/search-surface'
1111
import { prefetchSearch } from '@/platform/lib/data/prefetch'
1212

1313
export const metadata: Metadata = { title: 'Search' }
1414

15-
interface PrefetchedSearchProps {
16-
organizationId: string
17-
principal: SessionPrincipal
18-
}
19-
20-
/** Holds only its own boundary, so the page's arrangement paints before the read settles. */
21-
async function PrefetchedSearch({ organizationId, principal }: PrefetchedSearchProps) {
22-
return (
23-
<HydrationBoundary state={await prefetchSearch(organizationId, principal)}>
24-
<OrganizationSearchSurface userId={principal.userId} />
25-
</HydrationBoundary>
26-
)
27-
}
28-
2915
export default async function OrganizationSearchPage({
3016
params,
17+
searchParams,
3118
}: {
3219
params: Promise<{ organizationId: string }>
20+
searchParams: Promise<Record<string, string | string[] | undefined>>
3321
}) {
34-
const { organizationId } = await params
22+
const [{ organizationId }, query] = await Promise.all([params, searchParams])
3523
const session = await getSession()
3624
if (!session?.user?.id) notFound()
3725
const context = await getOrganizationSurfaceContext(organizationId, session.user.id)
3826
if (!context) notFound()
3927
if (!context.searchAccess.memberScoped) redirect(WORKSPACE_SETTINGS_PATH)
28+
const surface = <OrganizationSearchSurface userId={session.user.id} />
29+
const { q, mode } = loadOrganizationSearchView(query)
4030
return (
4131
<Suspense fallback={<HomeFallback />}>
42-
<PrefetchedSearch
43-
organizationId={organizationId}
44-
principal={{ kind: 'session', userId: session.user.id, sessionId: session.session.id }}
45-
/>
32+
{q.trim() || mode === 'assistant' ? (
33+
surface
34+
) : (
35+
<Hydrated
36+
state={prefetchSearch(organizationId, {
37+
kind: 'session',
38+
userId: session.user.id,
39+
sessionId: session.session.id,
40+
})}
41+
>
42+
{surface}
43+
</Hydrated>
44+
)}
4645
</Suspense>
4746
)
4847
}

‎apps/sim/app/o/[organizationId]/search/search-params.ts‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
import { parseAsString, parseAsStringLiteral } from 'nuqs/server'
1+
import { createLoader, parseAsString, parseAsStringLiteral } from 'nuqs/server'
22

33
export const organizationSearchParsers = {
44
q: parseAsString.withDefault(''),
55
mode: parseAsStringLiteral(['search', 'assistant']),
66
} as const
77

8+
export const loadOrganizationSearchView = createLoader(organizationSearchParsers)
9+
810
/** A query is a filter-like view change, not navigation: replace, and clear when empty. */
911
export const organizationSearchUrlKeys = {
1012
history: 'replace',

‎apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/special-tags/search-integration-connection.tsx‎

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,7 @@ import {
88
InteractionCardActionRow,
99
} from '@/app/workspace/[workspaceId]/home/components/message-content/components/interaction-card'
1010
import { BrandIcon } from '@/blocks/brand-icon'
11-
import {
12-
searchConnectionControl,
13-
useSearchIntegrationConnection,
14-
} from '@/hooks/use-search-integration-connection'
11+
import { useSearchIntegrationConnection } from '@/hooks/use-search-integration-connection'
1512

1613
export interface SearchIntegrationConnectionProps {
1714
organizationId: string
@@ -49,8 +46,7 @@ function SearchIntegrationConnectionControl({
4946
controlId,
5047
onConnected,
5148
})
52-
const { icon, label, disabled } = searchConnectionControl(target, connection)
53-
const connected = connection.connected
49+
const { icon, label, disabled, connected } = connection
5450
const handleConnect = () => void connection.connect()
5551
const content = (
5652
<>

0 commit comments

Comments
 (0)