Skip to content

Conversation

@mtrezza
Copy link
Member

@mtrezza mtrezza commented Dec 23, 2025

New Pull Request Checklist

Issue Description

When migrating settings from local to server storage, any conflicting server-stored setting is overwritten and any non-conflicting server-stored setting is deleted.

Approach

Show conflict dialog to let user handle migration:

image

This also fixes an issue where JS console scripts are not migrated to the server.

Summary by CodeRabbit

  • New Features
    • Migration now detects conflicts across settings, filters, views, and scripts; when conflicts are found a detailed modal lists them and lets you overwrite or cancel the migration.
    • Success/failure messaging updated to report totals including scripts and to indicate when nothing was migrated.
    • Migration can now proceed in overwrite mode to replace conflicting server items.

✏️ Tip: You can customize this high-level summary in your review settings.

@parse-github-assistant
Copy link

parse-github-assistant bot commented Dec 23, 2025

🚀 Thanks for opening this pull request! We appreciate your effort in improving the project. Please let us know once your pull request is ready for review.

@parseplatformorg
Copy link
Contributor

parseplatformorg commented Dec 23, 2025

Snyk checks have passed. No issues have been found so far.

Status Scanner Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@coderabbitai
Copy link

coderabbitai bot commented Dec 23, 2025

📝 Walkthrough

Walkthrough

Adds conflict-detection and optional overwrite handling to migration flows for views, filters, and scripts; DashboardSettings now aggregates conflicts, displays a modal for user resolution, and can retry migrations with overwrite consent.

Changes

Cohort / File(s) Summary
Dashboard UI & Conflict Modal
src/dashboard/Settings/DashboardSettings/DashboardSettings.react.js
migrateToServer(overwriteConflicts = false) signature updated; added showConflictDialog, handleConflictOverwrite, handleConflictCancel, renderConflictModal; imported Modal; added showConflictModal and migrationConflicts state; updated render flow to surface conflict modal and handle user overwrite/cancel.
Filter Preferences Manager
src/lib/FilterPreferencesManager.js
migrateToServer(appId, overwriteConflicts = false) now detects local vs server ID conflicts and returns conflicts when overwrite not requested; added private helper async _migrateFiltersToServer(appId, className, filters, existingFilterIds, overwriteConflicts) to perform conditional save/merge; preserves non-local server filters.
View Preferences Manager
src/lib/ViewPreferencesManager.js
migrateToServer(appId, overwriteConflicts = false) now detects server conflicts and returns conflict details when overwrite not requested; added private _migrateViewsToServer() to perform conditional merge/overwrite and cleaning/stringifying of view data.
Script Manager
src/lib/ScriptManager.js
migrateToServer(appId, overwriteConflicts = false) now detects script ID conflicts and may return {success:false, conflicts}; added private _migrateScriptsToServer(appId, localScripts, overwriteConflicts) to handle merge/overwrite saving and prune null/undefined fields.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant DashboardSettings as DashboardSettings(UI)
    participant ViewMgr as ViewPreferencesManager
    participant FilterMgr as FilterPreferencesManager
    participant ScriptMgr as ScriptManager
    participant Server

    User->>DashboardSettings: Click "Migrate to Server"
    activate DashboardSettings

    par Parallel: Check Views/Filters/Scripts
        DashboardSettings->>ViewMgr: migrateToServer(appId, false)
        activate ViewMgr
        ViewMgr->>Server: Load existing views
        Server-->>ViewMgr: Server views
        ViewMgr->>ViewMgr: Detect conflicts
        alt Conflicts
            ViewMgr-->>DashboardSettings: {success:false, conflicts}
        else No Conflicts
            ViewMgr->>ViewMgr: _migrateViewsToServer()
            ViewMgr->>Server: Save views
            Server-->>ViewMgr: OK
            ViewMgr-->>DashboardSettings: {success:true, viewCount}
        end
        deactivate ViewMgr

        DashboardSettings->>FilterMgr: migrateToServer(appId, false)
        activate FilterMgr
        FilterMgr->>Server: Load existing filters
        Server-->>FilterMgr: Server filters
        FilterMgr->>FilterMgr: Detect conflicts
        alt Conflicts
            FilterMgr-->>DashboardSettings: {success:false, conflicts}
        else No Conflicts
            FilterMgr->>FilterMgr: _migrateFiltersToServer()
            FilterMgr->>Server: Save filters
            Server-->>FilterMgr: OK
            FilterMgr-->>DashboardSettings: {success:true, filterCount}
        end
        deactivate FilterMgr

        DashboardSettings->>ScriptMgr: migrateToServer(appId, false)
        activate ScriptMgr
        ScriptMgr->>Server: Load existing scripts
        Server-->>ScriptMgr: Server scripts
        ScriptMgr->>ScriptMgr: Detect conflicts
        alt Conflicts
            ScriptMgr-->>DashboardSettings: {success:false, conflicts}
        else No Conflicts
            ScriptMgr->>ScriptMgr: _migrateScriptsToServer()
            ScriptMgr->>Server: Save scripts
            Server-->>ScriptMgr: OK
            ScriptMgr-->>DashboardSettings: {success:true, scriptCount}
        end
        deactivate ScriptMgr
    end

    alt Any Conflicts Returned
        rect rgba(255,215,155,0.6)
            DashboardSettings->>DashboardSettings: Aggregate conflicts
            DashboardSettings->>User: renderConflictModal() (showConflictModal=true)
        end

        User->>DashboardSettings: Choose Overwrite or Cancel
        alt Overwrite
            DashboardSettings->>ViewMgr: migrateToServer(appId, true)
            DashboardSettings->>FilterMgr: migrateToServer(appId, true)
            DashboardSettings->>ScriptMgr: migrateToServer(appId, true)
        else Cancel
            DashboardSettings->>DashboardSettings: handleConflictCancel()
        end
    end

    deactivate DashboardSettings
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description check ✅ Passed The PR description follows the required template structure with all essential sections completed: checklist items checked, issue description provided, and approach with visual reference included.
Title check ✅ Passed The PR title accurately describes the main change: adding a confirmation dialog to handle conflicts during settings migration to server, which aligns with the comprehensive conflict-handling flow implemented across multiple manager classes.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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

