Skip to content

feat(custom-blocks): org-member visibility, workspace-admin-gated management#5507

Merged
TheodoreSpeaks merged 5 commits into
stagingfrom
feat/custom-block-nav-workspace-admin
Jul 8, 2026
Merged

feat(custom-blocks): org-member visibility, workspace-admin-gated management#5507
TheodoreSpeaks merged 5 commits into
stagingfrom
feat/custom-block-nav-workspace-admin

Conversation

@TheodoreSpeaks

@TheodoreSpeaks TheodoreSpeaks commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

Summary

Custom blocks are org-wide shared resources, so visibility and management are decoupled — and management gates match the server's source-workspace-admin authz.

Visibility (any org member):

  • The Custom blocks settings page previously sat behind the Enterprise nav section's org-admin/owner gate, hiding it even from workspace admins. New allowNonOrgAdmin nav flag exempts an item from the org-admin requirement requiresTeam/requiresEnterprise otherwise impose (plan + hosted entitlement still applies). Set only on custom-blocks; other Enterprise items stay org-admin-only.

Create (workspace admin):

  • The "Create block" action shows only when the user is admin of a workspace in the current org, matching the publish route's canAdmin check.
  • The source-workspace picker lists only workspaces the user administers; the default selection snaps to an eligible one.

Manage an existing block (source-workspace admin):

  • Added the source workspaceId to the block wire type and gated the detail view on it. A viewer who isn't an admin of the block's source workspace gets a read-only view — no Save/Discard, no Delete, all fields disabled. Mirrors the server authz (edit/delete require hasWorkspaceAdminAccess), so the UI no longer dangles buttons that 403.

Polish:

  • SettingsResourceRow gains an opt-in iconFill so uploaded image icons fill the tile edge-to-edge instead of clamping to 20px (glyph svgs still normalize). Custom blocks list opts in.

Files

  • settings/navigation.ts, settings-sidebar.tsxallowNonOrgAdmin flag + filter.
  • ee/custom-blocks/components/custom-blocks.tsxcanCreate gate + iconFill.
  • ee/custom-blocks/components/custom-block-detail.tsx — picker admin-filter + read-only canManageBlock gate.
  • lib/workflows/custom-blocks/operations.ts, lib/api/contracts/custom-blocks.ts, app/api/custom-blocks/route.ts — source workspaceId on the wire.
  • settings/components/settings-resource-row/settings-resource-row.tsxiconFill prop.

Type of Change

  • Bug fix / behavior change

Testing

  • biome check + tsc --noEmit clean on all changed files; custom-block test suites pass.
  • Verified locally in a hosted-mode config with an enterprise org: an org member who is workspace-admin of a source workspace sees the page + Create + editable detail; a read-only member sees the page but no Create and a fully read-only detail; the org owner sees everything.

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

🤖 Generated with Claude Code

…page

The Custom blocks page lives in the Enterprise nav section, which the
sidebar filter hides unless the user is an org admin/owner. But the
server authz for managing custom blocks is source-workspace admin
(hasWorkspaceAdminAccess), not org admin — so a workspace admin who
could manage a block via the API couldn't even reach the page.

Add an `allowNonOrgAdmin` nav flag that exempts an item from the org
admin/owner requirement (the plan/hosted entitlement still applies; the
page enforces its own per-resource authz), and set it on custom-blocks.
Other Enterprise items (access control, audit logs, SSO, data
retention/drains, whitelabeling) stay org-admin-only.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017ddpeoz81CSqYQ73Dzf7yo
@vercel

vercel Bot commented Jul 8, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped Jul 8, 2026 7:39pm

Request Review

@cursor

cursor Bot commented Jul 8, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Changes authorization UX and nav visibility for an Enterprise feature; server rules are aligned rather than relaxed, but mistaken client gating could hide controls or expose read-only paths incorrectly.

Overview
Custom blocks are treated as org-wide resources: any org member on an eligible plan can reach the settings page and browse blocks, while create/edit/delete stay tied to admin on the block’s source workspace (matching existing API authz).

The Enterprise settings sidebar no longer requires org admin/owner for Custom blocks via a new allowNonOrgAdmin nav flag; plan and hosted entitlements are unchanged. List and detail UIs gate Create block and save/delete on workspace-admin membership in the org, restrict the create flow’s workspace picker to admin workspaces, and render a read-only detail when the viewer isn’t admin of the block’s source workspace.

