Skip to content

Commit 237fc05

Browse files
feat(custom-block): move management to enterprise settings; derive inputs live from deployed start
1 parent 82eff54 commit 237fc05

28 files changed

Lines changed: 18417 additions & 526 deletions

File tree

apps/sim/app/api/custom-blocks/[id]/route.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,13 @@ export const PATCH = withRouteHandler(async (request: NextRequest, context: Rout
5757
const authz = await authorizeManage(session.user.id, id)
5858
if (authz.error) return authz.error
5959

60-
const { name, description, enabled, iconUrl, exposedOutputs } = parsed.data.body
60+
const { name, description, enabled, iconUrl, inputs, exposedOutputs } = parsed.data.body
6161
try {
6262
await updateCustomBlock(id, {
6363
name,
6464
description,
6565
enabled,
66+
inputs,
6667
iconUrl,
6768
exposedOutputs,
6869
})

apps/sim/app/api/custom-blocks/route.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ function toWire(block: CustomBlockWithInputs) {
3131
id: block.id,
3232
organizationId: block.organizationId,
3333
workflowId: block.workflowId,
34+
workflowName: block.workflowName,
35+
workspaceName: block.workspaceName,
3436
type: block.type,
3537
name: block.name,
3638
description: block.description,
@@ -82,7 +84,8 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
8284
if (!parsed.success) return parsed.response
8385

8486
const userId = session.user.id
85-
const { workspaceId, workflowId, name, description, iconUrl, exposedOutputs } = parsed.data.body
87+
const { workspaceId, workflowId, name, description, iconUrl, inputs, exposedOutputs } =
88+
parsed.data.body
8689

8790
if (!(await hasWorkspaceAdminAccess(userId, workspaceId))) {
8891
return NextResponse.json({ error: 'Admin permissions required' }, { status: 403 })
@@ -117,6 +120,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
117120
name,
118121
description,
119122
iconUrl,
123+
inputs,
120124
exposedOutputs,
121125
})
122126
return NextResponse.json({ customBlock: toWire(block) })

apps/sim/app/workspace/[workspaceId]/providers/custom-blocks-loader.tsx

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { useParams } from 'next/navigation'
55
import { buildCustomBlockConfig } from '@/blocks/custom/build-config'
66
import { hydrateClientCustomBlocks } from '@/blocks/custom/client-overlay'
77
import { getCustomBlockIcon } from '@/blocks/custom/custom-block-icon'
8+
import { useWhitelabelSettings } from '@/ee/whitelabeling/hooks/whitelabel'
89
import { useCustomBlocks } from '@/hooks/queries/custom-blocks'
910

1011
/**
@@ -19,15 +20,21 @@ export function CustomBlocksLoader() {
1920
const workspaceId = params?.workspaceId as string | undefined
2021
const { data } = useCustomBlocks(workspaceId)
2122

23+
// Blocks with no uploaded icon fall back to the org's whitelabel logo, then the
24+
// default glyph. All blocks share the org, so read it off the first row.
25+
const { data: whitelabel } = useWhitelabelSettings(data?.[0]?.organizationId)
26+
const fallbackIconUrl = whitelabel?.logoUrl ?? null
27+
2228
useEffect(() => {
2329
hydrateClientCustomBlocks(
2430
// Only enabled blocks are resolvable/executable server-side, so the client
2531
// overlay (toolbar, canvas, palette) must exclude disabled ones too — else
2632
// the block is offered but every run fails.
2733
(data ?? [])
2834
.filter((block) => block.enabled)
29-
.map((block) =>
30-
buildCustomBlockConfig(
35+
.map((block) => {
36+
const effectiveIcon = block.iconUrl || fallbackIconUrl
37+
return buildCustomBlockConfig(
3138
{
3239
type: block.type,
3340
name: block.name,
@@ -37,13 +44,13 @@ export function CustomBlocksLoader() {
3744
},
3845
block.inputFields,
3946
{
40-
icon: getCustomBlockIcon(block.iconUrl),
41-
bgColor: block.iconUrl ? 'transparent' : undefined,
47+
icon: getCustomBlockIcon(block.iconUrl, fallbackIconUrl),
48+
bgColor: effectiveIcon ? 'transparent' : undefined,
4249
}
4350
)
44-
)
51+
})
4552
)
46-
}, [data])
53+
}, [data, fallbackIconUrl])
4754

4855
return null
4956
}

apps/sim/app/workspace/[workspaceId]/settings/[section]/settings.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@ const WorkflowMcpServers = dynamic(() =>
7878
const AccessControl = dynamic(() =>
7979
import('@/ee/access-control/components/access-control').then((m) => m.AccessControl)
8080
)
81+
const CustomBlocks = dynamic(() =>
82+
import('@/ee/custom-blocks/components/custom-blocks').then((m) => m.CustomBlocks)
83+
)
8184
const AuditLogs = dynamic(() =>
8285
import('@/ee/audit-logs/components/audit-logs').then((m) => m.AuditLogs)
8386
)
@@ -133,6 +136,7 @@ export function SettingsPage({ section }: SettingsPageProps) {
133136
{effectiveSection === 'secrets' && <Secrets />}
134137
{effectiveSection === 'credential-sets' && <CredentialSets />}
135138
{effectiveSection === 'access-control' && <AccessControl />}
139+
{effectiveSection === 'custom-blocks' && <CustomBlocks />}
136140
{effectiveSection === 'audit-logs' && <AuditLogs />}
137141
{effectiveSection === 'apikeys' && <ApiKeys />}
138142
{isBillingEnabled && effectiveSection === 'billing' && <Billing />}

apps/sim/app/workspace/[workspaceId]/settings/navigation.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export type SettingsSection =
2727
| 'secrets'
2828
| 'credential-sets'
2929
| 'access-control'
30+
| 'custom-blocks'
3031
| 'audit-logs'
3132
| 'apikeys'
3233
| 'byok'
@@ -81,6 +82,7 @@ export interface NavigationItem {
8182
const isSSOEnabled = isTruthy(getEnv('NEXT_PUBLIC_SSO_ENABLED'))
8283
const isCredentialSetsEnabled = isTruthy(getEnv('NEXT_PUBLIC_CREDENTIAL_SETS_ENABLED'))
8384
const isAccessControlEnabled = isTruthy(getEnv('NEXT_PUBLIC_ACCESS_CONTROL_ENABLED'))
85+
const isCustomBlocksEnabled = isTruthy(getEnv('NEXT_PUBLIC_CUSTOM_BLOCKS_ENABLED'))
8486
const isInboxEnabled = isTruthy(getEnv('NEXT_PUBLIC_INBOX_ENABLED'))
8587
const isWhitelabelingEnabled = isTruthy(getEnv('NEXT_PUBLIC_WHITELABELING_ENABLED'))
8688
const isAuditLogsEnabled = isTruthy(getEnv('NEXT_PUBLIC_AUDIT_LOGS_ENABLED'))
@@ -279,6 +281,16 @@ export const allNavigationItems: NavigationItem[] = [
279281
selfHostedOverride: isWhitelabelingEnabled,
280282
docsLink: 'https://docs.sim.ai/platform/enterprise/whitelabeling',
281283
},
284+
{
285+
id: 'custom-blocks',
286+
label: 'Custom blocks',
287+
description: 'Publish workflows as reusable blocks for your organization.',
288+
icon: HexSimple,
289+
section: 'enterprise',
290+
requiresHosted: true,
291+
requiresEnterprise: true,
292+
selfHostedOverride: isCustomBlocksEnabled,
293+
},
282294
{
283295
id: 'admin',
284296
label: 'Admin',

0 commit comments

Comments
 (0)