Skip to content

[chores] Improvements to the dashboard map#809

Open
dee077 wants to merge 13 commits into
masterfrom
feature/705-improvements-to-dashboard-maps
Open

[chores] Improvements to the dashboard map#809
dee077 wants to merge 13 commits into
masterfrom
feature/705-improvements-to-dashboard-maps

Conversation

@dee077

@dee077 dee077 commented May 27, 2026

Copy link
Copy Markdown
Member

Checklist

  • I have read the OpenWISP Contributing Guidelines.
  • I have manually tested the changes proposed in this pull request.
  • I have written new test cases for new code and/or updated existing tests for changes to existing code.
  • I have updated the documentation.

Reference to Existing Issue

Closes #705.

Description of Changes

  • The closeButtonHandler and addNavigationHandlers can be generalized, so event handlers aren't re-added on every render. Check out $(parent).on("click", targetID, function(){})
  • The floorplan overlay should close when the ESC key is pressed.
  • Implement a utility method to unbind all event listeners and destroy Netjsongraph.js objects when closing the floorplan overlay.
  • There is reusable code inside the openFloorPlan function. Make this function leaner. Also, global variables (e.g., allResults) should be scoped inside openFloorPlan.
  • Find a better solution for handling the height of the popup, perhaps we can center the pop up to the map after the content in is reloaded due to having clicked on any filter or search?

Screenshot

Screencast.from.2026-05-28.03-49-01.webm

Note: This PR shows changes of #796, as it has been built on top of it. For now, we can just wait for the above PR to get merged, and then we can move forward with this

@dee077 dee077 self-assigned this May 27, 2026
@coderabbitai

coderabbitai Bot commented May 27, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR centralizes popup and floorplan state, converts popup content loading to an async AJAX flow with abortable requests, refactors popup rendering and event handling to support replace-vs-append loading and infinite scroll, scopes floorplan overlay state with session guards and delegated navigation handlers, adjusts CSS for loading states and spinner placement, replaces hardcoded admin map URLs with a runtime window._owMapPageUrl, and updates Selenium tests to wait on the new loading-state classes.

Sequence Diagram(s)

sequenceDiagram
  participant NetJSONGraph
  participant MapLeaflet
  participant Client
  participant Server
  participant PopupTable
  NetJSONGraph->>MapLeaflet: nodePopup triggers loadPopUpContent(node, netjson)
  MapLeaflet->>Client: invoke loadPopUpContent
  Client->>Server: AJAX request for devices (debounced, abortable)
  Server-->>Client: returns devices + nextUrl
  Client->>PopupTable: renderRows(devices) / update DOM (replace or append)
  PopupTable-->>Client: hide spinner / remove loading classes
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • nemesifier
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title '[chores] Improvements to the dashboard map' follows the required format with the [chores] type prefix and clearly describes the main changes throughout the PR.
Description check ✅ Passed The description includes all major sections: completed checklist items, issue reference (#705), detailed change descriptions, and a screencast demonstration.
Linked Issues check ✅ Passed The PR addresses most critical coding requirements from #705: event handler generalization and delegation, ESC key overlay closing, event listener cleanup, spinner logic fixes, code refactoring, test improvements, API parameter changes, and URL hardcoding removal.
Out of Scope Changes check ✅ Passed All changes in the PR are scoped to objectives from #705: CSS styling updates, JavaScript refactoring for floorplan/device-map, test suite enhancements, and template configuration are all within the stated requirements.
Bug Fixes ✅ Passed PR is labeled as "enhancement," not a bug-fix PR. The custom check is conditional ("If the PR fixes a bug..."), so it does not apply to this refactoring/improvement PR.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/705-improvements-to-dashboard-maps

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.

@dee077 dee077 changed the title Feature/705 improvements to dashboard maps [feature] Improvements to dashboard maps May 27, 2026
Comment thread openwisp_monitoring/device/static/monitoring/js/floorplan.js Fixed
@coderabbitai coderabbitai Bot added the enhancement New feature or request label May 27, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 5

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

Inline comments:
In `@openwisp_monitoring/device/static/monitoring/js/device-map.js`:
- Around line 202-266: The popup teardown currently only clears _popupState but
doesn't cancel a queued fetchDevices timeout or abort an in-flight $.ajax;
update fetchDevices to store the timeout id (fetchDevicesTimeout) and the jqXHR
(e.g., fetchDevicesRequest) when starting the request, add logic to the error
handler to ignore aborts, and in the popup teardown routine where _popupState is
cleared also call clearTimeout(fetchDevicesTimeout) and if (fetchDevicesRequest)
fetchDevicesRequest.abort(); additionally clear/reset fetchDevicesRequest in the
ajax complete() callback so subsequent teardowns are safe.
- Around line 141-142: renderRows is performing global DOM reads/writes (e.g.,
using $(".map-detail") and updating "`#status-filter`") which causes one popup to
overwrite another; change renderRows and the popup helper used by nodePopup to
accept (or locate) the current popup element and scope all DOM queries/updates
to that element (use popupElement.querySelector or $(popupElement).find(...)
instead of global selectors), replace global id usage like "`#status-filter`" with
scoped selectors inside the popup (or convert to a class and scope it) and
update all occurrences referenced in renderRows, nodePopup and the ranges called
out (lines ~158-187, ~251, ~271-285) to use the scoped popup element.

In `@openwisp_monitoring/device/static/monitoring/js/floorplan.js`:
- Around line 279-293: removeUrlFragment currently requires an initialized map
instance to clear the indoor URL fragment, which leaves a stale hash if the
overlay is closed before renderIndoorMap sets maps[currentFloor]; update
removeUrlFragment (using getFloorplanState, floorplanState.state.locationId and
floorplanState.state.currentFloor) to fall back to removing the URL fragment
even when floorplanState.maps[currentFloor] is undefined: if the map exists keep
using maps[currentFloor].utils.removeUrlFragment(id), otherwise clear the
fragment directly (e.g. via history.replaceState to remove location.hash) when
state.locationId is present so the bookmarkable overlay hash is reliably
removed.
- Around line 143-155: openFloorPlan currently allows id = null but
setFloorplanState expects floorplanState.state.locationId to be non-null; update
openFloorPlan to ensure a valid locationId before calling setFloorplanState by
either deriving a proper id (from url or floor) or throwing an error if id is
absent. Locate the openFloorPlan function and the call to
setFloorplanState(buildInitialFloorplanState(url, id, floor)) and add a guard
that computes/validates a non-null id (or fails fast) and pass that canonical id
into buildInitialFloorplanState so the persisted state never contains
locationId=null.
- Around line 476-568: The onReady() continuation mutates map/ECharts/DOM after
awaiting img.decode() without checking if the floorplan session was closed or
replaced; to fix, capture the current session identifier before awaiting (e.g.,
read getFloorplanState()._sessionId or other unique session token), then
immediately after await img.decode() re-read getFloorplanState() and compare the
session id and state; if the state is missing or the session id differs, return
early and avoid touching map, this.data, DOM, or calling
pushIndoorMapIdFragment; update logic around the img.decode() await in onReady()
to perform this guard so destroyed/replaced sessions don't get mutated.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 28b6dc1c-c1b0-4b82-b21c-8437f66cbe0a

📥 Commits

Reviewing files that changed from the base of the PR and between 23c8fc8 and 5531f9d.

⛔ Files ignored due to path filters (1)
  • openwisp_monitoring/device/static/monitoring/js/lib/netjsongraph.min.js is excluded by !**/*.min.js
📒 Files selected for processing (3)
  • openwisp_monitoring/device/static/monitoring/css/device-map.css
  • openwisp_monitoring/device/static/monitoring/js/device-map.js
  • openwisp_monitoring/device/static/monitoring/js/floorplan.js
📜 Review details
⏰ 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). (11)
  • GitHub Check: Python==3.12 | django~=4.2.0
  • GitHub Check: Python==3.11 | django~=5.2.0
  • GitHub Check: Python==3.13 | django~=5.2.0
  • GitHub Check: Python==3.12 | django~=5.2.0
  • GitHub Check: Python==3.11 | django~=4.2.0
  • GitHub Check: Python==3.13 | django~=5.1.0
  • GitHub Check: Python==3.12 | django~=5.1.0
  • GitHub Check: Python==3.10 | django~=4.2.0
  • GitHub Check: Python==3.10 | django~=5.1.0
  • GitHub Check: Python==3.10 | django~=5.2.0
  • GitHub Check: Python==3.11 | django~=5.1.0
🧰 Additional context used
📓 Path-based instructions (1)
{**/*.sh,**/*.{js,ts,tsx,jsx},**/*.py,**/*.rb,**/*.go,**/*.java,**/*.cs,**/*.cpp,**/*.c}