@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.

Actionable comments posted: 0

🧹 Nitpick comments (5)
src/lib/ViewPreferencesManager.js (2)

106-139: Duplicate server fetch can be optimized.

getConfigsByPrefix is called once here (line 108) for conflict detection, and then again inside _migrateViewsToServer (line 195). Consider passing existingViewConfigs and existingViewIds to _migrateViewsToServer to avoid the redundant network call.

🔎 Suggested optimization
-      await this._migrateViewsToServer(appId, localViews, overwriteConflicts);
+      await this._migrateViewsToServer(appId, localViews, existingViewIds, overwriteConflicts);

Then update _migrateViewsToServer to accept existingViewIds as a parameter instead of fetching again.


200-229: Consider extracting view config cleanup into a helper.

The logic for cleaning a view config (removing ID, null/undefined values, stringifying query) is duplicated across _migrateViewsToServer, _saveViewsToServer (lines 303-319), and _saveViewToServer (lines 341-354). Extracting this into a private _prepareViewConfig(view) method would reduce duplication and ensure consistency.

src/dashboard/Settings/DashboardSettings/DashboardSettings.react.js (2)

617-671: Consider moving inline styles to SCSS.

The conflict list uses extensive inline styles. For consistency with the rest of the component and easier maintenance, consider moving these to DashboardSettings.scss.


169-173: Clear migrationConflicts after overwrite to avoid stale state.

When the user confirms overwrite, migrationConflicts remains populated with the old conflict data. While it won't cause visible issues (the modal is hidden), clearing it maintains cleaner state.

🔎 Suggested fix
  handleConflictOverwrite() {
-   this.setState({ showConflictModal: false });
+   this.setState({ showConflictModal: false, migrationConflicts: [] });
    // User chose to overwrite - retry migration with overwrite flag
    this.migrateToServer(true);
  }
src/lib/FilterPreferencesManager.js (1)

228-259: Consider extracting filter config cleanup into a helper.

