diff --git a/lib/towerops_web/live/dashboard_live.ex b/lib/towerops_web/live/dashboard_live.ex index 414e2757..73652400 100644 --- a/lib/towerops_web/live/dashboard_live.ex +++ b/lib/towerops_web/live/dashboard_live.ex @@ -109,7 +109,7 @@ defmodule ToweropsWeb.DashboardLive do defp load_dashboard_data(socket, organization_id) do organization = socket.assigns.current_scope.organization summary = Dashboard.get_dashboard_summary(organization_id) - active_alerts = Alerts.list_organization_active_alerts(organization_id, limit: 20) + active_alerts_all = Alerts.list_organization_active_alerts(organization_id, limit: 20) total_alert_count = Alerts.count_active_alerts(organization_id) sites_count = if organization.use_sites, do: Sites.count_organization_sites(organization_id), else: 0 @@ -129,6 +129,19 @@ defmodule ToweropsWeb.DashboardLive do impact_summary = Dashboard.get_impact_summary(organization_id) active_incidents = Dashboard.list_active_incidents(organization_id) + # Exclude device_down alerts for devices already shown as active incidents + # to avoid showing the same outage twice in the Active Issues section. + incident_device_ids = MapSet.new(active_incidents, & &1.device_id) + + active_alerts = + Enum.reject(active_alerts_all, fn alert -> + alert.alert_type == "device_down" and + MapSet.member?(incident_device_ids, alert.device_id) + end) + + deduped_alert_count = + total_alert_count - (length(active_alerts_all) - length(active_alerts)) + site_impact_summaries = if organization.use_sites do Dashboard.get_site_impact_summaries(organization_id) @@ -154,6 +167,7 @@ defmodule ToweropsWeb.DashboardLive do |> assign(:summary, summary) |> assign(:active_alerts, active_alerts) |> assign(:total_alert_count, total_alert_count) + |> assign(:deduped_alert_count, deduped_alert_count) |> assign(:sites_count, sites_count) |> assign(:device_count, device_count) |> assign(:device_up, device_up) diff --git a/lib/towerops_web/live/dashboard_live.html.heex b/lib/towerops_web/live/dashboard_live.html.heex index 927a7365..3f4c62cf 100644 --- a/lib/towerops_web/live/dashboard_live.html.heex +++ b/lib/towerops_web/live/dashboard_live.html.heex @@ -340,7 +340,7 @@ {t("Active Issues")} - {length(@active_incidents) + @total_alert_count} + {length(@active_incidents) + @deduped_alert_count} diff --git a/test/towerops_web/live/dashboard_live_test.exs b/test/towerops_web/live/dashboard_live_test.exs index 4eec8e7f..130909b4 100644 --- a/test/towerops_web/live/dashboard_live_test.exs +++ b/test/towerops_web/live/dashboard_live_test.exs @@ -128,7 +128,11 @@ defmodule ToweropsWeb.DashboardLiveTest do end describe "alert feed" do - test "shows alerts with device links", %{conn: conn, organization: organization, site: site} do + test "shows down device as incident (not duplicate in alerts table)", %{ + conn: conn, + organization: organization, + site: site + } do {:ok, device} = create_device(organization, site, "Main Router", "10.0.0.1") Towerops.Devices.update_device_status(device, :down) @@ -142,9 +146,11 @@ defmodule ToweropsWeb.DashboardLiveTest do {:ok, _view, html} = live(conn, ~p"/dashboard") + # Device appears in the Active Incidents section assert html =~ "Main Router" - assert html =~ "Device is unreachable" assert html =~ ~p"/devices/#{device.id}" + # device_down alert is filtered from the alerts table to avoid double-counting + refute html =~ "Device is unreachable" end test "shows acknowledge button for unacknowledged alerts", %{