📄 CodeRabbit inference engine (Custom checks)

Cryptic or non-obvious code (regex, complex bash commands, or hard-to-read code) must include a concise comment explaining why it is needed and why the complexity is acceptable

Files:

  • openwisp_monitoring/device/static/monitoring/js/device-map.js
  • openwisp_monitoring/device/static/monitoring/js/floorplan.js
🪛 GitHub Check: CodeQL
openwisp_monitoring/device/static/monitoring/js/floorplan.js

[failure] 34-34: Insecure randomness
This uses a cryptographically insecure random number generated at Math.random() in a security context.

🔇 Additional comments (1)
openwisp_monitoring/device/static/monitoring/css/device-map.css (1)

150-162: LGTM!

Also applies to: 189-189

Comment thread openwisp_monitoring/device/static/monitoring/js/device-map.js Outdated
Comment thread openwisp_monitoring/device/static/monitoring/js/device-map.js
Comment thread openwisp_monitoring/device/static/monitoring/js/floorplan.js Outdated
Comment thread openwisp_monitoring/device/static/monitoring/js/floorplan.js Outdated
Comment thread openwisp_monitoring/device/static/monitoring/js/floorplan.js
@nemesifier nemesifier changed the title [feature] Improvements to dashboard maps [chores] Improvements to the dashboard map May 29, 2026
@dee077
dee077 force-pushed the feature/705-improvements-to-dashboard-maps branch from 5531f9d to 0ab579f Compare May 31, 2026 13:29

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

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

Inline comments:
In `@openwisp_monitoring/device/static/monitoring/css/device-map.css`:
- Around line 192-195: The spinner is horizontally off-center because transform
only translates vertically; update the rule for
.table-container.is-loading-append .table-spinner to translate both axes (e.g.,
use translate(-50%, -50%) or combine translateX(-50%) and translateY(-50%)) so
the spinner's center is aligned at left: 50%; keep the existing top and left
declarations and the !important flags intact.

In `@openwisp_monitoring/device/static/monitoring/js/device-map.js`:
- Around line 199-267: The fetchDevices function currently uses a single loading
flag that blocks any new fetch while one is active; change it so only concurrent
append requests are blocked and non-append (replace) requests cancel/overtake
in-flight append work: at the top of fetchDevices, keep the early-return for
missing url but replace the blanket "if (loading) return" with logic that (a) if
append and loading (or loadingAppend) is true, return to block concurrent
appends, and (b) if not append and activeRequest exists (and especially if the
activeRequest was an append), call activeRequest.abort() and clear any
append-specific flags before proceeding; introduce/track a distinct append flag
(e.g., loadingAppend) or inspect the append parameter when setting/clearing
flags so success/complete handlers still update devices/nextUrl correctly; use
activeRequest.abort() to ensure replace requests overtake in-flight appends and
then start the new replace fetch.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: a82f7863-336a-4c97-8b42-c481fff5fd28

📥 Commits

Reviewing files that changed from the base of the PR and between 5531f9d and 0ab579f.

📒 Files selected for processing (4)
  • openwisp_monitoring/device/static/monitoring/css/device-map.css
  • openwisp_monitoring/device/static/monitoring/js/device-map.js
  • openwisp_monitoring/device/static/monitoring/js/floorplan.js
  • openwisp_monitoring/tests/test_selenium.py
📜 Review details
⏰ 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). (11)
  • GitHub Check: Python==3.11 | django~=4.2.0
  • GitHub Check: Python==3.12 | django~=4.2.0
  • GitHub Check: Python==3.10 | django~=4.2.0
  • GitHub Check: Python==3.10 | django~=5.1.0
  • GitHub Check: Python==3.13 | django~=5.2.0
  • GitHub Check: Python==3.12 | django~=5.2.0
  • GitHub Check: Python==3.12 | django~=5.1.0
  • GitHub Check: Python==3.11 | django~=5.1.0
  • GitHub Check: Python==3.13 | django~=5.1.0
  • GitHub Check: Python==3.11 | django~=5.2.0
  • GitHub Check: Python==3.10 | django~=5.2.0