Similar to ViewPreferencesManager, the logic for preparing a filter config (removing ID, adding className, removing null/undefined values, stringifying filter) is duplicated across _migrateFiltersToServer, _saveFiltersToServer (lines 348-367), and _saveFilterToServer (lines 388-405). A shared _prepareFilterConfig(filter, className) helper would reduce duplication.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between abe4647 and 371e793.

📒 Files selected for processing (3)
  • src/dashboard/Settings/DashboardSettings/DashboardSettings.react.js
  • src/lib/FilterPreferencesManager.js
  • src/lib/ViewPreferencesManager.js
🧰 Additional context used
🧬 Code graph analysis (1)
src/lib/FilterPreferencesManager.js (2)
src/lib/ClassPreferences.js (2)
  • getAllPreferences (54-75)
  • className (62-62)
src/lib/generatePath.js (2)
  • filters (8-8)
  • filter (12-12)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Docker linux/amd64
🔇 Additional comments (3)
src/dashboard/Settings/DashboardSettings/DashboardSettings.react.js (1)

119-136: LGTM - conflict aggregation logic is sound.

The approach of collecting conflicts from both views and filters, then conditionally showing the dialog, correctly handles the migration flow. The early return after showing the conflict dialog properly prevents partial migration.

src/lib/FilterPreferencesManager.js (2)

221-267: LGTM - the helper correctly handles conditional saves.

The _migrateFiltersToServer method properly checks overwriteConflicts || !existingFilterIds.includes(filterId) before saving, ensuring non-conflicting filters are migrated while conflicts are skipped when not overwriting.


153-157: Filters in classes with conflicts are skipped entirely, preventing migration of non-conflicting filters.

When conflictingIds.length > 0 and !overwriteConflicts, line 153's condition fails and the entire class's migration is skipped (lines 153–157). This includes non-conflicting filters from that class. The underlying _migrateFiltersToServer method already handles per-filter conflict detection (line 250), so class-level gating creates an all-or-nothing behavior that differs from ViewPreferencesManager, which migrates items individually and only returns conflicts when requested.

If non-conflicting filters should always be migrated while collecting conflicts for user decision, remove the class-level gate and rely on the per-filter check at line 250.

coderabbitai[bot]
coderabbitai bot previously approved these changes Dec 23, 2025
Copy link

@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.

Actionable comments posted: 0

Caution

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

⚠️ Outside diff range comments (1)
src/dashboard/Settings/DashboardSettings/DashboardSettings.react.js (1)

101-171: Set loading state when retrying migration after conflict resolution.

The migration flow correctly aggregates conflicts from all three sources (views, filters, scripts) and displays the conflict dialog. However, when the user chooses to overwrite (line 184), this.migrateToServer(true) is called without setting migrationLoading back to true. This could result in a brief moment where the UI doesn't show a loading indicator during the retry.

🔧 Proposed fix to ensure loading state during retry
  handleConflictOverwrite() {
-   this.setState({ showConflictModal: false });
+   this.setState({ showConflictModal: false, migrationLoading: true });
    // User chose to overwrite - retry migration with overwrite flag
    this.migrateToServer(true);
  }

Otherwise, the conflict detection, aggregation, and success messaging logic is sound and correctly handles all three resource types.

🧹 Nitpick comments (2)
src/lib/ScriptManager.js (2)

195-249: Approve the conflict detection logic with a minor optimization opportunity.

The conflict detection and migration flow is well-structured. The logic correctly:

  • Detects conflicts by comparing local and server script IDs
  • Returns conflicts for user decision when overwriteConflicts is false
  • Delegates to the helper method to perform the actual migration

However, there's a minor inefficiency: existing scripts are fetched twice—once here (lines 212-215) and again in _migrateScriptsToServer (lines 308-311).

💡 Optional optimization to avoid duplicate fetch

Consider passing the already-fetched existingScriptConfigs to the helper method:

      // Proceed with migration (merge mode)
-     await this._migrateScriptsToServer(appId, localScripts, overwriteConflicts);
+     await this._migrateScriptsToServer(appId, localScripts, overwriteConflicts, existingScriptConfigs);
      return { success: true, scriptCount: localScripts.length };

Then update the helper signature to accept and use the cached configs instead of re-fetching.


301-345: Clarify the comment about merge mode.

The implementation correctly handles both merge and overwrite scenarios. However, the comment on line 242 says "Proceed with migration (merge mode)" which could be misleading since this method is called in both modes (merge when no conflicts, overwrite when conflicts exist and user approved).

📝 Suggested comment clarification
-     // Proceed with migration (merge mode)
+     // Proceed with migration (merge or overwrite mode based on overwriteConflicts flag)
      await this._migrateScriptsToServer(appId, localScripts, overwriteConflicts);

The merge/preserve behavior on lines 328-335 and the comment on line 339 correctly document that server scripts not in local storage are preserved during migration, which is the right behavior.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 371e793 and 431a92e.

📒 Files selected for processing (2)
  • src/dashboard/Settings/DashboardSettings/DashboardSettings.react.js
  • src/lib/ScriptManager.js
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-05-27T12:09:47.644Z
Learnt from: mtrezza
Repo: parse-community/parse-dashboard PR: 2828
File: src/dashboard/Data/Browser/Browser.react.js:1605-1607
Timestamp: 2025-05-27T12:09:47.644Z
Learning: In script execution dialogs in Parse Dashboard (specifically the `confirmExecuteScriptRows` method in `src/dashboard/Data/Browser/Browser.react.js`), individual `setState` calls to update `processedScripts` counter should be kept as-is rather than batched, because this provides real-time progress feedback to users in the dialog UI.

Applied to files:

  • src/dashboard/Settings/DashboardSettings/DashboardSettings.react.js
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Docker linux/amd64
  • GitHub Check: Node 18
🔇 Additional comments (5)
src/dashboard/Settings/DashboardSettings/DashboardSettings.react.js (5)

18-18: LGTM! Clean state and import additions.

The Modal import and new state properties (showConflictModal, migrationConflicts) are properly added to support the conflict handling flow.

Also applies to: 51-52


173-190: LGTM! Clear conflict handling flow.

The three new methods provide a clean separation of concerns:

  • showConflictDialog: prepares and displays the modal
  • handleConflictOverwrite: retries with overwrite permission
  • handleConflictCancel: cancels and notifies the user

The user feedback on cancellation (line 189) is helpful.


582-582: LGTM! Improved user-facing description.

The updated description text clearly informs users about the conflict detection and resolution flow, which improves transparency and sets proper expectations.


621-725: LGTM! Well-structured conflict modal with clear presentation.

The renderConflictModal method effectively:

  • Groups conflicts by type (views, filters, scripts)
  • Displays each conflict with a meaningful name and ID
  • Provides scrolling for long conflict lists
  • Uses appropriate Modal type (DANGER) and clear action buttons

The conditional formatting on lines 644, 663 handles spacing between sections gracefully, and the filter display (lines 668-671) includes both the filter name and class name for context.


553-553: No change needed to the condition.

The three managers (viewPreferencesManager, filterPreferencesManager, and scriptManager) are always initialized together in the constructor (lines 72-74), so checking two of them is sufficient to guarantee the third is initialized. The current condition at line 553 is safe as written.

Likely an incorrect or invalid review comment.

@mtrezza mtrezza changed the title feat: Add dialog to handle conflicts when migrating settings to server feat: Add confirmation dialog to handle conflicts when migrating settings to server Dec 23, 2025
@mtrezza mtrezza merged commit ae50b8d into parse-community:alpha Dec 23, 2025
12 checks passed
@mtrezza mtrezza deleted the feat/handle-server-config-conflicts branch December 23, 2025 18:33
parseplatformorg pushed a commit that referenced this pull request Dec 23, 2025
# [8.2.0-alpha.13](8.2.0-alpha.12...8.2.0-alpha.13) (2025-12-23)

### Features

* Add confirmation dialog to handle conflicts when migrating settings to server ([#3092](#3092)) ([ae50b8d](ae50b8d))
@parseplatformorg
Copy link
Contributor

🎉 This change has been released in version 8.2.0-alpha.13

@parseplatformorg parseplatformorg added the state:released-alpha Released as alpha version label Dec 23, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

state:released-alpha Released as alpha version

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants