feat: add status emoji to page titles

Add colored circle emoji (🔴/🟡/🟢) to page titles based on organization
health status:
- 🔴 Red: Critical alerts (device_down, agent_offline, severity 1)
- 🟡 Yellow: Warning alerts (severity 2 or any active alerts)
- 🟢 Green: No active alerts

Provides at-a-glance status visibility in browser tabs.

Note: Favicon remains static stoplight, only page title is dynamic.
This commit is contained in:
Graham McIntire 2026-03-06 14:45:02 -06:00
parent 100245975a
commit 9bef1b289d
No known key found for this signature in database
3 changed files with 79 additions and 1 deletions

View file

@ -7,6 +7,7 @@ defmodule ToweropsWeb.Layouts do
alias ToweropsWeb.Components.ConsentPrompt
alias ToweropsWeb.Components.CookieConsent
alias ToweropsWeb.Helpers.StatusHelpers
alias ToweropsWeb.Live.Components.GlobalSearchComponent
require Logger
@ -1046,4 +1047,18 @@ defmodule ToweropsWeb.Layouts do
</footer>
"""
end
@doc """
Returns the page title with status emoji prepended based on organization health.
"""
def status_title(assigns) do
page_title = assigns[:page_title] || "TowerOps"
organization = get_in(assigns, [:current_scope, Access.key(:organization)])
if organization do
StatusHelpers.title_with_status(page_title, organization)
else
page_title
end
end
end

View file

@ -27,7 +27,7 @@
<link rel="apple-touch-icon" href={~p"/images/towerops_logo.png"} />
<.live_title suffix=" | TowerOps">
{assigns[:page_title] || "TowerOps"}
{status_title(assigns)}
</.live_title>
<link phx-track-static rel="stylesheet" href={~p"/assets/css/app.css"} />
<script defer phx-track-static type="text/javascript" src={~p"/assets/js/app.js"}>

View file

@ -0,0 +1,63 @@
defmodule ToweropsWeb.Helpers.StatusHelpers do
@moduledoc """
Helpers for displaying organization status indicators.
"""
alias Towerops.Alerts
alias Towerops.Organizations.Organization
@doc """
Returns a status emoji based on the organization's active alerts.
## Status Logic
- 🔴 Red circle: Critical alerts (device_down, agent_offline, or severity 1)
- 🟡 Yellow circle: Warning alerts (severity 2 or other active alerts)
- 🟢 Green circle: No active alerts
## Examples
iex> status_emoji(%Organization{id: org_id})
"🔴"
"""
def status_emoji(nil), do: "🟢"
def status_emoji(%Organization{id: organization_id}) do
case get_organization_status(organization_id) do
:critical -> "🔴"
:warning -> "🟡"
:healthy -> "🟢"
end
end
@doc """
Returns a page title with status emoji prepended.
## Examples
iex> title_with_status("Dashboard", %Organization{})
"🔴 Dashboard"
"""
def title_with_status(title, organization) do
emoji = status_emoji(organization)
"#{emoji} #{title}"
end
defp get_organization_status(organization_id) do
alerts = Alerts.list_organization_active_alerts(organization_id)
cond do
has_critical_alerts?(alerts) -> :critical
Enum.any?(alerts) -> :warning
true -> :healthy
end
end
defp has_critical_alerts?(alerts) do
Enum.any?(alerts, fn alert ->
# Device down or agent offline are always critical
# Severity 1 is critical
alert.alert_type in ["device_down", "agent_offline"] or
alert.severity == 1
end)
end
end