🧰 Additional context used
📓 Path-based instructions (1)
{**/*.sh,**/*.{js,ts,tsx,jsx},**/*.py,**/*.rb,**/*.go,**/*.java,**/*.cs,**/*.cpp,**/*.c}

📄 CodeRabbit inference engine (Custom checks)

Cryptic or non-obvious code (regex, complex bash commands, or hard-to-read code) must include a concise comment explaining why it is needed and why the complexity is acceptable

Files:

  • openwisp_monitoring/tests/test_selenium.py
  • openwisp_monitoring/device/static/monitoring/js/floorplan.js
  • openwisp_monitoring/device/static/monitoring/js/device-map.js
🧠 Learnings (5)
📚 Learning: 2026-02-21T01:03:37.822Z
Learnt from: nemesifier
Repo: openwisp/openwisp-monitoring PR: 738
File: openwisp_monitoring/tests/test_selenium.py:827-859
Timestamp: 2026-02-21T01:03:37.822Z
Learning: In Selenium tests (e.g., in openwisp_monitoring/tests/test_selenium.py and similar test files), when testing JS animations on dashboards or elements driven by JavaScript (such as real-time location updates), insert a short sleep (e.g., sleep(0.3)) before WebDriverWait assertions to allow animations to complete and reduce flakiness. Note: use this as a targeted workaround and prefer explicit waits or animation-end checks where possible to avoid relying on fixed delays.

Applied to files:

  • openwisp_monitoring/tests/test_selenium.py
📚 Learning: 2026-02-21T18:44:28.852Z
Learnt from: dee077
Repo: openwisp/openwisp-monitoring PR: 738
File: openwisp_monitoring/device/api/views.py:263-281
Timestamp: 2026-02-21T18:44:28.852Z
Learning: In openwisp-monitoring, MonitoringIndoorCoordinatesList inherits organization scoping from the parent IndoorCoordinatesList (from openwisp-controller), which uses FilterByParentManaged mixin and filters by location_id in get_queryset(). The child class only overrides the queryset attribute to add monitoring-specific select_related fields; this pattern is safe as long as get_queryset() from the parent is not bypassed. During reviews, verify that MonitoringIndoorCoordinatesList continues to rely on the parent's get_queryset() and that any added select_related fields in the child do not alter the parent's filtering logic.

Applied to files:

  • openwisp_monitoring/tests/test_selenium.py
📚 Learning: 2026-02-25T18:42:08.825Z
Learnt from: dee077
Repo: openwisp/openwisp-monitoring PR: 738
File: openwisp_monitoring/tests/test_selenium.py:309-317
Timestamp: 2026-02-25T18:42:08.825Z
Learning: In Selenium tests that use ChannelsLiveServerTestCase (e.g., openwisp_monitoring/tests/test_selenium.py), override settings to configure CHANNEL_LAYERS with channels_redis.core.RedisChannelLayer instead of InMemoryChannelLayer. This is required because the live server runs in a separate process from the test process, and InMemoryChannelLayer is per-process only and cannot handle cross-process WebSocket broadcasting needed for real-time location update tests.

Applied to files:

  • openwisp_monitoring/tests/test_selenium.py
📚 Learning: 2026-03-14T18:39:04.626Z
Learnt from: UltraBot05
Repo: openwisp/openwisp-monitoring PR: 766
File: openwisp_monitoring/utils.py:59-68
Timestamp: 2026-03-14T18:39:04.626Z
Learning: In this repository (openwisp/openwisp-monitoring), the project targets Python 3.10–3.13 as defined by CI. Do not flag backports.zoneinfo as a missing dependency; zoneinfo is a built-in module in all supported Python versions. When reviewing Python code, assume zoneinfo is available and avoid suggesting installation of backports.zoneinfo. If a file imports zoneinfo or uses it for time zone handling, treat it as standard library usage compatible with the supported CI matrix.

Applied to files:

  • openwisp_monitoring/tests/test_selenium.py
📚 Learning: 2026-03-14T18:39:04.626Z
Learnt from: UltraBot05
Repo: openwisp/openwisp-monitoring PR: 766
File: openwisp_monitoring/utils.py:59-68
Timestamp: 2026-03-14T18:39:04.626Z
Learning: In the openwisp-monitoring project, targets are Linux-based environments. Do not flag a Windows-specific tzdata package as a missing dependency in code reviews for Python files (e.g., openwisp_monitoring/utils.py). If a platform-specific dependency is truly required, document the exception in review guidelines and ensure CI/packaging checks enforce platform constraints rather than manual review.

Applied to files:

  • openwisp_monitoring/tests/test_selenium.py
🔇 Additional comments (20)
openwisp_monitoring/device/static/monitoring/js/floorplan.js (19)

7-38: LGTM!


40-52: LGTM!


84-116: LGTM!


118-127: LGTM!


129-141: LGTM!


143-168: LGTM!


170-232: LGTM!


234-257: LGTM!


259-280: LGTM!


282-317: LGTM!


319-346: LGTM!


348-386: LGTM!


388-405: LGTM!


407-433: LGTM!


435-609: LGTM!


610-643: LGTM!


645-662: LGTM!


664-700: LGTM!


702-703: LGTM!

openwisp_monitoring/tests/test_selenium.py (1)

325-350: LGTM!

Also applies to: 390-440, 475-477

Comment thread openwisp_monitoring/device/static/monitoring/css/device-map.css Outdated
Comment thread openwisp_monitoring/device/static/monitoring/js/device-map.js
@openwisp-companion

Copy link
Copy Markdown

The CI is failing due to transient infrastructure issues (not related to your code). I have restarted the failed jobs automatically (1/3).

@dee077
dee077 force-pushed the feature/705-improvements-to-dashboard-maps branch from 0ab579f to ee83d0d Compare May 31, 2026 13:47
@dee077

dee077 commented May 31, 2026

Copy link
Copy Markdown
Member Author

Updates

  • Made loading overlay consistent with filter for infinite scroll as well
Infinite.scroll.webm
  • Fix tests and address coderabbit comments.

@dee077
dee077 marked this pull request as ready for review May 31, 2026 13:55

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (2)
openwisp_monitoring/device/static/monitoring/js/floorplan.js (2)

506-518: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Rollback failed image initialization so the floor can be retried.

maps[floor] is registered before img.decode(). If the image load fails, this catch exits without clearing that entry or the floor container, and showFloor() will treat the floor as already rendered on the next visit instead of retrying renderIndoorMap().

Suggested fix
          try {
            await img.decode();
          } catch (e) {
            console.error("Failed to load floorplan image:", e);
+           const latestState = getFloorplanState();
+           if (latestState?.maps?.[floor] === indoorMap) {
+             delete latestState.maps[floor];
+             setFloorplanState(latestState);
+           }
+           $(`#floor-content-${floor}`).remove();
            $(".floorplan-loading-spinner").hide();
            return;
          }
🤖 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 `@openwisp_monitoring/device/static/monitoring/js/floorplan.js` around lines
506 - 518, The code registers floorplanState.maps[floor] before the image is
confirmed loaded, so on img.decode() failure the entry remains and showFloor()
will skip re-rendering; fix by deferring the assignment of
floorplanState.maps[floor] (and the setFloorplanState call) until after await
img.decode() succeeds, or if you prefer to keep the assignment early then in the
img.decode() catch remove the stale entry (delete floorplanState.maps[floor] and
call setFloorplanState) and clear the floor container so
renderIndoorMap()/showFloor() can retry; update the catch to also hide the
loading spinner as already done.

151-167: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Handle open-time failures before leaving the overlay mounted.

The overlay and backdrop are created before the first awaited fetch, but this path does not catch fetchData() / showFloor() failures. Current callers fire-and-forget window.openFloorPlan(...), so a rejected promise here leaves a half-built overlay on screen and surfaces as an unhandled rejection.

Suggested fix
-    await fetchData(url, floor);
-    const floorplanState = getFloorplanState();
-    if (!floorplanState?.state) return;
-    calculateNavigationState(floorplanState.state.currentFloor);
-
-    addFloorButtons();
-    await showFloor(url, floorplanState.state.currentFloor);
+    try {
+      await fetchData(url, floor);
+      const floorplanState = getFloorplanState();
+      if (!floorplanState?.state) return;
+      calculateNavigationState(floorplanState.state.currentFloor);
+
+      addFloorButtons();
+      await showFloor(url, floorplanState.state.currentFloor);
+    } catch (error) {
+      destroyFloorplan();
+      return;
+    }
🤖 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 `@openwisp_monitoring/device/static/monitoring/js/floorplan.js` around lines
151 - 167, The overlay and backdrop are mounted before awaiting
fetchData/showFloor, so any rejection leaves a partial UI and an unhandled
promise; wrap the async sequence starting from setFloorplanState(...) through
await showFloor(...) in a try/catch (or add .catch) that on error reverses the
DOM mutations (remove the created $floorPlanContainer/$floorNavigation from
`#dashboard-map-overlay` and `#floorplan-overlay` and remove the "active" class
added to .menu-backdrop), clears or resets state via setFloorplanState(null) or
similar, and rethrows or handle/log the error to avoid an unhandled rejection;
locate the code around createFloorPlanContainer(), createFloorNavigation(),
fetchData(url, floor), showFloor(url, ...), getFloorplanState() to implement the
cleanup in the catch block so callers of window.openFloorPlan(...) won't leave
the overlay mounted on failure.
♻️ Duplicate comments (1)
openwisp_monitoring/device/static/monitoring/js/device-map.js (1)

203-205: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Prevent aborted requests from resetting loading/UI state

fetchDevices() aborts the previous activeRequest when !append, but the jqXHR complete() handler unconditionally sets activeRequest = null, loading = false, hides the spinner, and removes loading classes. Since the replacement request is started later inside setTimeout(ms), the aborted request’s complete() can run during the delay and make the popup look idle while the next fetch is pending/in-flight.

      if (!append && activeRequest) {
        activeRequest.abort();
      }
Suggested fix
     let fetchDevicesTimeout;
     let activeRequest = null;
+    let activeRequestId = 0;
     let loading = false;
     function fetchDevices(url, ms = 0, append) {
       if (!url) return;
       if (append && loading) return;
       clearTimeout(fetchDevicesTimeout);
       if (!append && activeRequest) {
         activeRequest.abort();
       }
       loading = true;
       const container = el.find(".table-container");
       const spinner = el.find(".table-spinner");
       spinner.show();
@@
-      fetchDevicesTimeout = setTimeout(() => {
+      const requestId = ++activeRequestId;
+      fetchDevicesTimeout = setTimeout(() => {
         let params = new URLSearchParams();
@@
-        activeRequest = $.ajax({
+        const request = $.ajax({
           dataType: "json",
           url: fetchUrl,
           xhrFields: { withCredentials: true },
@@
           error(_jqXHR, textStatus) {
             if (textStatus === "abort") return;
             console.error(gettext("Could not load more devices from"), url);
             alert(gettext("Could not load more devices."));
           },
           complete() {
+            if (requestId !== activeRequestId || activeRequest !== request) return;
             activeRequest = null;
             loading = false;
             spinner.hide();
             container.removeClass("is-loading");
             container.removeClass("is-loading-append");
             container.css("min-height", "");
             container.css("--table-spinner-top", "");
           },
         });
+        activeRequest = request;
       }, ms);
     }
🤖 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 `@openwisp_monitoring/device/static/monitoring/js/device-map.js` around lines
203 - 205, The abort of activeRequest in fetchDevices() can cause the aborted
jqXHR complete() handler to clear UI/loading state while a replacement request
is scheduled inside setTimeout(ms); update the logic so the complete() handler
only clears activeRequest/loading/spinner when the completing request is still
the current activeRequest (or use a per-request id token) instead of
unconditionally nulling them—modify the complete handler in device-map.js to
check xhr === activeRequest (or compare requestId) before changing loading,
hiding the spinner, or removing loading classes so aborted or stale completions
do not reset the UI for the new fetch.
🤖 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.

Inline comments:
In `@openwisp_monitoring/tests/test_selenium.py`:
- Around line 334-347: The `_wait_for_popup_table_ready` method contains a
non-obvious predicate that polls for the absence of specific CSS classes
(`is-loading` and `is-loading-append`) to determine when the popup table is
ready. Add a concise comment above the WebDriverWait call explaining why
monitoring these CSS classes is used instead of directly checking spinner
visibility, and why this approach provides a stable readiness signal. This
clarifies the tight coupling to the popup's JS/CSS loading contract for future
maintainers.

---

Outside diff comments:
In `@openwisp_monitoring/device/static/monitoring/js/floorplan.js`:
- Around line 506-518: The code registers floorplanState.maps[floor] before the
image is confirmed loaded, so on img.decode() failure the entry remains and
showFloor() will skip re-rendering; fix by deferring the assignment of
floorplanState.maps[floor] (and the setFloorplanState call) until after await
img.decode() succeeds, or if you prefer to keep the assignment early then in the
img.decode() catch remove the stale entry (delete floorplanState.maps[floor] and
call setFloorplanState) and clear the floor container so
renderIndoorMap()/showFloor() can retry; update the catch to also hide the
loading spinner as already done.
- Around line 151-167: The overlay and backdrop are mounted before awaiting
fetchData/showFloor, so any rejection leaves a partial UI and an unhandled
promise; wrap the async sequence starting from setFloorplanState(...) through
await showFloor(...) in a try/catch (or add .catch) that on error reverses the
DOM mutations (remove the created $floorPlanContainer/$floorNavigation from
`#dashboard-map-overlay` and `#floorplan-overlay` and remove the "active" class
added to .menu-backdrop), clears or resets state via setFloorplanState(null) or
similar, and rethrows or handle/log the error to avoid an unhandled rejection;
locate the code around createFloorPlanContainer(), createFloorNavigation(),
fetchData(url, floor), showFloor(url, ...), getFloorplanState() to implement the
cleanup in the catch block so callers of window.openFloorPlan(...) won't leave
the overlay mounted on failure.

---

Duplicate comments:
In `@openwisp_monitoring/device/static/monitoring/js/device-map.js`:
- Around line 203-205: The abort of activeRequest in fetchDevices() can cause
the aborted jqXHR complete() handler to clear UI/loading state while a
replacement request is scheduled inside setTimeout(ms); update the logic so the
complete() handler only clears activeRequest/loading/spinner when the completing
request is still the current activeRequest (or use a per-request id token)
instead of unconditionally nulling them—modify the complete handler in
device-map.js to check xhr === activeRequest (or compare requestId) before
changing loading, hiding the spinner, or removing loading classes so aborted or
stale completions do not reset the UI for the new fetch.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 0c1963a1-44d9-47b7-a3b0-cb964e406865

📥 Commits

Reviewing files that changed from the base of the PR and between 0ab579f and ee83d0d.

📒 Files selected for processing (4)
  • openwisp_monitoring/device/static/monitoring/css/device-map.css
  • openwisp_monitoring/device/static/monitoring/js/device-map.js
  • openwisp_monitoring/device/static/monitoring/js/floorplan.js
  • openwisp_monitoring/tests/test_selenium.py
📜 Review details
⏰ 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). (11)
  • GitHub Check: Python==3.11 | django~=4.2.0
  • GitHub Check: Python==3.10 | django~=5.2.0
  • GitHub Check: Python==3.12 | django~=4.2.0
  • GitHub Check: Python==3.13 | django~=5.1.0
  • GitHub Check: Python==3.12 | django~=5.2.0
  • GitHub Check: Python==3.13 | django~=5.2.0
  • GitHub Check: Python==3.12 | django~=5.1.0
  • GitHub Check: Python==3.10 | django~=5.1.0
  • GitHub Check: Python==3.11 | django~=5.1.0
  • GitHub Check: Python==3.10 | django~=4.2.0
  • GitHub Check: Python==3.11 | django~=5.2.0
🧰 Additional context used
📓 Path-based instructions (1)
{**/*.sh,**/*.{js,ts,tsx,jsx},**/*.py,**/*.rb,**/*.go,**/*.java,**/*.cs,**/*.cpp,**/*.c}

📄 CodeRabbit inference engine (Custom checks)

Cryptic or non-obvious code (regex, complex bash commands, or hard-to-read code) must include a concise comment explaining why it is needed and why the complexity is acceptable

Files:

  • openwisp_monitoring/tests/test_selenium.py
  • openwisp_monitoring/device/static/monitoring/js/floorplan.js
  • openwisp_monitoring/device/static/monitoring/js/device-map.js
🧠 Learnings (5)
📚 Learning: 2026-02-21T01:03:37.822Z
Learnt from: nemesifier
Repo: openwisp/openwisp-monitoring PR: 738
File: openwisp_monitoring/tests/test_selenium.py:827-859
Timestamp: 2026-02-21T01:03:37.822Z
Learning: In Selenium tests (e.g., in openwisp_monitoring/tests/test_selenium.py and similar test files), when testing JS animations on dashboards or elements driven by JavaScript (such as real-time location updates), insert a short sleep (e.g., sleep(0.3)) before WebDriverWait assertions to allow animations to complete and reduce flakiness. Note: use this as a targeted workaround and prefer explicit waits or animation-end checks where possible to avoid relying on fixed delays.

Applied to files:

  • openwisp_monitoring/tests/test_selenium.py
📚 Learning: 2026-02-21T18:44:28.852Z
Learnt from: dee077
Repo: openwisp/openwisp-monitoring PR: 738
File: openwisp_monitoring/device/api/views.py:263-281
Timestamp: 2026-02-21T18:44:28.852Z
Learning: In openwisp-monitoring, MonitoringIndoorCoordinatesList inherits organization scoping from the parent IndoorCoordinatesList (from openwisp-controller), which uses FilterByParentManaged mixin and filters by location_id in get_queryset(). The child class only overrides the queryset attribute to add monitoring-specific select_related fields; this pattern is safe as long as get_queryset() from the parent is not bypassed. During reviews, verify that MonitoringIndoorCoordinatesList continues to rely on the parent's get_queryset() and that any added select_related fields in the child do not alter the parent's filtering logic.