The list API and client contract now include each block’s source workspaceId so the UI can enforce that gate. SettingsResourceRow adds optional iconFill so uploaded block icons fill the tile; the custom blocks list opts in.

Reviewed by Cursor Bugbot for commit 414375f. Bugbot is set up for automated code reviews on this repo. Configure here.

@greptile-apps

greptile-apps Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR decouples visibility from management for org-wide custom blocks: any enterprise org member can now browse the Custom blocks settings page (via a new allowNonOrgAdmin nav flag), while creating a block requires workspace-admin status and managing an existing block requires admin on its source workspace. The workspaceId of the source workflow is now included in the block wire type to drive the client-side management gate.

  • Adds allowNonOrgAdmin to the NavigationItem interface and applies it only to custom-blocks, so the sidebar shows the page to all enterprise members while other Enterprise nav items stay org-admin-only.
  • Client-side canManageBlock in custom-block-detail.tsx checks whether the user is admin of the block's source workspace (matching the server's hasWorkspaceAdminAccess authz), switching to a fully read-only view (all fields disabled, no Save/Discard/Delete) for non-admins.
  • SettingsResourceRow gains an opt-in iconFill prop so uploaded image icons fill the tile edge-to-edge.

Confidence Score: 5/5

Safe to merge. All write paths are fail-closed and the server-side authz is unchanged; the UI changes only mirror existing server checks.

The three-tier authz model (visibility → create → manage) is correctly implemented end to end. The allowNonOrgAdmin flag preserves the enterprise plan gate while relaxing only the org-admin requirement for the nav item. The canCreate and canManageBlock checks are both conservative (default to false while queries load, and default to false when workspaceId is null), so all transient states are safe. Server-side routes remain the authoritative enforcement layer and are untouched.

No files require special attention. The most complex change is in custom-block-detail.tsx, where the render-phase state snap and the canManageBlock derivation are both correct and follow existing patterns in the file.

Important Files Changed

