Skip to content

🐛 Replace all window.confirm() with styled ConfirmDialog#4057

Merged
clubanderson merged 1 commit intomainfrom
replace-window-confirm
Apr 1, 2026
Merged

🐛 Replace all window.confirm() with styled ConfirmDialog#4057
clubanderson merged 1 commit intomainfrom
replace-window-confirm

Conversation

@clubanderson
Copy link
Copy Markdown
Collaborator

Summary

  • Replace all 6 instances of native window.confirm() across 5 modal components with the existing styled ConfirmDialog component
  • Add ESLint no-restricted-globals rule to error on future use of alert, confirm, or prompt

Files changed:

  • FeatureRequestModal.tsx — discard unsaved changes confirm
  • FeedbackModal.tsx — discard unsaved changes confirm
  • ReservationFormModal.tsx — discard unsaved changes confirm
  • AlertRuleEditor.tsx — discard unsaved changes confirm
  • CreateNamespaceModal.tsx — discard unsaved changes confirm
  • GrantAccessModal.tsx — discard unsaved changes confirm
  • eslint.config.js — added no-restricted-globals rule

Test plan

  • Open any modal, type something, press Escape — verify styled dialog appears (not browser native)
  • Click "Keep editing" — stays in modal
  • Click "Discard" — closes modal and clears form
  • Verify npm run lint passes
  • Verify build passes

Replace native browser confirm dialogs with the existing ConfirmDialog
component across 5 files (6 instances). Add ESLint no-restricted-globals
rule to prevent future use of alert/confirm/prompt.

Files changed:
- FeatureRequestModal.tsx
- FeedbackModal.tsx
- ReservationFormModal.tsx
- AlertRuleEditor.tsx
- CreateNamespaceModal.tsx
- GrantAccessModal.tsx
- eslint.config.js (added no-restricted-globals rule)

Signed-off-by: Andrew Anderson <andy@clubanderson.com>
Copilot AI review requested due to automatic review settings April 1, 2026 01:44
@kubestellar-prow kubestellar-prow bot added the dco-signoff: yes Indicates the PR's author has signed the DCO. label Apr 1, 2026
@kubestellar-prow
Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please assign clubanderson for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@netlify
Copy link
Copy Markdown

netlify bot commented Apr 1, 2026

Deploy Preview for kubestellarconsole ready!

Name Link
🔨 Latest commit 13a48b3
🔍 Latest deploy log https://app.netlify.com/projects/kubestellarconsole/deploys/69cc7883f178300008379166
😎 Deploy Preview https://deploy-preview-4057.console-deploy-preview.kubestellar.io
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 1, 2026

👋 Hey @clubanderson — thanks for opening this PR!

🤖 This project is developed exclusively using AI coding assistants.

Please do not attempt to code anything for this project manually.
All contributions should be authored using an AI coding tool such as:

This ensures consistency in code style, architecture patterns, test coverage,
and commit quality across the entire codebase.


This is an automated message.

@clubanderson clubanderson merged commit 0d65daa into main Apr 1, 2026
19 of 20 checks passed
@kubestellar-prow kubestellar-prow bot added the size/L Denotes a PR that changes 100-499 lines, ignoring generated files. label Apr 1, 2026
@kubestellar-prow kubestellar-prow bot deleted the replace-window-confirm branch April 1, 2026 01:44
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 1, 2026

Thank you for your contribution! Your PR has been merged.

Check out what's new:

Stay connected: Slack #kubestellar-dev | Multi-Cluster Survey

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Replaces native window.confirm() prompts in several UI flows with the shared styled ConfirmDialog, and adds an ESLint rule intended to prevent future use of native browser dialogs.

Changes:

  • Swapped discard/unsaved-changes confirmation from window.confirm() to ConfirmDialog across multiple modal components.
  • Added per-modal state (showDiscardConfirm) and forceClose handlers to manage confirm/close behavior.
  • Added an ESLint no-restricted-globals rule targeting alert, confirm, and prompt.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
web/src/components/namespaces/GrantAccessModal.tsx Replaces unsaved-changes close confirmation with ConfirmDialog.
web/src/components/namespaces/CreateNamespaceModal.tsx Replaces unsaved-changes close confirmation with ConfirmDialog.
web/src/components/gpu/ReservationFormModal.tsx Replaces unsaved-changes close confirmation with ConfirmDialog.
web/src/components/feedback/FeedbackModal.tsx Adds ConfirmDialog + refactors close logic into forceClose/handleClose.
web/src/components/feedback/FeatureRequestModal.tsx Replaces unsaved-changes close confirmation with ConfirmDialog.
web/src/components/alerts/AlertRuleEditor.tsx Replaces unsaved-changes close confirmation with ConfirmDialog.
web/eslint.config.js Adds no-restricted-globals entries for alert/confirm/prompt.

Comment on lines +29 to +33
'no-restricted-globals': ['error',
{ name: 'alert', message: 'Use ConfirmDialog or Toast instead of browser alert().' },
{ name: 'confirm', message: 'Use ConfirmDialog instead of browser confirm().' },
{ name: 'prompt', message: 'Use a styled input modal instead of browser prompt().' },
],
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

The new no-restricted-globals rule will block confirm()/alert()/prompt() as bare globals, but it will not catch the pattern previously used in this codebase (window.confirm(...)). To actually prevent regressions, add a restriction for window.confirm/alert/prompt as well (e.g., no-restricted-properties for window/globalThis, or a no-restricted-syntax selector targeting MemberExpression[property.name="confirm"]).

Copilot uses AI. Check for mistakes.
Comment on lines +112 to +115
title={t('common:common.discardUnsavedChanges', 'Discard unsaved changes?')}
message={t('common:common.discardUnsavedChangesMessage', 'You have unsaved changes that will be lost.')}
confirmLabel={t('common:common.discard', 'Discard')}
cancelLabel={t('common:common.keepEditing', 'Keep editing')}
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

The i18n keys used here (common.discardUnsavedChanges, common.discardUnsavedChangesMessage, common.discard, common.keepEditing) don't exist in web/src/locales/en/common.json, so the UI will always fall back to the default strings. Consider adding these keys to common.json (preferred) or switching to existing common action keys to keep translations consistent.

Suggested change
title={t('common:common.discardUnsavedChanges', 'Discard unsaved changes?')}
message={t('common:common.discardUnsavedChangesMessage', 'You have unsaved changes that will be lost.')}
confirmLabel={t('common:common.discard', 'Discard')}
cancelLabel={t('common:common.keepEditing', 'Keep editing')}
title={t('common:discardUnsavedChanges', 'Discard unsaved changes?')}
message={t('common:discardUnsavedChangesMessage', 'You have unsaved changes that will be lost.')}
confirmLabel={t('common:discard', 'Discard')}
cancelLabel={t('common:keepEditing', 'Keep editing')}

Copilot uses AI. Check for mistakes.
Comment on lines +125 to +128
title={t('common:common.discardUnsavedChanges', 'Discard unsaved changes?')}
message={t('common:common.discardUnsavedChangesMessage', 'You have unsaved changes that will be lost.')}
confirmLabel={t('common:common.discard', 'Discard')}
cancelLabel={t('common:common.keepEditing', 'Keep editing')}
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

The i18n keys used here (common.discardUnsavedChanges, common.discardUnsavedChangesMessage, common.discard, common.keepEditing) don't exist in web/src/locales/en/common.json, so the UI will always fall back to the default strings. Consider adding these keys to common.json (preferred) or switching to existing common action keys to keep translations consistent.

Suggested change
title={t('common:common.discardUnsavedChanges', 'Discard unsaved changes?')}
message={t('common:common.discardUnsavedChangesMessage', 'You have unsaved changes that will be lost.')}
confirmLabel={t('common:common.discard', 'Discard')}
cancelLabel={t('common:common.keepEditing', 'Keep editing')}
title={t('common:discardUnsavedChanges', 'Discard unsaved changes?')}
message={t('common:discardUnsavedChangesMessage', 'You have unsaved changes that will be lost.')}
confirmLabel={t('common:discard', 'Discard')}
cancelLabel={t('common:keepEditing', 'Keep editing')}

Copilot uses AI. Check for mistakes.
Comment on lines +232 to +235
title={t('common:common.discardUnsavedChanges', 'Discard unsaved changes?')}
message={t('common:common.discardUnsavedChangesMessage', 'You have unsaved changes that will be lost.')}
confirmLabel={t('common:common.discard', 'Discard')}
cancelLabel={t('common:common.keepEditing', 'Keep editing')}
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