Applied to files:

  • openwisp_monitoring/tests/test_selenium.py
📚 Learning: 2026-02-25T18:42:08.825Z
Learnt from: dee077
Repo: openwisp/openwisp-monitoring PR: 738
File: openwisp_monitoring/tests/test_selenium.py:309-317
Timestamp: 2026-02-25T18:42:08.825Z
Learning: In Selenium tests that use ChannelsLiveServerTestCase (e.g., openwisp_monitoring/tests/test_selenium.py), override settings to configure CHANNEL_LAYERS with channels_redis.core.RedisChannelLayer instead of InMemoryChannelLayer. This is required because the live server runs in a separate process from the test process, and InMemoryChannelLayer is per-process only and cannot handle cross-process WebSocket broadcasting needed for real-time location update tests.

Applied to files:

  • openwisp_monitoring/tests/test_selenium.py
📚 Learning: 2026-03-14T18:39:04.626Z
Learnt from: UltraBot05
Repo: openwisp/openwisp-monitoring PR: 766
File: openwisp_monitoring/utils.py:59-68
Timestamp: 2026-03-14T18:39:04.626Z
Learning: In this repository (openwisp/openwisp-monitoring), the project targets Python 3.10–3.13 as defined by CI. Do not flag backports.zoneinfo as a missing dependency; zoneinfo is a built-in module in all supported Python versions. When reviewing Python code, assume zoneinfo is available and avoid suggesting installation of backports.zoneinfo. If a file imports zoneinfo or uses it for time zone handling, treat it as standard library usage compatible with the supported CI matrix.

Applied to files:

  • openwisp_monitoring/tests/test_selenium.py
📚 Learning: 2026-03-14T18:39:04.626Z
Learnt from: UltraBot05
Repo: openwisp/openwisp-monitoring PR: 766
File: openwisp_monitoring/utils.py:59-68
Timestamp: 2026-03-14T18:39:04.626Z
Learning: In the openwisp-monitoring project, targets are Linux-based environments. Do not flag a Windows-specific tzdata package as a missing dependency in code reviews for Python files (e.g., openwisp_monitoring/utils.py). If a platform-specific dependency is truly required, document the exception in review guidelines and ensure CI/packaging checks enforce platform constraints rather than manual review.

Applied to files:

  • openwisp_monitoring/tests/test_selenium.py
🔇 Additional comments (4)
openwisp_monitoring/device/static/monitoring/css/device-map.css (1)

192-195: LGTM!

openwisp_monitoring/device/static/monitoring/js/device-map.js (1)

123-147: LGTM!

Also applies to: 158-184

openwisp_monitoring/tests/test_selenium.py (1)

390-440: LGTM!

Also applies to: 475-477

openwisp_monitoring/device/static/monitoring/js/floorplan.js (1)

33-35: LGTM!

Also applies to: 143-146, 299-316, 521-529

Comment thread openwisp_monitoring/tests/test_selenium.py
@openwisp-companion

Copy link
Copy Markdown

Test Failures in test_wifi_client_chart

Hello @dee077,
(Analysis for commit ee83d0d)

The CI build failed due to multiple AssertionError exceptions in the test_wifi_client_chart test cases. These errors indicate that the actual values returned by the API do not match the expected values in the tests.

Specifically, the tests are failing because the last element in the traces data is 0 when the expected values were 3, 1, and 2 in different test scenarios.

To fix this, you need to investigate why the test_wifi_client_chart is returning 0 instead of the expected values and adjust the test or the underlying code to ensure the correct data is being returned.

# Example of a failing assertion:
self.assertEqual(chart["traces"][0][1][-1], 3)
# The error indicates that chart["traces"][0][1][-1] was 0, not 3.

@dee077
dee077 force-pushed the feature/705-improvements-to-dashboard-maps branch 2 times, most recently from b9c6d2a to 5aa2e02 Compare May 31, 2026 14:04

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (1)
openwisp_monitoring/device/static/monitoring/js/floorplan.js (1)

143-168: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Tear down the overlay when the initial floorplan load fails.

openFloorPlan() now mounts the backdrop/overlay before the first request, but if fetchData() rejects the function exits without cleanup. That leaves a blank overlay and active backdrop behind after API errors.

💡 Minimal fix
   setFloorplanState(buildInitialFloorplanState(url, id, floor));
   setupNavigationHandlers();

-    await fetchData(url, floor);
-    const floorplanState = getFloorplanState();
-    if (!floorplanState?.state) return;
-    calculateNavigationState(floorplanState.state.currentFloor);
-
-    addFloorButtons();
-    await showFloor(url, floorplanState.state.currentFloor);
+    try {
+      await fetchData(url, floor);
+      const floorplanState = getFloorplanState();
+      if (!floorplanState?.state) return;
+      calculateNavigationState(floorplanState.state.currentFloor);
+      addFloorButtons();
+      await showFloor(url, floorplanState.state.currentFloor);
+    } catch {
+      destroyFloorplan();
+    }
   }
🤖 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 `@openwisp_monitoring/device/static/monitoring/js/floorplan.js` around lines
143 - 168, openFloorPlan mounts UI elements before calling fetchData, so if
fetchData rejects the overlay/backdrop are left behind; wrap the await
fetchData(url, floor) call in a try/catch (inside openFloorPlan) and on any
error call destroyFloorplan() (or otherwise remove the backdrop/overlay and
clear state via destroyFloorplan) before rethrowing or returning to ensure
cleanup, and also ensure the subsequent early return after getFloorplanState()
failure also triggers destroyFloorplan() so no orphaned UI remains; reference
functions: openFloorPlan, fetchData, destroyFloorplan,
buildInitialFloorplanState, setFloorplanState.
🤖 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.

Inline comments:
In `@openwisp_monitoring/device/static/monitoring/js/device-map.js`:
- Around line 203-205: The cleanup code for XHR callbacks currently clears
activeRequest, loading, and the loading CSS classes unconditionally, which lets
an older aborted request hide the spinner for a newer request; fix by making
each request capture its own XHR reference and only perform cleanup if that
reference still equals the global activeRequest (e.g. store the created XHR in a
local const xhrRef, assign activeRequest = xhrRef, then in success/fail/done
callbacks wrap the logic that clears activeRequest, sets loading=false and
removes loading classes with if (activeRequest === xhrRef) { ... }); also avoid
nulling activeRequest when aborting a previous request so the newer request
remains the authoritative handle.

---

Outside diff comments:
In `@openwisp_monitoring/device/static/monitoring/js/floorplan.js`:
- Around line 143-168: openFloorPlan mounts UI elements before calling
fetchData, so if fetchData rejects the overlay/backdrop are left behind; wrap
the await fetchData(url, floor) call in a try/catch (inside openFloorPlan) and
on any error call destroyFloorplan() (or otherwise remove the backdrop/overlay
and clear state via destroyFloorplan) before rethrowing or returning to ensure
cleanup, and also ensure the subsequent early return after getFloorplanState()
failure also triggers destroyFloorplan() so no orphaned UI remains; reference
functions: openFloorPlan, fetchData, destroyFloorplan,
buildInitialFloorplanState, setFloorplanState.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: bbb3149c-e824-43b7-8ba6-8c286fbdd16c

📥 Commits

Reviewing files that changed from the base of the PR and between ee83d0d and b9c6d2a.

📒 Files selected for processing (4)
  • openwisp_monitoring/device/static/monitoring/css/device-map.css
  • openwisp_monitoring/device/static/monitoring/js/device-map.js
  • openwisp_monitoring/device/static/monitoring/js/floorplan.js
  • openwisp_monitoring/tests/test_selenium.py
📜 Review details
⏰ 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). (11)
  • GitHub Check: Python==3.13 | django~=5.2.0
  • GitHub Check: Python==3.12 | django~=5.2.0
  • GitHub Check: Python==3.10 | django~=5.1.0
  • GitHub Check: Python==3.12 | django~=4.2.0
  • GitHub Check: Python==3.11 | django~=4.2.0
  • GitHub Check: Python==3.10 | django~=4.2.0
  • GitHub Check: Python==3.13 | django~=5.1.0
  • GitHub Check: Python==3.12 | django~=5.1.0
  • GitHub Check: Python==3.11 | django~=5.1.0
  • GitHub Check: Python==3.10 | django~=5.2.0
  • GitHub Check: Python==3.11 | django~=5.2.0
🧰 Additional context used
📓 Path-based instructions (1)
{**/*.sh,**/*.{js,ts,tsx,jsx},**/*.py,**/*.rb,**/*.go,**/*.java,**/*.cs,**/*.cpp,**/*.c}

📄 CodeRabbit inference engine (Custom checks)

Cryptic or non-obvious code (regex, complex bash commands, or hard-to-read code) must include a concise comment explaining why it is needed and why the complexity is acceptable

Files:

  • openwisp_monitoring/device/static/monitoring/js/device-map.js
  • openwisp_monitoring/tests/test_selenium.py
  • openwisp_monitoring/device/static/monitoring/js/floorplan.js
🧠 Learnings (5)
📚 Learning: 2026-02-21T01:03:37.822Z
Learnt from: nemesifier
Repo: openwisp/openwisp-monitoring PR: 738
File: openwisp_monitoring/tests/test_selenium.py:827-859
Timestamp: 2026-02-21T01:03:37.822Z
Learning: In Selenium tests (e.g., in openwisp_monitoring/tests/test_selenium.py and similar test files), when testing JS animations on dashboards or elements driven by JavaScript (such as real-time location updates), insert a short sleep (e.g., sleep(0.3)) before WebDriverWait assertions to allow animations to complete and reduce flakiness. Note: use this as a targeted workaround and prefer explicit waits or animation-end checks where possible to avoid relying on fixed delays.

Applied to files:

  • openwisp_monitoring/tests/test_selenium.py
📚 Learning: 2026-02-21T18:44:28.852Z
Learnt from: dee077
Repo: openwisp/openwisp-monitoring PR: 738
File: openwisp_monitoring/device/api/views.py:263-281
Timestamp: 2026-02-21T18:44:28.852Z
Learning: In openwisp-monitoring, MonitoringIndoorCoordinatesList inherits organization scoping from the parent IndoorCoordinatesList (from openwisp-controller), which uses FilterByParentManaged mixin and filters by location_id in get_queryset(). The child class only overrides the queryset attribute to add monitoring-specific select_related fields; this pattern is safe as long as get_queryset() from the parent is not bypassed. During reviews, verify that MonitoringIndoorCoordinatesList continues to rely on the parent's get_queryset() and that any added select_related fields in the child do not alter the parent's filtering logic.

Applied to files:

  • openwisp_monitoring/tests/test_selenium.py
📚 Learning: 2026-02-25T18:42:08.825Z
Learnt from: dee077
Repo: openwisp/openwisp-monitoring PR: 738
File: openwisp_monitoring/tests/test_selenium.py:309-317
Timestamp: 2026-02-25T18:42:08.825Z
Learning: In Selenium tests that use ChannelsLiveServerTestCase (e.g., openwisp_monitoring/tests/test_selenium.py), override settings to configure CHANNEL_LAYERS with channels_redis.core.RedisChannelLayer instead of InMemoryChannelLayer. This is required because the live server runs in a separate process from the test process, and InMemoryChannelLayer is per-process only and cannot handle cross-process WebSocket broadcasting needed for real-time location update tests.

Applied to files:

  • openwisp_monitoring/tests/test_selenium.py
📚 Learning: 2026-03-14T18:39:04.626Z
Learnt from: UltraBot05
Repo: openwisp/openwisp-monitoring PR: 766
File: openwisp_monitoring/utils.py:59-68
Timestamp: 2026-03-14T18:39:04.626Z
Learning: In this repository (openwisp/openwisp-monitoring), the project targets Python 3.10–3.13 as defined by CI. Do not flag backports.zoneinfo as a missing dependency; zoneinfo is a built-in module in all supported Python versions. When reviewing Python code, assume zoneinfo is available and avoid suggesting installation of backports.zoneinfo. If a file imports zoneinfo or uses it for time zone handling, treat it as standard library usage compatible with the supported CI matrix.

Applied to files:

  • openwisp_monitoring/tests/test_selenium.py
📚 Learning: 2026-03-14T18:39:04.626Z
Learnt from: UltraBot05
Repo: openwisp/openwisp-monitoring PR: 766
File: openwisp_monitoring/utils.py:59-68
Timestamp: 2026-03-14T18:39:04.626Z
Learning: In the openwisp-monitoring project, targets are Linux-based environments. Do not flag a Windows-specific tzdata package as a missing dependency in code reviews for Python files (e.g., openwisp_monitoring/utils.py). If a platform-specific dependency is truly required, document the exception in review guidelines and ensure CI/packaging checks enforce platform constraints rather than manual review.

Applied to files:

  • openwisp_monitoring/tests/test_selenium.py
🔇 Additional comments (1)
openwisp_monitoring/device/static/monitoring/js/floorplan.js (1)

34-35: LGTM!

Also applies to: 299-316, 502-529

Comment thread openwisp_monitoring/device/static/monitoring/js/device-map.js
@openwisp-companion

Copy link
Copy Markdown

Test Failures in OpenWISP Monitoring

Hello @dee077,
(Analysis for commit b9c6d2a)

There are 4 test failures in the CI logs:

  1. test_tolerance failure in test_models.py:
  • Assertion Error: True != False
  • Analysis: The test test_tolerance in test_models.py is asserting that m.is_healthy_tolerant should be False, but it is True. This indicates an issue with how the is_healthy_tolerant status is being determined or updated in the test_tolerance scenario.
  • Fix: Review the logic in test_tolerance that sets m.is_healthy_tolerant and the conditions under which it should be False. Ensure the test accurately reflects the expected behavior or adjust the code to meet the test's expectation.
  1. test_tolerance failure in test_models.py:
  • Assertion Error: 0 != 2
  • Analysis: The test test_tolerance in test_models.py expects 2 Notification objects to be created, but 0 were found. This suggests that the notification creation logic is not being triggered as expected in this specific test scenario.
  • Fix: Examine the test_tolerance test case in test_models.py to understand why notifications are not being created. Check the conditions for notification creation and ensure they are met within the test's setup or the code's execution.
  1. test_flapping_metric_with_tolerance failure in test_monitoring_notifications.py:
  • Assertion Error: 0 != 1
  • Analysis: The test test_flapping_metric_with_tolerance in test_monitoring_notifications.py expects 1 Notification object to be created, but 0 were found. Similar to the previous point, this indicates an issue with the notification creation logic in this specific test.
  • Fix: Review the test_flapping_metric_with_tolerance test in test_monitoring_notifications.py. Ensure that the conditions for creating a notification are correctly met within the test's logic or the code being tested.
  1. test_store_result failure in test_wifi_client.py:
  • Assertion Error: {'wifi_clients_min': 0, 'wifi_clients_max': 3} != {'wifi_clients_min': 3, 'wifi_clients_max': 3}
  • Analysis: The test test_store_result in test_wifi_client.py is asserting that the wifi_clients_min value should be 3, but it is 0. This points to an issue in how the minimum number of WiFi clients is being calculated or stored.
  • Fix: Examine the test_store_result test in test_wifi_client.py and the store_result method it calls. Ensure that the minimum WiFi client count is correctly calculated and stored, or adjust the test's expectation if the current behavior is intended.

@openwisp-companion

Copy link
Copy Markdown

The CI is failing due to transient infrastructure issues (not related to your code). I have restarted the failed jobs automatically (1/3).

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

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

Inline comments:
In `@openwisp_monitoring/device/static/monitoring/js/floorplan.js`:
- Around line 521-529: The current guard in the onReady()/continuation path
checks for missing state, sessionId mismatch, or map change but misses the case
where navigateToFloor() changed the current floor while the previous floor's
decode continued; update the condition so UI sync (hiding
.floorplan-loading-spinner and pushing the fragment) only runs when
getFloorplanState().state.currentFloor === floor. Concretely, within the same
block around getFloorplanState(), add a check referencing
latestState.state.currentFloor and the local floor variable (and keep the
existing checks for _sessionId and maps[floor]) so that if the currentFloor
differs you return early and do not hide the spinner or update the URL fragment.

In `@openwisp_monitoring/device/static/monitoring/js/location-inline.js`:
- Line 16: The code reads window._owMapPageUrl into mapPageUrl without
validation, which can produce broken hrefs like "undefined#..."; update the
logic around the map link generation (where mapPageUrl is used) to first verify
window._owMapPageUrl is a non-empty string (e.g., const mapPageUrl = typeof
window._owMapPageUrl === 'string' && window._owMapPageUrl ? window._owMapPageUrl
: null), then handle the null case by either skipping/disabled link creation or
using a safe default base URL before concatenating hashes so no href contains
"undefined"; ensure all uses of mapPageUrl (the variable) check for
null/validity before building links.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: ae48b85b-0cfc-4018-8a31-45a93a26a9a5

📥 Commits

Reviewing files that changed from the base of the PR and between b9c6d2a and 1a3b6d7.

📒 Files selected for processing (6)
  • openwisp_monitoring/device/static/monitoring/css/device-map.css
  • openwisp_monitoring/device/static/monitoring/js/device-map.js
  • openwisp_monitoring/device/static/monitoring/js/floorplan.js
  • openwisp_monitoring/device/static/monitoring/js/location-inline.js
  • openwisp_monitoring/device/templates/admin/monitoring/device/change_form.html
  • openwisp_monitoring/tests/test_selenium.py
📜 Review details
⏰ 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). (11)
  • GitHub Check: Python==3.13 | django~=5.1.0
  • GitHub Check: Python==3.12 | django~=5.2.0
  • GitHub Check: Python==3.12 | django~=4.2.0
  • GitHub Check: Python==3.12 | django~=5.1.0
  • GitHub Check: Python==3.11 | django~=4.2.0
  • GitHub Check: Python==3.13 | django~=5.2.0
  • GitHub Check: Python==3.10 | django~=5.1.0
  • GitHub Check: Python==3.10 | django~=4.2.0
  • GitHub Check: Python==3.11 | django~=5.2.0
  • GitHub Check: Python==3.11 | django~=5.1.0
  • GitHub Check: Python==3.10 | django~=5.2.0
🧰 Additional context used
📓 Path-based instructions (1)
{**/*.sh,**/*.{js,ts,tsx,jsx},**/*.py,**/*.rb,**/*.go,**/*.java,**/*.cs,**/*.cpp,**/*.c}

📄 CodeRabbit inference engine (Custom checks)

Cryptic or non-obvious code (regex, complex bash commands, or hard-to-read code) must include a concise comment explaining why it is needed and why the complexity is acceptable

