Skip to content

feat(sdk): project delete blockers UI - #2041

Open
rohanchkrabrty wants to merge 6 commits into
feat/workspace-delete-blockers-dialogfrom
feat/project-delete-blocked-dialog
Open

rohanchkrabrty wants to merge 6 commits into
feat/workspace-delete-blockers-dialogfrom
feat/project-delete-blocked-dialog

Conversation

@rohanchkrabrty

@rohanchkrabrty rohanchkrabrty commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • The project row menu in ProjectsView and the actions menu in ProjectDetailsView run CheckProjectDelete on click. The menu item shows a spinner while the check runs, then the menu closes and the confirmation or the blocked dialog opens.
  • A refused delete closes the confirmation and opens the blocked dialog.
  • Both menus use useDeleteCheck and the deleteBlockedDialog slot from feat(sdk): org delete blockers UI #2025, so a host app words the organization and project dialogs in one place. The slot gets entity: 'project' and the resource blockers.
  • The delete dialogs and the project menus are created per mount, so a check that returns after the user leaves, or browser Back, does not reopen them ([dialog] dialog with createHandle survives navigation and preserve pre-navigation store state mui/base-ui#4951).
  • Delete errors no longer show the server message in a toast.

Stacked on #2025.

Test plan

  • tsc --noEmit shows no new errors, eslint, pnpm build
  • Delete a project with resources that the deletion rules block: the blocked dialog opens from both menus
  • Delete a clean project: the confirmation opens and the delete goes through

@vercel

vercel Bot commented Nov 3, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated
frontier Ready Ready Preview Nov 4, 2026 6:33am UTC

@coderabbitai

coderabbitai Bot commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

Explain Change →

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 25 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used all 2 included reviews currently available.

Learn how review limits work.

Review configuration:

Run configuration
Commits

Reviewing files that changed from the base of the PR and between 825809c and 936eb3e.

Files selected for processing: 6
  • web/sdk/client/components/delete-blocked-dialog/delete-blocked-dialog.tsx
  • web/sdk/client/views/projects/components/delete-project-dialog.tsx
  • web/sdk/client/views/projects/components/delete-project-menu-item.tsx
  • web/sdk/client/views/projects/project-details-view.tsx
  • web/sdk/client/views/projects/projects-view.tsx
  • web/sdk/shared/types.ts
Summary

Summary by CodeRabbit

  • New Features
    • Project deletion now checks whether the project can be deleted before opening the confirmation dialog.
    • While the check is running, the delete action shows a loading indicator and is unavailable.
    • If deletion is blocked, a message explains that the project contains resources that must be addressed first.

Project deletion in both project views now runs a deletion check before confirmation. The views show a blocked-deletion dialog when resources prevent deletion and pass deletion errors to a callback.

Changes: Project deletion flow

Layer / File(s) Summary
Deletion contracts and shared UI
web/sdk/shared/types.ts, web/sdk/client/components/delete-blocked-dialog/delete-blocked-dialog.tsx, web/sdk/client/views/projects/components/delete-project-dialog.tsx, web/sdk/client/views/projects/components/delete-project-menu-item.tsx
The blocked-deletion payload accepts projects, and the dialog has project-specific copy. The delete dialog forwards errors to a callback. The project delete menu item displays a spinner and is disabled while checking.
Projects list deletion flow
web/sdk/client/views/projects/projects-view.tsx
The view creates mount-scoped handles and checks for deletion blockers before opening the confirmation or blocked-deletion dialog. The menu closes after the check completes.
Project details deletion flow
web/sdk/client/views/projects/project-details-view.tsx
The view creates mount-scoped handles and checks for deletion blockers before opening the confirmation or blocked-deletion dialog. The menu closes after the check completes.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Feature

Possibly related PRs

Suggested reviewers: rohilsurana

Merge Risk: Moderate · previous review 82580

Pending deletion checks can show confirmations for a previous project after navigation, and completing a check can close another project's menu. Resolve these stale-operation behaviors before merging.

Pre-merge checks: Passed 2
Check name Status Explanation
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

@coveralls

coveralls commented Nov 3, 2026 •

Copy link
Copy Markdown

Coverage Report for CI Build 41786173085

Coverage remained the same at 56.053%

Details

  • Coverage remained the same as the base build.
  • Patch coverage: No coverable lines changed in this PR.
  • No coverage regressions found.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 42189
Covered Lines: 23648
Line Coverage: 56.05%
Coverage Strength: 19.7 hits per line

💛 - Coveralls

@rohanchkrabrty
rohanchkrabrty force-pushed the feat/project-delete-blocked-dialog branch from a36e1ba to 34d5be9 Compare November 3, 2026 23:12
@rohanchkrabrty rohanchkrabrty changed the title feat(sdk): check project delete blockers before the confirmation feat(sdk): project delete blockers UI Nov 4, 2026
@rohanchkrabrty
rohanchkrabrty force-pushed the feat/project-delete-blocked-dialog branch from 0d56ae4 to 825809c Compare November 4, 2026 05:57

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Additional comments: 1
web/sdk/client/views/projects/projects-view.tsx-340-344: 1

340-344: Minor · Potential issue · Quick win

Unverified finding
We couldn't verify this from the code. It is shown for review, not as a verified issue.

startDelete opens the confirmation or blocked dialog before the caller closes the still-open menu. If the menu's close/focus handling dismisses a newly opened dialog, the new flow loses its result.

The confirmation or blocker dialog may close immediately or fail to receive focus, preventing the intended deletion flow. The details view uses the same ordering.

This can occur when: A deletion check resolves while the project menu is open and opens a dialog, followed immediately by projectMenuHandle.close().

Review info
Run configuration
Commits

Reviewing files that changed from the base of the PR and between 6277d9b and 825809c.

Files selected for processing: 6
  • web/sdk/client/components/delete-blocked-dialog/delete-blocked-dialog.tsx
  • web/sdk/client/views/projects/components/delete-project-dialog.tsx
  • web/sdk/client/views/projects/components/delete-project-menu-item.tsx
  • web/sdk/client/views/projects/project-details-view.tsx
  • web/sdk/client/views/projects/projects-view.tsx
  • web/sdk/shared/types.ts

Included review availability: This review used your included allowance. Your plan provides up to 2 included reviews per hour; 0 remain after this review.

Comment on lines +349 to +353
isCheckingDelete={isChecking}
onDelete={async () => {
await startDelete({ projectId, projectName: projectTitle });
projectActionsMenuHandle.close();
}}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Major · Functional Correctness · Quick win

Supported by static analysis

Script executed:

sed -n '1,110p' web/sdk/client/hooks/useDeleteCheck.ts; rg -n -C 3 'ProjectDetailsView' web/sdk/client web/apps | head -110

Repository: raystack/frontier

Length of output: 6373


Script executed:

cd web/sdk/client && rg -n -F -e 'projectId' -e 'key=' -e 'startDelete' -e 'openWithPayload' -e 'useEffect' views/projects/project-details-view.tsx views/projects/components/delete-project-dialog.tsx views/projects/projects-view.tsx || test "$?" -eq 1
echo '---dialog---'
cat views/projects/components/delete-project-dialog.tsx
echo '---details---'
sed -n '1,140p' views/projects/project-details-view.tsx
echo '---routes---'
cd /  2>/dev/null; cd - >/dev/null
cd "$(git rev-parse --show-toplevel)" && rg -n -F -e 'ProjectDetails' -e 'ProjectDetailsView' -e 'projectId' web/apps --glob '*.tsx' --glob '*.ts' | head -60 || test "$?" -eq 1

Repository: raystack/frontier

Length of output: 12676


Script executed:

cd "$(git rev-parse --show-toplevel)/web/sdk/client" && rg -n -F -e 'DeleteProjectDialog' -e 'DeleteBlockedDialogHost' -e 'deleteProjectDialogHandle' -e 'deleteBlockedDialogHandle' -e 'projectActionsMenuHandle' views/projects/project-details-view.tsx || test "$?" -eq 1
echo '---170-230---'
sed -n '170,230p' views/projects/project-details-view.tsx
echo '---295-365---'
sed -n '295,365p' views/projects/project-details-view.tsx
echo '---480-520---'
sed -n '476,520p' views/projects/project-details-view.tsx

Repository: raystack/frontier

Length of output: 5440


Key the details view by projectId so stale delete checks cannot reach a new project.

ProjectDetailsView keeps its state when projectId changes. If a check for project A resolves after navigation to project B, the confirm dialog opens with A's payload. Confirming from B's page deletes A. Remount the view when projectId changes, for example by keying it by projectId inside the SDK export.

Comment on lines +99 to +106
const { startDelete, isChecking, handleDeleteError } = useDeleteCheck({
entity: 'project',
check: ({ projectId }) =>
checkProjectDelete(
create(CheckProjectDeleteRequestSchema, { id: projectId })
),
confirmDialogHandle: deleteProjectDialogHandle,
blockedDialogHandle: deleteBlockedDialogHandle

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Minor · Functional Correctness · Poor tradeoff

Supported by static analysis

Script executed:

sed -n '1,110p' web/sdk/client/hooks/useDeleteCheck.ts; rg -n 'activeOrganization|<ProjectsView|ProjectsView\(' web/sdk/client web/apps | head -110

Repository: raystack/frontier

Length of output: 12444


Script executed:

sed -n '55,140p' web/sdk/client/views/projects/projects-view.tsx; echo '-----DIALOG-----'; sed -n '1,120p' web/sdk/client/views/projects/components/delete-project-dialog.tsx; echo '-----PAYLOAD TYPES-----'; rg -n -F -e 'DeleteProjectPayload' -e 'ProjectMenuPayload' -e 'DeleteBlockedPayload' -- web/sdk | head -40; echo '-----USAGES OF ProjectsView-----'; rg -n -F -e '<ProjectsView' -e 'ProjectsView' -- web/apps web/sdk | head -40

Repository: raystack/frontier

Length of output: 9697


Script executed:

sed -n '1,60p' web/apps/client-demo/src/pages/settings/Projects.tsx; echo '-----SETTINGS-----'; sed -n '1,60p' web/apps/client-demo/src/pages/Settings.tsx; echo '-----ROUTES-----'; rg -n -F -e 'Settings' -e 'orgId' -- web/apps/client-demo/src | head -40; echo '-----CONTEXT-----'; sed -n '180,240p' web/sdk/client/contexts/FrontierContext.tsx

Repository: raystack/frontier

Length of output: 9008


Script executed:

rg -n -F -e '<DeleteProjectDialog' -e 'deleteProjectDialogHandle' -e 'deleteBlockedDialogHandle' -e 'DeleteBlockedDialog' -e 'startDelete' -- web/sdk/client/views/projects/projects-view.tsx || test "$?" -eq 1; echo '-----ROUTER-----'; sed -n '1,70p' web/apps/client-demo/src/Router.tsx

Repository: raystack/frontier

Length of output: 3779


Discard late delete checks after the active organization changes.

If the active organization changes while a project delete check is pending, the late result still opens the delete confirmation in the current organization's view. The confirmation names the project from the previous organization and can delete it. Compare the organization ID when the check starts with the ID when it resolves, and skip the confirmation on a mismatch.

Comment on lines +339 to +345
onClick={async () => {
await startDelete({
projectId: payload.projectId,
projectName: payload.title
})
}
});
projectMenuHandle.close();
}}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Minor · Functional Correctness · Quick win

