-
Notifications
You must be signed in to change notification settings - Fork 836
feat: Add modal for critical actions #7004
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
Claude finished @tristantr's task —— View job Code Review CompleteTasks
Review SummaryThis PR successfully adds confirmation modals for two critical workspace actions: archiving and deleting. The implementation follows good safety practices by requiring explicit user confirmation before executing destructive operations. Positive Aspects✅ Good security practice: Adding confirmation modals for critical actions prevents accidental data loss Issues Found1. Shared Loading State Bug (Medium Priority)Location: Lines 64-66, 1052-1063, 1078-1090 Issue: Both modals share the same
Recommendation: let isProcessingArchive = $state(false)
let isProcessingDelete = $state(false)Then use 2. Missing Error Handling (Medium Priority)Location: Lines 1053-1064, 1079-1090 Issue: Both
Recommendation: Add error handling: onConfirmed={async () => {
isProcessingArchive = true
try {
await WorkspaceService.archiveWorkspace({ workspace: $workspaceStore ?? '' })
sendUserToast(`Archived workspace ${$workspaceStore}`)
workspaceStore.set(undefined)
usersWorkspaceStore.set(undefined)
goto('/user/workspaces')
} catch (error) {
sendUserToast(`Failed to archive workspace: ${error.message}`, 'error')
// Don't close modal or navigate on error
} finally {
isProcessingArchive = false
archiveModalOpen = false // Only close on success or error, user can try again
}
}}3. Modal Closure Logic Issue (Low Priority)Location: Lines 1063, 1089 Issue: The modals are closed in the Current behavior: Modal closes whether operation succeeds or fails 4. Comment Accuracy (Minor)Location: Line 63 Issue: The comment says "Modal state for workspace deletion" but it actually covers both archive and delete operations. Recommendation: // Modal state for workspace archive/delete operationsPerformance Considerations✅ Good: Loading state prevents multiple simultaneous requests No performance concerns identified. Security Considerations✅ Good: Backend validation still applies (these are just UI confirmations) Svelte 5 Best Practices✅ Correctly uses Additional Observations
Recommendations SummaryMust Fix:
Should Fix:
Nice to Have:
Testing InstructionsTo verify these changes work correctly, navigate to the Windmill workspace settings page by clicking on your workspace name and selecting "Workspace Settings" from the dropdown. Then go to the "General" tab where you'll find the workspace management section at the bottom of the page. Click the "Archive workspace" button and verify that a confirmation modal appears asking you to confirm the archival, with clear messaging that this action can be reversed by a superadmin. Cancel the modal and verify it closes properly. For superadmin users, also test the "Delete workspace (superadmin)" button which should show a more severe warning modal indicating the action is permanent and irreversible with emphasized red text. Verify both modals show loading states when confirmed and properly navigate to the workspaces list upon successful completion. Also test that the buttons are appropriately disabled for the special "admins" and "starter" workspaces. |
| // Modal state for workspace deletion | ||
| let archiveModalOpen = $state(false) | ||
| let deleteModalOpen = $state(false) | ||
| let isProcessingWorkspaceDeletion = $state(false) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider using separate processing states (or a more generic name) for archiving and deletion. The current isProcessingWorkspaceDeletion is used in both modals, which may be confusing.
Deploying windmill with
|
| Latest commit: |
484350c
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://5e7cbb2d.windmill.pages.dev |
| Branch Preview URL: | https://tl-modals-for-critical-actio.windmill.pages.dev |
| // Modal state for workspace deletion | ||
| let archiveModalOpen = $state(false) | ||
| let deleteModalOpen = $state(false) | ||
| let isProcessingWorkspaceDeletion = $state(false) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Issue: Shared loading state between modals
Both the archive and delete modals share isProcessingWorkspaceDeletion. This could cause UI bugs if both operations could somehow be triggered, or if one fails partway through.
Recommendation: Use separate loading states:
let isProcessingArchive = $state(false)
let isProcessingDelete = $state(false)| import ConfirmationModal from '$lib/components/common/confirmationModal/ConfirmationModal.svelte' | ||
| import TextInput from '$lib/components/text_input/TextInput.svelte' | ||
| // Modal state for workspace deletion |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor: Inaccurate comment
The comment says "workspace deletion" but these state variables handle both archive and delete operations.
Suggestion:
// Modal state for workspace archive/delete operations| onConfirmed={async () => { | ||
| isProcessingWorkspaceDeletion = true | ||
| try { | ||
| await WorkspaceService.archiveWorkspace({ workspace: $workspaceStore ?? '' }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Critical: Missing error handling
If WorkspaceService.archiveWorkspace() throws an error, the user won't see a helpful error message, and the modal will close even though the operation failed.
Recommendation:
try {
await WorkspaceService.archiveWorkspace({ workspace: $workspaceStore ?? '' })
sendUserToast(`Archived workspace ${$workspaceStore}`)
workspaceStore.set(undefined)
usersWorkspaceStore.set(undefined)
goto('/user/workspaces')
archiveModalOpen = false
} catch (error) {
sendUserToast(`Failed to archive workspace: ${error.message}`, 'error')
// Keep modal open so user can retry
} finally {
isProcessingArchive = false
}This way, on error:
- User sees a clear error message
- Modal stays open (they can retry or cancel)
- Loading state is cleared
Only close the modal on success.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tristantr claude is correct
| onConfirmed={async () => { | ||
| isProcessingWorkspaceDeletion = true | ||
| try { | ||
| await WorkspaceService.deleteWorkspace({ workspace: $workspaceStore ?? '' }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Critical: Missing error handling
Same issue as the archive modal - if WorkspaceService.deleteWorkspace() throws an error, the user won't see a helpful error message and the modal will close anyway.
Recommendation:
try {
await WorkspaceService.deleteWorkspace({ workspace: $workspaceStore ?? '' })
sendUserToast(`Deleted workspace ${$workspaceStore}`)
workspaceStore.set(undefined)
usersWorkspaceStore.set(undefined)
goto('/user/workspaces')
deleteModalOpen = false
} catch (error) {
sendUserToast(`Failed to delete workspace: ${error.message}`, 'error')
// Keep modal open so user can retry
} finally {
isProcessingDelete = false
}There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tristantr claude is correct
| goto('/user/workspaces') | ||
| archiveModalOpen = false | ||
| } catch (error) { | ||
| sendUserToast(`Failed to archive workspace: ${error}`, true) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the catch block for archiving, the error is directly interpolated in the toast. Consider checking if error is an instance of Error and using error.message to avoid unexpected output.
| sendUserToast(`Failed to archive workspace: ${error}`, true) | |
| sendUserToast(`Failed to archive workspace: ${error instanceof Error ? error.message : error}`, true) |
| goto('/user/workspaces') | ||
| deleteModalOpen = false | ||
| } catch (error) { | ||
| sendUserToast(`Failed to delete workspace: ${error}`, true) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the catch block for deletion, consider formatting the error message (e.g., using error.message if available) instead of interpolating the entire error object directly.
| sendUserToast(`Failed to delete workspace: ${error}`, true) | |
| \t sendUserToast(`Failed to delete workspace: ${error.message ?? error}`, true) |
Add confirmation modals for 2 critical actions : Archiving and Deleting a workspace