diff --git a/assets/js/app.ts b/assets/js/app.ts index 323b417f..74d2cd86 100644 --- a/assets/js/app.ts +++ b/assets/js/app.ts @@ -885,35 +885,50 @@ const NetworkMap = { } } -// Dynamic Favicon - alternates favicon between status indicator SVGs +// Dynamic Favicon - sets favicon color based on data-status from LiveView const DynamicFavicon = { - interval: null as ReturnType | null, - isGreen: true, + statusColors: { green: '#22c55e', yellow: '#eab308', red: '#ef4444' } as Record, + cache: {} as Record, + originalHref: null as string | null, - mounted(this: any) { - const greenSvg = `` - const redSvg = `` - - const greenDataUri = 'data:image/svg+xml,' + encodeURIComponent(greenSvg) - const redDataUri = 'data:image/svg+xml,' + encodeURIComponent(redSvg) + getFaviconPng(this: any, status: string): string { + if (this.cache[status]) return this.cache[status] + const color = this.statusColors[status] || this.statusColors.green + const canvas = document.createElement('canvas') + canvas.width = 64 + canvas.height = 64 + const ctx = canvas.getContext('2d')! + ctx.beginPath() + ctx.arc(32, 32, 32, 0, Math.PI * 2) + ctx.fillStyle = color + ctx.fill() + this.cache[status] = canvas.toDataURL('image/png') + return this.cache[status] + }, + applyStatus(this: any) { const faviconEl = document.getElementById('favicon') as HTMLLinkElement if (!faviconEl) return + const status = this.el.dataset.status || 'green' + faviconEl.type = 'image/png' + faviconEl.href = this.getFaviconPng(status) + }, - this.isGreen = true - faviconEl.type = 'image/svg+xml' - faviconEl.href = greenDataUri + mounted(this: any) { + const faviconEl = document.getElementById('favicon') as HTMLLinkElement + if (faviconEl) this.originalHref = faviconEl.href + this.applyStatus() + }, - this.interval = setInterval(() => { - this.isGreen = !this.isGreen - faviconEl.href = this.isGreen ? greenDataUri : redDataUri - }, 1000) + updated(this: any) { + this.applyStatus() }, destroyed(this: any) { - if (this.interval) { - clearInterval(this.interval) - this.interval = null + const faviconEl = document.getElementById('favicon') as HTMLLinkElement + if (faviconEl && this.originalHref) { + faviconEl.type = 'image/png' + faviconEl.href = this.originalHref } } } diff --git a/lib/towerops_web/controllers/user_session_html/new.html.heex b/lib/towerops_web/controllers/user_session_html/new.html.heex index 9fd0d0ba..2f888bdf 100644 --- a/lib/towerops_web/controllers/user_session_html/new.html.heex +++ b/lib/towerops_web/controllers/user_session_html/new.html.heex @@ -65,7 +65,12 @@ {t_auth("You are running the local mail adapter.")}

- <%= t_auth("To see sent emails, visit %{link}.", link: raw(~s(#{t_auth("the mailbox page")}))) %> + {t_auth("To see sent emails, visit %{link}.", + link: + raw( + ~s(#{t_auth("the mailbox page")}) + ) + )}

diff --git a/lib/towerops_web/live/dashboard_live.ex b/lib/towerops_web/live/dashboard_live.ex index 76c9d32a..da200b8e 100644 --- a/lib/towerops_web/live/dashboard_live.ex +++ b/lib/towerops_web/live/dashboard_live.ex @@ -156,6 +156,7 @@ defmodule ToweropsWeb.DashboardLive do |> assign(:uptime_percentage, uptime_percentage) |> assign(:recent_activity, recent_activity) |> assign(:last_updated, DateTime.utc_now()) + |> assign(:favicon_status, favicon_status(Map.get(status_counts, :down, 0), length(active_alerts))) |> load_insights(organization_id) |> load_recent_config_changes(organization_id) end @@ -185,6 +186,10 @@ defmodule ToweropsWeb.DashboardLive do assign(socket, :insights, insights) end + defp favicon_status(devices_down, _alert_count) when devices_down > 0, do: "red" + defp favicon_status(_devices_down, alert_count) when alert_count > 0, do: "yellow" + defp favicon_status(_devices_down, _alert_count), do: "green" + defp health_score_color(score) when score > 80, do: "text-green-600 dark:text-green-400" defp health_score_color(score) when score > 50, do: "text-yellow-600 dark:text-yellow-400" defp health_score_color(_score), do: "text-red-600 dark:text-red-400" diff --git a/lib/towerops_web/live/dashboard_live.html.heex b/lib/towerops_web/live/dashboard_live.html.heex index 72823402..5f412f52 100644 --- a/lib/towerops_web/live/dashboard_live.html.heex +++ b/lib/towerops_web/live/dashboard_live.html.heex @@ -3,7 +3,8 @@ current_scope={@current_scope} active_page="dashboard" > - + <%!-- ═══════════════════════════════════════════════ NOC HEADER — dense, zero chrome ═══════════════════════════════════════════════ --%>