The i18n keys used here (common.discardUnsavedChanges, common.discardUnsavedChangesMessage, common.discard, common.keepEditing) don't exist in web/src/locales/en/common.json, so the UI will always fall back to the default strings. Consider adding these keys to common.json (preferred) or switching to existing common action keys to keep translations consistent.

Suggested change
title={t('common:common.discardUnsavedChanges', 'Discard unsaved changes?')}
message={t('common:common.discardUnsavedChangesMessage', 'You have unsaved changes that will be lost.')}
confirmLabel={t('common:common.discard', 'Discard')}
cancelLabel={t('common:common.keepEditing', 'Keep editing')}
title={t('common:discardUnsavedChanges', 'Discard unsaved changes?')}
message={t('common:discardUnsavedChangesMessage', 'You have unsaved changes that will be lost.')}
confirmLabel={t('common:discard', 'Discard')}
cancelLabel={t('common:keepEditing', 'Keep editing')}

Copilot uses AI. Check for mistakes.
Comment on lines +199 to +205
const handleClose = () => {
if (!success && (title.trim() !== '' || description.trim() !== '')) {
setShowDiscardConfirm(true)
return
}
forceClose()
}
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

handleClose is used from the Escape/Space keydown listener, but that useEffect only depends on isOpen. This can leave the key handler with stale success/title/description values, so Escape may discard edits without showing the confirm dialog. Fix by making the key handler depend on handleClose (and making handleClose stable via useCallback), or by using a ref to always read the latest state in the listener.

Copilot uses AI. Check for mistakes.
Comment on lines +247 to +250
title={t('common:common.discardUnsavedChanges', 'Discard unsaved changes?')}
message={t('common:common.discardUnsavedChangesMessage', 'You have unsaved changes that will be lost.')}
confirmLabel={t('common:common.discard', 'Discard')}
cancelLabel={t('common:common.keepEditing', 'Keep editing')}
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

The i18n keys used here (common.discardUnsavedChanges, common.discardUnsavedChangesMessage, common.discard, common.keepEditing) don't exist in web/src/locales/en/common.json, so the UI will always fall back to the default strings. Consider adding these keys to common.json (preferred) or switching to existing common action keys to keep translations consistent.

Suggested change
title={t('common:common.discardUnsavedChanges', 'Discard unsaved changes?')}
message={t('common:common.discardUnsavedChangesMessage', 'You have unsaved changes that will be lost.')}
confirmLabel={t('common:common.discard', 'Discard')}
cancelLabel={t('common:common.keepEditing', 'Keep editing')}
title="Discard unsaved changes?"
message="You have unsaved changes that will be lost."
confirmLabel="Discard"
cancelLabel="Keep editing"

Copilot uses AI. Check for mistakes.
Comment on lines +403 to +406
title={t('common:common.discardUnsavedChanges', 'Discard unsaved changes?')}
message={t('common:common.discardUnsavedChangesMessage', 'You have unsaved changes that will be lost.')}
confirmLabel={t('common:common.discard', 'Discard')}
cancelLabel={t('common:common.keepEditing', 'Keep editing')}
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

The i18n keys used here (common.discardUnsavedChanges, common.discardUnsavedChangesMessage, common.discard, common.keepEditing) don't exist in web/src/locales/en/common.json, so the UI will always fall back to the default strings. Consider adding these keys to common.json (preferred) or switching to existing common action keys to keep translations consistent.

Suggested change
title={t('common:common.discardUnsavedChanges', 'Discard unsaved changes?')}
message={t('common:common.discardUnsavedChangesMessage', 'You have unsaved changes that will be lost.')}
confirmLabel={t('common:common.discard', 'Discard')}
cancelLabel={t('common:common.keepEditing', 'Keep editing')}
title={t('feedback.discardUnsavedChangesTitle', 'Discard unsaved changes?')}
message={t('feedback.discardUnsavedChangesMessage', 'You have unsaved changes that will be lost.')}
confirmLabel={t('feedback.discard', 'Discard')}
cancelLabel={t('feedback.keepEditing', 'Keep editing')}