Filename Overview
apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-sidebar/settings-sidebar.tsx Adds allowNonOrgAdmin flag logic: `orgAdminSatisfied = isOrgAdminOrOwner
apps/sim/app/workspace/[workspaceId]/settings/navigation.ts Adds allowNonOrgAdmin?: boolean to NavigationItem with clear JSDoc and applies it only to the custom-blocks entry. No other items affected.
apps/sim/ee/custom-blocks/components/custom-blocks.tsx Adds useWorkspacesQuery to derive canCreate (admin of any org workspace) and conditionally renders the "Create block" action and empty-state message. Correct dependency memos; safe fail-closed when workspaces haven't loaded.
apps/sim/ee/custom-blocks/components/custom-block-detail.tsx Central change: useWorkspacesQuery now always runs (not gated on isCreate), and canManageBlock gates all write affordances. Render-phase setSelectedWorkspaceId for workspace snapping follows existing pattern in the file. The eligibleDefaultWorkspaceId memo correctly prevents infinite loops. Read-only state is comprehensive (inputs, textarea, combobox, icon buttons, save/discard/delete).
apps/sim/lib/workflows/custom-blocks/operations.ts Adds workspaceId: workflow.workspaceId to both the SELECT projection and the mapped return in listCustomBlocksWithInputs, and sets it in publishCustomBlock. The field is typed `string
apps/sim/lib/api/contracts/custom-blocks.ts Adds workspaceId: z.string().nullable() to customBlockSchema with an explanatory JSDoc. Schema addition is additive and non-breaking.
apps/sim/app/api/custom-blocks/route.ts One-line addition of workspaceId to toWire, piping the new field from CustomBlockWithInputs to the API response.
apps/sim/app/workspace/[workspaceId]/settings/components/settings-resource-row/settings-resource-row.tsx Adds iconFill?: boolean prop using cn to toggle between [&_img]:size-full and [&_img]:size-5. SVGs still normalize to 20px via the base class. Clean, minimal change.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[User navigates to Settings] --> B{Enterprise plan?}
    B -- No --> C[Nav item hidden]
    B -- Yes --> D[Custom Blocks nav item visible\nvia allowNonOrgAdmin]

    D --> E[Custom Blocks page loads]
    E --> F{canManage:\nfeature flag + enterprise?}
    F -- No --> G[Show Enterprise plan required message]
    F -- Yes --> H[Show block list]

    H --> I{canCreate:\nadmin of any org workspace?}
    I -- Yes --> J[Show Create block button]
    I -- No --> K[Hide Create block button\nread-only list view]

    H --> L[User clicks a block]
    L --> M{canManageBlock:\nadmin of block's SOURCE workspace?}
    M -- Yes --> N[Full edit view\nSave / Discard / Delete]
    M -- No --> O[Read-only view\nall fields disabled\nno action buttons]

    J --> P[Create block flow\nWorkspace picker shows\nonly admin workspaces]
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A[User navigates to Settings] --> B{Enterprise plan?}
    B -- No --> C[Nav item hidden]
    B -- Yes --> D[Custom Blocks nav item visible\nvia allowNonOrgAdmin]

    D --> E[Custom Blocks page loads]
    E --> F{canManage:\nfeature flag + enterprise?}
    F -- No --> G[Show Enterprise plan required message]
    F -- Yes --> H[Show block list]

    H --> I{canCreate:\nadmin of any org workspace?}
    I -- Yes --> J[Show Create block button]
    I -- No --> K[Hide Create block button\nread-only list view]

    H --> L[User clicks a block]
    L --> M{canManageBlock:\nadmin of block's SOURCE workspace?}
    M -- Yes --> N[Full edit view\nSave / Discard / Delete]
    M -- No --> O[Read-only view\nall fields disabled\nno action buttons]

    J --> P[Create block flow\nWorkspace picker shows\nonly admin workspaces]
Loading

Reviews (3): Last reviewed commit: "fix(custom-blocks): don't flag create as..." | Re-trigger Greptile

…admin

Split visibility from management to match the org-wide nature of custom
blocks:
- View: any org member (the nav item is now plan-gated, not org-admin).
- Create: the "Create block" action shows only when the user is admin of
  a workspace in the current org, matching the publish route's
  source-workspace admin authz.
- Source picker: lists only workspaces the user administers (in the
  current org); the default selection snaps to an eligible workspace when
  the current one isn't one they can publish from.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017ddpeoz81CSqYQ73Dzf7yo
@TheodoreSpeaks TheodoreSpeaks changed the title fix(settings): let workspace admins reach the Custom blocks settings page feat(custom-blocks): org-member visibility + workspace-admin-gated create Jul 8, 2026

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit cd25970. Configure here.

Comment thread apps/sim/ee/custom-blocks/components/custom-block-detail.tsx
- Add the source workspaceId to the block wire type and gate the detail
  view on it: a viewer who isn't an admin of the block's source workspace
  gets a read-only view — no Save/Discard, no Delete, all fields disabled.
  Matches the server authz (edit/delete require source-workspace admin),
  so the UI no longer dangles buttons that 403.
- SettingsResourceRow: add an opt-in `iconFill` so uploaded image icons
  fill the tile edge-to-edge instead of clamping to 20px; glyph svgs still
  normalize to 20px. Custom blocks list opts in.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017ddpeoz81CSqYQ73Dzf7yo
@TheodoreSpeaks TheodoreSpeaks changed the title feat(custom-blocks): org-member visibility + workspace-admin-gated create feat(custom-blocks): org-member visibility, workspace-admin-gated management Jul 8, 2026
The publish path builds a CustomBlockWithInputs literal; it was missing
the workspaceId field added to the interface, breaking the type check.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017ddpeoz81CSqYQ73Dzf7yo
@TheodoreSpeaks

Copy link
Copy Markdown
Collaborator Author

@greptile review

The create dirty-check compared selectedWorkspaceId against the URL
workspace, but the source picker now auto-snaps to the first workspace
the user can publish from — which may differ. Compare against that
eligible default so opening/discarding the create flow doesn't show a
false unsaved-changes state. (Bugbot)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017ddpeoz81CSqYQ73Dzf7yo
@TheodoreSpeaks

Copy link
Copy Markdown
Collaborator Author

@greptile review

@TheodoreSpeaks TheodoreSpeaks merged commit 4cd1461 into staging Jul 8, 2026
18 checks passed
@TheodoreSpeaks TheodoreSpeaks deleted the feat/custom-block-nav-workspace-admin branch July 8, 2026 20:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant