Skip to content

Commit 5cd02a6

Browse files
committed
improvement(platform): consolidate organization integrations
1 parent 3fcc730 commit 5cd02a6

59 files changed

Lines changed: 1665 additions & 413 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.agents/skills/implement-platform/SKILL.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Start in the Sim checkout containing the requested product work. Read the [Platf
1010
1. **Establish scope on the real route.** Identify the product entry, interaction, phase, and affected consumers. When rebuilding an existing workflow, trace the old navigation target, its access gate, and the actions reachable by that viewer. Use that evidence to decide which controls belong on the new page before consulting the broader API or provider catalog. Phase 1 owns foundations and the library. Phase 2 owns working UI and transient interaction state, with examples supplied through the server fixture flag. Phase 3 connects real data and operations. Reuse the same renderer in the browser and Sim Desktop. Read the installed Next guides for the file structure or framework behavior you are changing.
1111
For organization Settings, inventory the old sidebar in order, its viewer/deployment gates and every action reachable from each section. Map old header buttons to the row or section they act on. Verify the same destination and drilldown on the fixture and authenticated routes; keep real writes outside fixture sessions.
1212
2. **Resolve design intent.** For Paper-backed work, use `$sim-paper-design` to locate the live Shared or Platform master and the product states that use it. Exclude Marcus. Preserve the key and theme roles. Treat artboard sizes as reference measurements. For code-first changes, follow the latest explicit product direction and inspect the rendered route. Identify any source you could not verify. Update the Paper canvas when the request includes Paper changes.
13-
3. **Choose or change the owner.** Start with the behavior. An action, navigation row, value picker, or disclosure has an existing keyboard and pointer contract. Different interactions may share visual roles while retaining different semantics. Use the existing family and named props for a change in size, intent, icon, or content. Put unique anatomy and its CSS beside the route component. Keep page files free of styling. Only EMCN owns the appearance of its controls. Use Base UI there for complex keyboard, floating, and dismissal behavior.
14-
4. **Trace the full change.** On a second independent feature use, promote the original implementation and migrate both callers. For consolidation, compare semantics, map every consumer to its replacement, then remove the obsolete implementation, export, variant styles, and unused registrations. For deletion, also check menus, preferences, fixture data, workbench references, and tests that existed only for the removed feature. Preserve unrelated consumers and historical Paper evidence. A new variant needs a reachable product use. Edit the source or compiler for generated foundations and recipes.
13+
3. **Choose or change the owner.** Start with the behavior. An action, navigation row, value picker, or disclosure has an existing keyboard and pointer contract. Different interactions may share visual roles while retaining different semantics. Use the existing family and named props for a change in size, intent, icon, or content. Toolbar choices use ChoiceMenu, as Home's period does; Select/SelectField belong to forms and settings. Compare the rendered trigger, not just its variant name. Put unique anatomy and its CSS together in `_components/<component>/<component>.tsx` and `<component>.module.css`. Match an existing semantic rhythm, such as Home's ActivityRow, for a custom composition. Keep page files free of styling. Only EMCN owns the appearance of its controls. Use Base UI there for complex keyboard, floating, and dismissal behavior.
14+
4. **Trace the full change.** On a second independent feature use, promote the original implementation and migrate both callers. For consolidation, compare semantics, map every consumer to its replacement, then remove the obsolete implementation, export, variant styles, and unused registrations. For deletion, also check menus, preferences, fixture data, workbench references, and tests that existed only for the removed feature. Preserve unrelated consumers and historical Paper evidence. Before adding a shared visual variant, name at least two independent real consumers. A single feature composes the existing primitives in its local owner; a label, icon, data value or responsive arrangement does not justify another variant. Keep the public API small enough to understand at the call site. Edit the source or compiler for generated foundations and recipes.
1515
5. **Verify where people use it.** Run the design audit and applicable checks from the README. Render the changed product route with empty, full, stress, and fixture-off data as relevant. Check both themes, widths between artboards, long content, text scaling, keyboard, and pointer states. Compare shared controls in their actual consumers. Check the actual scroll edge and the open popup when space is tight. For shell, panel, or desktop work, exercise the real Electron renderer. For substantial implementations and consolidations, use an independent agent during design or implementation and for a final review of the changed owners and routes. Provide the working localhost route for visual review.
1616
6. **Keep decisions durable.** Follow the contract’s maintenance guidance when an approved correction changes a shared rule. Record feature-specific decisions with their feature. For sustained work, keep a task checkpoint with the code revision, verified states, evidence, and outstanding work. Include the branch or PR, checkpoint path, and localhost route in a handoff. On resumption, compare the checkpoint with the current branch, working tree, and relevant live sources before acting.

‎apps/sim/app/workspace/[workspaceId]/components/integration-tabs-header/integration-tabs-header.tsx‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import type { ReactNode } from 'react'
44
import { ChipLink, cn } from '@sim/emcn'
55
import { HEADER_ACTION_CLUSTER, PAGE_HEADER_BAR } from '@/components/page-header-bar'
6+
import { workspaceIntegrationsPath } from '@/lib/navigation/paths'
7+
import { useWorkspaceHostContext } from '@/app/workspace/[workspaceId]/providers/workspace-host-provider'
68

79
interface IntegrationTabsHeaderProps {
810
active: 'integrations' | 'skills'
@@ -17,9 +19,13 @@ export function IntegrationTabsHeader({
1719
workspaceId,
1820
rightSlot,
1921
}: IntegrationTabsHeaderProps) {
22+
const host = useWorkspaceHostContext()
2023
return (
2124
<div className={cn(PAGE_HEADER_BAR, 'gap-1')}>
22-
<ChipLink href={`/workspace/${workspaceId}/integrations`} active={active === 'integrations'}>
25+
<ChipLink
26+
href={workspaceIntegrationsPath(workspaceId, host)}
27+
active={active === 'integrations'}
28+
>
2329
Integrations
2430
</ChipLink>
2531
<ChipLink href={`/workspace/${workspaceId}/skills`} active={active === 'skills'}>

‎apps/sim/app/workspace/[workspaceId]/integrations/integrations.tsx‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212
Search,
1313
} from '@sim/emcn'
1414
import { Lock } from '@sim/emcn/icons'
15-
import { useParams } from 'next/navigation'
15+
import { redirect, useParams } from 'next/navigation'
1616
import { useQueryStates } from 'nuqs'
1717
import {
1818
blockTypeToIconMap,
@@ -22,6 +22,7 @@ import {
2222
resolveCredentialDisplay,
2323
} from '@/lib/integrations'
2424
import { isIntegrationPermitted } from '@/lib/integrations/access'
25+
import { workspaceIntegrationsPath } from '@/lib/navigation/paths'
2526
import { IntegrationTabsHeader } from '@/app/workspace/[workspaceId]/components/integration-tabs-header'
2627
import { IntegrationSection } from '@/app/workspace/[workspaceId]/integrations/components/integration-section'
2728
import { IntegrationTile } from '@/app/workspace/[workspaceId]/integrations/components/integrations-showcase'
@@ -34,6 +35,7 @@ import {
3435
integrationsParsers,
3536
integrationsUrlKeys,
3637
} from '@/app/workspace/[workspaceId]/integrations/search-params'
38+
import { useWorkspaceHostContext } from '@/app/workspace/[workspaceId]/providers/workspace-host-provider'
3739
import {
3840
SettingsEmptyState,
3941
SettingsQueryErrorState,
@@ -159,6 +161,10 @@ function ConnectedItem({ href, blockType, name, description, icon: Icon }: Conne
159161
}
160162

161163
export function Integrations() {
164+
const host = useWorkspaceHostContext()
165+
const workspaceId = host.workspace.id
166+
const destination = workspaceIntegrationsPath(workspaceId, host)
167+
if (destination !== `/workspace/${workspaceId}/integrations`) redirect(destination)
162168
return (
163169
<PermissionAccessBoundary configKey='hideIntegrationsTab'>
164170
<IntegrationsContent />

‎apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/search-modal/search-modal.tsx‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,13 @@ import { useDeploymentShape } from '@/lib/core/config/deployment-shape'
4545
import { MothershipHandoffStorage } from '@/lib/core/utils/browser-storage'
4646
import { getFolderPathNames } from '@/lib/folders/tree'
4747
import { sendMothershipMessage } from '@/lib/mothership/events'
48+
import { workspaceIntegrationsPath } from '@/lib/navigation/paths'
4849
import { captureEvent } from '@/lib/posthog/client'
4950
import { toSearchToken } from '@/lib/search/tokens'
5051
import { hasTriggerCapability } from '@/lib/workflows/triggers/utils'
5152
import { parseWorkspaceFileFolderDisplayPath } from '@/lib/workspace-files/folder-display-path'
5253
import { useInvokeGlobalCommand } from '@/app/workspace/[workspaceId]/providers/global-commands-provider'
54+
import { useWorkspaceHostContext } from '@/app/workspace/[workspaceId]/providers/workspace-host-provider'
5355
import {
5456
CommandFadedList,
5557
CommandSearch,
@@ -157,6 +159,8 @@ function SearchModalContent({
157159
const router = useRouter()
158160
const { chatEnabled } = useDeploymentShape()
159161
const workspaceId = params.workspaceId as string
162+
const host = useWorkspaceHostContext()
163+
const integrationsHref = workspaceIntegrationsPath(workspaceId, host)
160164
const currentWorkflowId = params.workflowId as string | undefined
161165
const inputRef = useRef<HTMLInputElement>(null)
162166
const listRef = useRef<HTMLDivElement>(null)
@@ -249,7 +253,7 @@ function SearchModalContent({
249253
id: 'integrations',
250254
name: 'Integrations',
251255
icon: Integration,
252-
href: `/workspace/${workspaceId}/integrations`,
256+
href: integrationsHref,
253257
hidden:
254258
permissionConfig.hideIntegrationsTab &&
255259
!requestableFeatureKeys.has('hideIntegrationsTab'),
@@ -318,6 +322,7 @@ function SearchModalContent({
318322
permissionConfig.hideTablesTab,
319323
permissionConfig.hideFilesTab,
320324
permissionConfig.hideIntegrationsTab,
325+
integrationsHref,
321326
requestableFeatureKeys,
322327
]
323328
)

‎apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar.tsx‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ import { buildFolderTree, getFolderPathNames } from '@/lib/folders/tree'
5454
import { DOCS_URL, SLACK_COMMUNITY_URL } from '@/lib/help-links'
5555
import { isIntegrationUsable } from '@/lib/integrations/access'
5656
import { SIM_RESOURCES_DRAG_TYPE } from '@/lib/mothership/resource-types'
57+
import { workspaceIntegrationsPath } from '@/lib/navigation/paths'
5758
import { captureEvent } from '@/lib/posthog/client'
5859
import { LOGO_ACCEPT_ATTRIBUTE } from '@/lib/uploads/client/logo-file'
5960
import { useSidebarChrome } from '@/app/workspace/[workspaceId]/components/workspace-chrome'
@@ -415,6 +416,7 @@ export const Sidebar = memo(function Sidebar({ organizationHref, desktopExecutor
415416
const { data: sessionData, isPending: sessionLoading } = useSession()
416417
const { data: profile } = useUserProfile()
417418
const hostContext = useWorkspaceHostContext()
419+
const integrationsHref = workspaceIntegrationsPath(workspaceId, hostContext)
418420
const { workspace: routeWorkspace } = hostContext
419421
const { hosted, chatEnabled } = useDeploymentShape()
420422
const { canAdmin, canEdit, isLoading: permissionsLoading } = useUserPermissionsContext()
@@ -798,7 +800,7 @@ export const Sidebar = memo(function Sidebar({ organizationHref, desktopExecutor
798800
id: 'integrations',
799801
label: 'Integrations',
800802
icon: Integration,
801-
href: `/workspace/${workspaceId}/integrations`,
803+
href: integrationsHref,
802804
additionalActivePaths: [`/workspace/${workspaceId}/skills`],
803805
hidden:
804806
permissionConfig.hideIntegrationsTab &&
@@ -812,6 +814,7 @@ export const Sidebar = memo(function Sidebar({ organizationHref, desktopExecutor
812814
canEdit,
813815
permissionsLoading,
814816
permissionConfig.hideIntegrationsTab,
817+
integrationsHref,
815818
permissionConfig.hideCopilot,
816819
permissionConfig.hideFilesTab,
817820
requestableFeatureKeys,

‎apps/sim/lib/navigation/paths.ts‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,21 @@ export function organizationRoutes(organizationId: string) {
5959
} as const
6060
}
6161

62+
interface WorkspaceIntegrationContext {
63+
hostOrganizationId: string | null
64+
features?: { organizationSearch?: boolean }
65+
}
66+
67+
/** Organization members use the center; standalone workspaces retain their credential routes. */
68+
export function workspaceIntegrationsPath(
69+
workspaceId: string,
70+
context: WorkspaceIntegrationContext | null
71+
) {
72+
return context?.features?.organizationSearch && context.hostOrganizationId
73+
? organizationRoutes(context.hostOrganizationId).integrations
74+
: `/workspace/${workspaceId}/integrations`
75+
}
76+
6277
/** Where `sim/<name>/<values…>` resolves in this app: the address a member types, kept verbatim. */
6378
export function simLinkPath(...segments: string[]): string {
6479
return ['/sim', ...segments.map(encodeURIComponent)].join('/')

0 commit comments

Comments
 (0)