Copilot uses AI. Check for mistakes.
Comment on lines +399 to +408
<ConfirmDialog
isOpen={showDiscardConfirm}
onClose={() => setShowDiscardConfirm(false)}
onConfirm={forceClose}
title={t('common:common.discardUnsavedChanges', 'Discard unsaved changes?')}
message={t('common:common.discardUnsavedChangesMessage', 'You have unsaved changes that will be lost.')}
confirmLabel={t('common:common.discard', 'Discard')}
cancelLabel={t('common:common.keepEditing', 'Keep editing')}
variant="warning"
/>
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

ConfirmDialog uses BaseModal with z-[9999], but this component also renders the login prompt overlay at z-[10001]. If the user attempts to close with unsaved changes while the login prompt is open, the discard confirmation can render behind the login overlay and be non-interactive. Consider handling close by dismissing the login prompt first, or ensure the discard confirmation renders above any in-modal overlays (e.g., allow ConfirmDialog/BaseModal to take a higher z-index).

Copilot uses AI. Check for mistakes.
Comment on lines +190 to +193
title={t('common:common.discardUnsavedChanges', 'Discard unsaved changes?')}
message={t('common:common.discardUnsavedChangesMessage', 'You have unsaved changes that will be lost.')}
confirmLabel={t('common:common.discard', 'Discard')}
cancelLabel={t('common:common.keepEditing', 'Keep editing')}
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

The i18n keys used here (common.discardUnsavedChanges, common.discardUnsavedChangesMessage, common.discard, common.keepEditing) don't exist in web/src/locales/en/common.json, so the UI will always fall back to the default strings. Consider adding these keys to common.json (preferred) or switching to existing common action keys to keep translations consistent.

Suggested change
title={t('common:common.discardUnsavedChanges', 'Discard unsaved changes?')}
message={t('common:common.discardUnsavedChangesMessage', 'You have unsaved changes that will be lost.')}
confirmLabel={t('common:common.discard', 'Discard')}
cancelLabel={t('common:common.keepEditing', 'Keep editing')}
title={t('common:discardUnsavedChanges', 'Discard unsaved changes?')}
message={t('common:discardUnsavedChangesMessage', 'You have unsaved changes that will be lost.')}
confirmLabel={t('common:discard', 'Discard')}
cancelLabel={t('common:keepEditing', 'Keep editing')}

Copilot uses AI. Check for mistakes.
@clubanderson
Copy link
Copy Markdown
Collaborator Author

🔄 Auto-Applying Copilot Code Review

Copilot code review found 6 code suggestion(s) and 3 general comment(s).

@copilot Please apply all of the following code review suggestions:

  • web/src/components/namespaces/GrantAccessModal.tsx (line 115): title={t('common:discardUnsavedChanges', 'Discard unsaved changes?')} me...
  • web/src/components/namespaces/CreateNamespaceModal.tsx (line 128): title={t('common:discardUnsavedChanges', 'Discard unsaved changes?')} me...
  • web/src/components/gpu/ReservationFormModal.tsx (line 235): title={t('common:discardUnsavedChanges', 'Discard unsaved changes?')} me...
  • web/src/components/feedback/FeedbackModal.tsx (line 250): title="Discard unsaved changes?" message="You have unsaved changes that ...
  • web/src/components/feedback/FeatureRequestModal.tsx (line 406): title={t('feedback.discardUnsavedChangesTitle', 'Discard unsaved changes?')} ...
  • web/src/components/alerts/AlertRuleEditor.tsx (line 193): title={t('common:discardUnsavedChanges', 'Discard unsaved changes?')} me...

Also address these general comments:

  • web/eslint.config.js (line 33): The new no-restricted-globals rule will block confirm()/alert()/prompt() as bare globals, but it will not catch
  • web/src/components/feedback/FeedbackModal.tsx (line 205): handleClose is used from the Escape/Space keydown listener, but that useEffect only depends on isOpen. This can le
  • web/src/components/feedback/FeatureRequestModal.tsx (line 408): ConfirmDialog uses BaseModal with z-[9999], but this component also renders the login prompt overlay at `z-[10001]

Push all fixes in a single commit. Run cd web && npm run build && npm run lint before committing.


Auto-generated by copilot-review-apply workflow.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dco-signoff: yes Indicates the PR's author has signed the DCO. size/L Denotes a PR that changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants