d25d482dc2
Publish CLI Package / publish-npm (push) Waiting to run
Publish Python SDK / publish-pypi (push) Waiting to run
Publish TypeScript SDK / publish-npm (push) Waiting to run
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
133 lines
4.8 KiB
TypeScript
133 lines
4.8 KiB
TypeScript
'use client'
|
|
|
|
import { useMemo, useState } from 'react'
|
|
import { ChipTag } from '@sim/emcn'
|
|
import { ArrowRight, Plus } from 'lucide-react'
|
|
import { useParams } from 'next/navigation'
|
|
import { SettingsEmptyState } from '@/app/workspace/[workspaceId]/settings/components/settings-empty-state'
|
|
import { SettingsPanel } from '@/app/workspace/[workspaceId]/settings/components/settings-panel'
|
|
import { SettingsResourceRow } from '@/app/workspace/[workspaceId]/settings/components/settings-resource-row/settings-resource-row'
|
|
import { SettingsSection } from '@/app/workspace/[workspaceId]/settings/components/settings-section/settings-section'
|
|
import { getCustomBlockIcon } from '@/blocks/custom/custom-block-icon'
|
|
import { CustomBlockDetail } from '@/ee/custom-blocks/components/custom-block-detail'
|
|
import { useWhitelabelSettings } from '@/ee/whitelabeling/hooks/whitelabel'
|
|
import { useCanPublishCustomBlock, useCustomBlocks } from '@/hooks/queries/custom-blocks'
|
|
import { useWorkspacesQuery } from '@/hooks/queries/workspace'
|
|
|
|
export function CustomBlocks() {
|
|
const params = useParams()
|
|
const workspaceId = typeof params?.workspaceId === 'string' ? params.workspaceId : undefined
|
|
|
|
const { data: canManage = false, isLoading } = useCanPublishCustomBlock(workspaceId)
|
|
const { data: blocks = [] } = useCustomBlocks(workspaceId)
|
|
const { data: workspaces = [] } = useWorkspacesQuery()
|
|
|
|
// Any org member can view the org's blocks, but publishing requires admin on a
|
|
// source workspace — the publish route enforces admin on the picked workspace.
|
|
const currentOrgId = useMemo(
|
|
() => workspaces.find((w) => w.id === workspaceId)?.organizationId ?? null,
|
|
[workspaces, workspaceId]
|
|
)
|
|
const canCreate = useMemo(
|
|
() =>
|
|
!!currentOrgId &&
|
|
workspaces.some((w) => w.organizationId === currentOrgId && w.permissions === 'admin'),
|
|
[workspaces, currentOrgId]
|
|
)
|
|
const { data: whitelabel } = useWhitelabelSettings(blocks[0]?.organizationId)
|
|
const fallbackIconUrl = whitelabel?.logoUrl ?? null
|
|
|
|
const [searchTerm, setSearchTerm] = useState('')
|
|
const [selected, setSelected] = useState<string | 'new' | null>(null)
|
|
|
|
const filtered = useMemo(() => {
|
|
if (!searchTerm.trim()) return blocks
|
|
const q = searchTerm.toLowerCase()
|
|
return blocks.filter((b) => b.name.toLowerCase().includes(q))
|
|
}, [blocks, searchTerm])
|
|
|
|
if (isLoading) return null
|
|
|
|
if (!canManage) {
|
|
return (
|
|
<SettingsEmptyState>
|
|
Custom blocks require an Enterprise plan. Contact your admin to enable them.
|
|
</SettingsEmptyState>
|
|
)
|
|
}
|
|
|
|
if (selected !== null && workspaceId) {
|
|
return (
|
|
<CustomBlockDetail
|
|
key={selected}
|
|
blockId={selected === 'new' ? null : selected}
|
|
workspaceId={workspaceId}
|
|
onBack={() => setSelected(null)}
|
|
/>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<SettingsPanel
|
|
search={{
|
|
value: searchTerm,
|
|
onChange: setSearchTerm,
|
|
placeholder: 'Search custom blocks...',
|
|
}}
|
|
actions={
|
|
canCreate
|
|
? [
|
|
{
|
|
text: 'Create block',
|
|
icon: Plus,
|
|
variant: 'primary',
|
|
onSelect: () => setSelected('new'),
|
|
},
|
|
]
|
|
: []
|
|
}
|
|
>
|
|
<SettingsSection label={`Blocks (${blocks.length})`}>
|
|
{blocks.length === 0 ? (
|
|
<SettingsEmptyState variant='inline'>
|
|
{canCreate
|
|
? 'No custom blocks yet. Click "Create block" to publish a workflow as a block.'
|
|
: 'No custom blocks yet.'}
|
|
</SettingsEmptyState>
|
|
) : filtered.length === 0 ? (
|
|
<SettingsEmptyState variant='inline'>
|
|
No blocks found matching "{searchTerm}"
|
|
</SettingsEmptyState>
|
|
) : (
|
|
<div className='-mx-2 flex flex-col gap-y-0.5'>
|
|
{filtered.map((cb) => {
|
|
const Icon = getCustomBlockIcon(cb.iconUrl, fallbackIconUrl)
|
|
return (
|
|
<button
|
|
key={cb.id}
|
|
type='button'
|
|
onClick={() => setSelected(cb.id)}
|
|
className='w-full rounded-lg p-2 text-left transition-colors hover-hover:bg-[var(--surface-active)]'
|
|
>
|
|
<SettingsResourceRow
|
|
icon={<Icon />}
|
|
iconFill
|
|
title={cb.name}
|
|
description={cb.description || undefined}
|
|
trailing={
|
|
<div className='flex flex-shrink-0 items-center gap-2'>
|
|
{!cb.enabled && <ChipTag variant='gray'>Disabled</ChipTag>}
|
|
<ArrowRight className='size-4 text-[var(--text-icon)]' />
|
|
</div>
|
|
}
|
|
/>
|
|
</button>
|
|
)
|
|
})}
|
|
</div>
|
|
)}
|
|
</SettingsSection>
|
|
</SettingsPanel>
|
|
)
|
|
}
|