<%!-- Header --%>

{@current_scope.organization.name}

Network overview

<%= if assigns[:last_updated] do %>
Updated {Calendar.strftime(@last_updated, "%H:%M:%S UTC")}
<% end %>
<%= if !assigns[:summary] do %> <.skeleton_dashboard /> <% end %> <%!-- Empty State --%> <%= if assigns[:summary] && @device_count == 0 do %>
<.icon name="hero-light-bulb" class="mx-auto h-14 w-14 text-blue-500 dark:text-blue-400" />

Let's get started!

Set up your network monitoring in a few steps.

<%= if @current_scope.organization.use_sites do %>
1

Create a Site

Sites represent your physical locations

2

Add Devices

Add routers, switches, and servers to your sites

<% else %>
1

Add Devices

Add network devices to monitor

<% end %>
{if @current_scope.organization.use_sites, do: "3", else: "2"}

Monitor & Receive Alerts

TowerOps automatically monitors your devices and alerts you of issues

<%= if @current_scope.organization.use_sites do %> <.button navigate={~p"/sites/new"} variant="primary"> <.icon name="hero-plus" class="h-5 w-5" /> Create Your First Site <% else %> <.button navigate={~p"/devices/new"} variant="primary"> <.icon name="hero-plus" class="h-5 w-5" /> Add Your First Device <% end %>
<% end %> <%= if assigns[:summary] && @device_count > 0 do %> <%!-- ═══════════════════════════════════════════════ STAT CARDS — max 4 per row, generous sizing ═══════════════════════════════════════════════ --%>
<%!-- Health Score --%>

Health Score

80 -> "bg-green-100 text-green-700 dark:bg-green-900/40 dark:text-green-400" @summary.health_score > 50 -> "bg-yellow-100 text-yellow-700 dark:bg-yellow-900/40 dark:text-yellow-400" true -> "bg-red-100 text-red-700 dark:bg-red-900/40 dark:text-red-400" end ]}> {cond do @summary.health_score > 80 -> "Healthy" @summary.health_score > 50 -> "Degraded" true -> "Critical" end}

{@summary.health_score}

<%!-- Network Uptime --%>

Network Uptime

{if @uptime_percentage == 100.0, do: "100", else: :erlang.float_to_binary(@uptime_percentage, decimals: 1)}%

{@device_up} of {@device_count} devices up

<%!-- Devices --%> <.link navigate={~p"/devices"} class="rounded-xl border border-gray-200 bg-white p-5 transition hover:shadow-md dark:border-white/10 dark:bg-gray-800/50" >

Devices

{@device_count}

{@device_up} up {@device_down} down <%= if @device_unknown > 0 do %> {@device_unknown} <% end %>
<%!-- Active Alerts --%> <.link navigate={~p"/alerts"} class="rounded-xl border border-gray-200 bg-white p-5 transition hover:shadow-md dark:border-white/10 dark:bg-gray-800/50" >

Active Alerts

0, do: "text-red-600 dark:text-red-400", else: "text-gray-900 dark:text-white" ) ]}> {@total_alert_count}

<%= if @total_alert_count == 0 do %> All clear <% else %> View details → <% end %>

<%!-- Secondary stats row (subscribers, MRR, sites, insights) --%>
<%= if @has_subscribers do %>

Subscribers

{format_number(@summary.subscribers.total)}

{format_mrr(@summary.subscribers.total_mrr)}/mo MRR

MRR at Risk

{format_mrr(@impact_summary.mrr_at_risk)}

0} class="mt-1 text-sm text-red-600 dark:text-red-400" > {format_number(@impact_summary.subscribers_affected)} subscribers affected

<% end %> <%= if @current_scope.organization.use_sites do %> <.link navigate={~p"/sites"} class="rounded-xl border border-gray-200 bg-white p-5 transition hover:shadow-md dark:border-white/10 dark:bg-gray-800/50" >

Sites

{@sites_count}

<% end %> <.link navigate={~p"/insights"} class="rounded-xl border border-gray-200 bg-white p-5 transition hover:shadow-md dark:border-white/10 dark:bg-gray-800/50" >

Insights

{@summary.insights.critical + @summary.insights.warning + @summary.insights.info}

0} class="inline-flex items-center rounded-full bg-red-100 px-2 py-0.5 text-xs font-medium text-red-700 dark:bg-red-900/30 dark:text-red-400" > {@summary.insights.critical} critical 0} class="inline-flex items-center rounded-full bg-yellow-100 px-2 py-0.5 text-xs font-medium text-yellow-700 dark:bg-yellow-900/30 dark:text-yellow-400" > {@summary.insights.warning} warning
<%!-- ═══════════════════════════════════════════════ ACTIVE INCIDENTS ═══════════════════════════════════════════════ --%> <%= if @active_incidents != [] do %>

<.icon name="hero-fire" class="h-5 w-5 text-red-500" /> Active Incidents

{length(@active_incidents)}

<.link navigate={~p"/devices/#{incident.device_id}"} class="hover:text-blue-600 hover:underline dark:hover:text-blue-400" > {incident.device_name}

at <.link navigate={~p"/sites/#{incident.site_id}"} class="text-blue-600 hover:text-blue-700 dark:text-blue-400" > {incident.site_name}
<.icon name="hero-clock" class="h-3.5 w-3.5" /> Down {format_duration(incident.duration_seconds)} <%= if incident.subscribers_affected > 0 do %> <.icon name="hero-users" class="h-3.5 w-3.5" /> {format_number(incident.subscribers_affected)} affected <% end %> <%= if mrr_at_risk_positive?(incident.mrr_at_risk) do %> <.icon name="hero-currency-dollar" class="h-3.5 w-3.5" /> {format_mrr(incident.mrr_at_risk)}/mo at risk <% end %> <%= if incident.qoe_score do %> QoE {format_qoe(incident.qoe_score)} <% end %>
<% end %> <%!-- ═══════════════════════════════════════════════ MAIN CONTENT — 2 column (60/40 split) ═══════════════════════════════════════════════ --%>
<%!-- LEFT COLUMN: Alerts + Activity --%>
<%!-- Active Alerts --%>

<.icon name="hero-bell-alert" class="h-5 w-5 text-red-500" /> Active Alerts

<.link navigate={~p"/alerts"} class="text-sm font-medium text-blue-600 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300" > View all →
<%= if @active_alerts == [] do %>
<.icon name="hero-check-circle" class="mx-auto h-10 w-10 text-green-500" />

No active alerts — all systems operational

<% else %>
<.icon name="hero-exclamation-triangle" class="h-3.5 w-3.5" /> Down Acknowledged

<.link navigate={~p"/devices/#{alert.device.id}"} class="hover:text-blue-600 hover:underline dark:hover:text-blue-400" > {alert.device.name}

{alert.message}

<%!-- Gaiia impact --%> <%= if alert.gaiia_impact && alert.gaiia_impact["total_subscribers"] && alert.gaiia_impact["total_subscribers"] > 0 do %>
<.icon name="hero-users" class="h-3 w-3" /> {alert.gaiia_impact["total_subscribers"]} affected <%= if alert.gaiia_impact["total_mrr"] && alert.gaiia_impact["total_mrr"] != "0" do %> <.icon name="hero-currency-dollar" class="h-3 w-3" /> ${alert.gaiia_impact["total_mrr"]}/mo <% end %>
<% end %>
<.timestamp datetime={alert.triggered_at} /> at <.link navigate={~p"/sites/#{alert.device.site.id}"} class="text-blue-600 hover:text-blue-700 dark:text-blue-400" > {alert.device.site.name}
<%= if is_nil(alert.acknowledged_at) && is_nil(alert.resolved_at) do %> <% end %>
<%= if @total_alert_count > 20 do %>
<.link navigate={~p"/alerts"} class="text-sm font-medium text-blue-600 hover:text-blue-700 dark:text-blue-400" > + {@total_alert_count - 20} more alerts →
<% end %> <% end %>
<%!-- Recent Config Changes --%> <%= if Enum.any?(@recent_config_changes) do %>

<.icon name="hero-document-text" class="h-5 w-5 text-orange-500" /> Recent Config Changes

<.link navigate={~p"/insights"} class="text-sm font-medium text-blue-600 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300" > View all →
<%= for change <- @recent_config_changes do %> <.link navigate={~p"/devices/#{change.device_id}/config-timeline"} class="flex items-center gap-4 px-5 py-3.5 hover:bg-gray-50 dark:hover:bg-gray-700/50 transition-colors first:rounded-t-xl last:rounded-b-xl" >