Files:

  • openwisp_monitoring/device/static/monitoring/js/location-inline.js
  • openwisp_monitoring/tests/test_selenium.py
  • openwisp_monitoring/device/static/monitoring/js/floorplan.js
  • openwisp_monitoring/device/static/monitoring/js/device-map.js
🧠 Learnings (5)
📚 Learning: 2026-02-21T01:03:37.822Z
Learnt from: nemesifier
Repo: openwisp/openwisp-monitoring PR: 738
File: openwisp_monitoring/tests/test_selenium.py:827-859
Timestamp: 2026-02-21T01:03:37.822Z
Learning: In Selenium tests (e.g., in openwisp_monitoring/tests/test_selenium.py and similar test files), when testing JS animations on dashboards or elements driven by JavaScript (such as real-time location updates), insert a short sleep (e.g., sleep(0.3)) before WebDriverWait assertions to allow animations to complete and reduce flakiness. Note: use this as a targeted workaround and prefer explicit waits or animation-end checks where possible to avoid relying on fixed delays.

Applied to files:

  • openwisp_monitoring/tests/test_selenium.py
📚 Learning: 2026-02-21T18:44:28.852Z
Learnt from: dee077
Repo: openwisp/openwisp-monitoring PR: 738
File: openwisp_monitoring/device/api/views.py:263-281
Timestamp: 2026-02-21T18:44:28.852Z
Learning: In openwisp-monitoring, MonitoringIndoorCoordinatesList inherits organization scoping from the parent IndoorCoordinatesList (from openwisp-controller), which uses FilterByParentManaged mixin and filters by location_id in get_queryset(). The child class only overrides the queryset attribute to add monitoring-specific select_related fields; this pattern is safe as long as get_queryset() from the parent is not bypassed. During reviews, verify that MonitoringIndoorCoordinatesList continues to rely on the parent's get_queryset() and that any added select_related fields in the child do not alter the parent's filtering logic.

Applied to files:

  • openwisp_monitoring/tests/test_selenium.py
📚 Learning: 2026-02-25T18:42:08.825Z
Learnt from: dee077
Repo: openwisp/openwisp-monitoring PR: 738
File: openwisp_monitoring/tests/test_selenium.py:309-317
Timestamp: 2026-02-25T18:42:08.825Z
Learning: In Selenium tests that use ChannelsLiveServerTestCase (e.g., openwisp_monitoring/tests/test_selenium.py), override settings to configure CHANNEL_LAYERS with channels_redis.core.RedisChannelLayer instead of InMemoryChannelLayer. This is required because the live server runs in a separate process from the test process, and InMemoryChannelLayer is per-process only and cannot handle cross-process WebSocket broadcasting needed for real-time location update tests.

Applied to files:

  • openwisp_monitoring/tests/test_selenium.py
📚 Learning: 2026-03-14T18:39:04.626Z
Learnt from: UltraBot05
Repo: openwisp/openwisp-monitoring PR: 766
File: openwisp_monitoring/utils.py:59-68
Timestamp: 2026-03-14T18:39:04.626Z
Learning: In this repository (openwisp/openwisp-monitoring), the project targets Python 3.10–3.13 as defined by CI. Do not flag backports.zoneinfo as a missing dependency; zoneinfo is a built-in module in all supported Python versions. When reviewing Python code, assume zoneinfo is available and avoid suggesting installation of backports.zoneinfo. If a file imports zoneinfo or uses it for time zone handling, treat it as standard library usage compatible with the supported CI matrix.

Applied to files:

  • openwisp_monitoring/tests/test_selenium.py
📚 Learning: 2026-03-14T18:39:04.626Z
Learnt from: UltraBot05
Repo: openwisp/openwisp-monitoring PR: 766
File: openwisp_monitoring/utils.py:59-68
Timestamp: 2026-03-14T18:39:04.626Z
Learning: In the openwisp-monitoring project, targets are Linux-based environments. Do not flag a Windows-specific tzdata package as a missing dependency in code reviews for Python files (e.g., openwisp_monitoring/utils.py). If a platform-specific dependency is truly required, document the exception in review guidelines and ensure CI/packaging checks enforce platform constraints rather than manual review.

Applied to files:

  • openwisp_monitoring/tests/test_selenium.py
🔇 Additional comments (8)
openwisp_monitoring/device/static/monitoring/js/location-inline.js (1)

22-22: LGTM!

Also applies to: 46-46

openwisp_monitoring/device/templates/admin/monitoring/device/change_form.html (2)

2-2: LGTM!


611-616: LGTM!

openwisp_monitoring/device/static/monitoring/js/device-map.js (2)

199-208: Older aborted requests can still clear a newer popup fetch.

After Line 207 aborts the previous jqXHR, the unguarded complete() block at Lines 268-276 still resets activeRequest, loading, and the loading classes for whichever request finishes. If a newer debounced search/filter fetch is pending or already in flight, the older aborted callback can hide the spinner and drop the newer handle. Guard cleanup with a per-fetch token or request identity tied to the latest scheduled fetch.

Also applies to: 248-276


123-147: LGTM!

Also applies to: 158-184, 296-312

openwisp_monitoring/device/static/monitoring/css/device-map.css (1)

192-195: LGTM!

openwisp_monitoring/tests/test_selenium.py (1)

333-352: LGTM!

Also applies to: 392-442, 477-479

openwisp_monitoring/device/static/monitoring/js/floorplan.js (1)

34-35: LGTM!

Also applies to: 143-146, 299-316

Comment thread openwisp_monitoring/device/static/monitoring/js/floorplan.js
Comment thread openwisp_monitoring/device/static/monitoring/js/location-inline.js
@openwisp-companion

Copy link
Copy Markdown

The CI is failing due to transient infrastructure issues (not related to your code). I have restarted the failed jobs automatically (1/3).

@dee077
dee077 force-pushed the feature/705-improvements-to-dashboard-maps branch from 1a3b6d7 to ab7a1a2 Compare May 31, 2026 20:48
Comment thread openwisp_monitoring/device/templates/admin/monitoring/device/change_form.html Outdated
@dee077
dee077 force-pushed the feature/705-improvements-to-dashboard-maps branch from 7c81439 to c1b54f0 Compare June 2, 2026 12:41
@openwisp-companion

Copy link
Copy Markdown

The CI is failing due to transient infrastructure issues (not related to your code). I have restarted the failed jobs automatically (1/3).

dee077 added 4 commits June 4, 2026 05:29
Refactored geo and indoor maps to use the configurable NetJSONGraph nodePopup config, reducing duplicated popup handling logic.

Fixes #795
Refactor floorplan overlay handling, popup management, event listeners, and device list behavior

Fixes #705
@dee077
dee077 force-pushed the feature/705-improvements-to-dashboard-maps branch from d6bd4fc to 55a3615 Compare June 3, 2026 23:59
@openwisp-companion

Copy link
Copy Markdown

The CI is failing due to transient infrastructure issues (not related to your code). I have restarted the failed jobs automatically (1/3).

@openwisp-companion

Copy link
Copy Markdown

The CI is failing due to transient infrastructure issues (not related to your code). I have restarted the failed jobs automatically (1/3).

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

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[chores] Improvements to the dashboard map

3 participants