Skip to content

feat(core-config): add unsaved changes confirmation dialog#465

Closed
ArthurKoba wants to merge 2 commits into
PasarGuard:devfrom
ArthurKoba:main
Closed

feat(core-config): add unsaved changes confirmation dialog#465
ArthurKoba wants to merge 2 commits into
PasarGuard:devfrom
ArthurKoba:main

Conversation

@ArthurKoba
Copy link
Copy Markdown

@ArthurKoba ArthurKoba commented May 11, 2026

  • Implement change tracking for core configuration editor
  • Block modal close when unsaved changes exist
  • Show confirmation dialog with options to keep editing or discard changes
  • Add translations for all supported languages (en, ru, fa, zh)
  • Reset dirty state after successful save operation

Solving the issue: #459

Summary by CodeRabbit

Release Notes

  • New Features
    • Configuration modals now display a confirmation dialog when users attempt to exit with unsaved changes
    • Users can choose to continue editing or discard their changes
    • Added multilingual support for the unsaved-changes flow in English, Persian, Russian, and Chinese

Review Change Stack

- Implement change tracking for core configuration editor
- Block modal close when unsaved changes exist
- Show confirmation dialog with options to keep editing or discard changes
- Add translations for all supported languages (en, ru, fa, zh)
- Reset dirty state after successful save operation
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 11, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 269333f6-9b36-48d1-af9d-10044c07c0f1

📥 Commits

Reviewing files that changed from the base of the PR and between 1795c22 and 48eb86b.

📒 Files selected for processing (1)
  • dashboard/src/components/dialogs/core-config-modal.tsx

Walkthrough

CoreConfigModal now checks react-hook-form's isDirty when the modal is closed. If dirty, it opens an AlertDialog to confirm discarding changes; handlers reset the form and close the modal on confirm. New localized strings were added in en/fa/ru/zh.

Changes

Unsaved Changes Confirmation Flow

Layer / File(s) Summary
Localization Strings
dashboard/public/statics/locales/en.json, dashboard/public/statics/locales/fa.json, dashboard/public/statics/locales/ru.json, dashboard/public/statics/locales/zh.json
Adds coreConfigModal.unsavedChanges, unsavedChangesMessage, keepEditing, discardChanges in all locales; coreConfigModal.shadowsocksTab reordered in English.
Component Imports & State
dashboard/src/components/dialogs/core-config-modal.tsx
Imports AlertDialog and AlertTriangle; introduces isConfirmExitDialogOpen state to control the confirmation dialog.
Close Handler & Event Wiring
dashboard/src/components/dialogs/core-config-modal.tsx
Adds handleModalCloseWithCheck, handleConfirmExit, and handleCancelExit; replaces Dialog onOpenChange and cancel button wiring to use the new handler and checks form.formState.isDirty.
Confirmation Dialog Rendering
dashboard/src/components/dialogs/core-config-modal.tsx
Renders an AlertDialog that uses the new i18n keys and wires confirm to reset the form and close the modal, cancel to dismiss the confirmation.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I nibbled keys and found a trace,
A tiny unsaved, hiding in the space.
A gentle bell — "Keep editing" or "Discard" — so neat,
Now forms won't vanish when you leap to your feet.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the primary change: adding an unsaved changes confirmation dialog to the core configuration modal, which is the main feature implemented across all modified files.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
dashboard/src/components/dialogs/core-config-modal.tsx (1)

191-208: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Potential stale dirty-state check can bypass the confirmation dialog.

The close guard depends on hasUnsavedChanges, which is asynchronously mirrored from form state. A fast edit→close sequence can still read false and close without prompting.

Suggested fix
-  const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false)
+  const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false)
+  const [hasUnsavedChangesRef, setHasUnsavedChangesRef] = useState(false)

   useEffect(() => {
     const subscription = form.watch(() => {
-      setHasUnsavedChanges(form.formState.isDirty)
+      const dirty = form.formState.isDirty
+      setHasUnsavedChanges(dirty)
+      setHasUnsavedChangesRef(dirty)
     })
     return () => subscription.unsubscribe()
   }, [form])

   const handleModalCloseWithCheck = useCallback((open: boolean) => {
-    if (!open && hasUnsavedChanges) {
+    if (!open && hasUnsavedChangesRef) {
       setIsConfirmExitDialogOpen(true)
     } else {
       onOpenChange(open)
       if (!open) {
         setHasUnsavedChanges(false)
+        setHasUnsavedChangesRef(false)
       }
     }
-  }, [hasUnsavedChanges, onOpenChange])
+  }, [hasUnsavedChangesRef, onOpenChange])

Also applies to: 218-227

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@dashboard/src/components/dialogs/core-config-modal.tsx` around lines 191 -
208, The local hasUnsavedChanges state can lag behind the form's actual dirty
flag and let a fast edit→close bypass the confirm dialog; remove or stop relying
on hasUnsavedChanges and instead read the form's current dirty state directly
when deciding to prompt (use form.formState.isDirty in the close/guard logic
that opens setIsConfirmExitDialogOpen), and update any other checks (e.g., the
other close handler around lines 218-227) to use form.formState.isDirty rather
than the stale hasUnsavedChanges; keep the form.watch subscription only for UI
updates if still needed or remove it entirely to avoid duplication.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@dashboard/src/components/dialogs/core-config-modal.tsx`:
- Around line 191-208: The local hasUnsavedChanges state can lag behind the
form's actual dirty flag and let a fast edit→close bypass the confirm dialog;
remove or stop relying on hasUnsavedChanges and instead read the form's current
dirty state directly when deciding to prompt (use form.formState.isDirty in the
close/guard logic that opens setIsConfirmExitDialogOpen), and update any other
checks (e.g., the other close handler around lines 218-227) to use
form.formState.isDirty rather than the stale hasUnsavedChanges; keep the
form.watch subscription only for UI updates if still needed or remove it
entirely to avoid duplication.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 73bdfa51-0cbf-464b-97ec-ed8aa6d32d18

📥 Commits

Reviewing files that changed from the base of the PR and between 3100cd7 and 1795c22.

📒 Files selected for processing (5)
  • dashboard/public/statics/locales/en.json
  • dashboard/public/statics/locales/fa.json
  • dashboard/public/statics/locales/ru.json
  • dashboard/public/statics/locales/zh.json
  • dashboard/src/components/dialogs/core-config-modal.tsx

Use form.formState.isDirty directly instead of mirrored state to prevent
race condition where fast edit→close sequence could bypass confirmation
dialog. Remove unnecessary state synchronization and simplify logic by
reading form state at decision time.
@ArthurKoba ArthurKoba changed the base branch from main to dev May 11, 2026 09:45
@ArthurKoba
Copy link
Copy Markdown
Author

I made a change in the wrong branch. I'll fix it now and create a correct pull request from my fork and the dev branch.

@ArthurKoba ArthurKoba closed this May 11, 2026
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