{if change.device, do: change.device.name, else: "Unknown Device"}

{Calendar.strftime(change.changed_at, "%b %d %H:%M")} · {change.change_size} lines · {Enum.join( Enum.take(change.sections_changed, 2), ", " )}

<% end %>
<% end %> <%!-- Recent Activity --%> <%= if assigns[:recent_activity] && @recent_activity != [] do %>

<.icon name="hero-clock" class="h-5 w-5 text-blue-500" /> Recent Activity

<.link navigate={~p"/activity"} class="text-sm font-medium text-blue-600 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300" > View all →

{activity.summary}

{Calendar.strftime(activity.timestamp, "%b %d %H:%M")} <%= if Map.get(activity, :actor_name) do %> by {activity.actor_name} <% end %>

<% end %>
<%!-- RIGHT COLUMN: Insights --%>

<.icon name="hero-light-bulb" class="h-5 w-5 text-yellow-500" /> Insights

<.link navigate={~p"/insights"} class="text-sm font-medium text-blue-600 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300" > View all →
<%!-- Source filter pills --%>
<.link patch={~p"/dashboard"} class={[ "rounded-full px-3 py-1 text-xs font-medium transition", if(is_nil(@insight_source), do: "bg-gray-900 text-white dark:bg-white dark:text-gray-900", else: "text-gray-500 hover:bg-gray-100 dark:text-gray-400 dark:hover:bg-white/10" ) ]} > All <.link :for={source <- ~w(preseem snmp gaiia system)} patch={~p"/dashboard?insight_source=#{source}"} class={[ "rounded-full px-3 py-1 text-xs font-medium transition", if(@insight_source == source, do: "bg-gray-900 text-white dark:bg-white dark:text-gray-900", else: "text-gray-500 hover:bg-gray-100 dark:text-gray-400 dark:hover:bg-white/10" ) ]} > {source}
<%= if @insights == [] do %>
<.icon name="hero-light-bulb" class="mx-auto h-10 w-10 text-gray-300 dark:text-gray-600" />

No active insights

<% else %>
{insight.urgency} {insight.source}

<.link navigate={~p"/insights?source=#{insight.source}"} class="hover:text-blue-600 hover:underline dark:hover:text-blue-400" > {insight.title}

<%= if insight.description do %>

{insight.description}

<% end %>
<% end %>
<%!-- ═══════════════════════════════════════════════ SITE HEALTH GRID ═══════════════════════════════════════════════ --%> <%= if @current_scope.organization.use_sites && @site_impact_summaries != [] do %>

<.icon name="hero-map-pin" class="h-5 w-5 text-blue-500" /> Site Health

<.link navigate={~p"/sites"} class="text-sm font-medium text-blue-600 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300" > View all →
<.link :for={site <- @site_impact_summaries} navigate={~p"/sites/#{site.site_id}"} class={[ "rounded-xl border p-5 transition hover:shadow-md", site_health_border(site.site_health) ]} >

{site.name}

{site.device_count} devices <%= if site.devices_down > 0 do %> {site.devices_down} down <% end %> <%= if site.avg_qoe do %> QoE {format_qoe(site.avg_qoe)} <% end %>
<%= if site.subscribers do %>
<.icon name="hero-users" class="h-3.5 w-3.5" /> {site.subscribers} subscribers <%= if site.mrr do %> {format_mrr(site.mrr)}/mo <% end %>
<% end %> <%= if site.subscribers_affected > 0 do %>
<.icon name="hero-exclamation-triangle" class="h-3.5 w-3.5" /> {format_number(site.subscribers_affected)} affected <%= if mrr_at_risk_positive?(site.mrr_at_risk) do %> {format_mrr(site.mrr_at_risk)}/mo at risk <% end %>
<% end %>
<% end %> <%!-- Quick Actions --%>
<.link navigate={~p"/devices/new"} class="btn btn-outline btn-sm gap-1.5"> <.icon name="hero-plus-circle" class="h-4 w-4" /> Add Device <.link navigate={~p"/alerts"} class="btn btn-outline btn-sm gap-1.5"> <.icon name="hero-bell-alert" class="h-4 w-4" /> View Alerts <.link navigate={~p"/trace"} class="btn btn-outline btn-sm gap-1.5"> <.icon name="hero-signal" class="h-4 w-4" /> Subscriber Trace
<% end %>