Supported by static analysis

Script executed:

sed -n '1,145p' web/sdk/client/views/projects/components/project-columns.tsx; sed -n '295,365p' web/sdk/client/views/projects/projects-view.tsx

Repository: raystack/frontier

Length of output: 5110


Script executed:

set -e
echo "=== projects-view.tsx outline ==="
ast-grep outline web/sdk/client/views/projects/projects-view.tsx --view expanded || true
echo "=== projects-view.tsx lines 1-120 ==="
nl -ba web/sdk/client/views/projects/projects-view.tsx | sed -n '1,120p'
echo "=== projects-view.tsx lines 120-300 ==="
nl -ba web/sdk/client/views/projects/projects-view.tsx | sed -n '120,300p'
echo "=== delete-project-menu-item.tsx ==="
nl -ba web/sdk/client/views/projects/components/delete-project-menu-item.tsx
echo "=== useDeleteCheck references ==="
rg -n -F --glob '*.ts*' -e 'useDeleteCheck' -- web/sdk || test "$?" -eq 1

Repository: raystack/frontier

Length of output: 14526


Script executed:

set -e
nl -ba web/sdk/client/hooks/useDeleteCheck.ts

Repository: raystack/frontier

Length of output: 3046


Close the project menu only when it still shows the project whose delete check finished.

The delete action calls projectMenuHandle.close() after startDelete resolves. The handle is shared by all rows, so this closes whichever project menu is open at that moment. If a user closes the menu for project A during its check and opens project B's menu, A's check completion closes B's menu. The same shared isChecking state also disables B's delete item while A is being checked.

Both project menus now run CheckProjectDelete on click. The menu item
shows a spinner while the check runs, then the menu closes and the
confirmation or the blocked dialog opens. A refused delete closes the
confirmation and shows the blocked dialog.

The project flow uses the same useDeleteFlow hook and deleteBlockedDialog
slot as the organization flow, so a host app words both in one place.
The project delete now uses useDeleteCheck, the same as the
organization delete. Both views import DeleteBlockedPayload from
shared/types.
The same fix as the organization delete: a handle created at module
scope keeps its open state after the view unmounts, so a late delete
check or a dialog left open on browser Back showed the dialog again on
the next visit (mui/base-ui#4951).

The project menus are included. They stay open while the delete check
runs, and a menu handle's close() does nothing once the menu has
unmounted, so leaving during the check brought the menu back open.
Both project views call useDeleteCheck directly, the same as the
organization view. Each menu still stays open with the spinner while
the check runs and closes once the dialog opens.

This branch was successfully deployed

1 active deployment
Preview — 936eb3e6 Deployed Nov 4, 2026 by vercel[bot]
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.

2 participants