towerops/lib/towerops_web/live/status_page_live.html.heex
Graham McIntire 7c8731f86a fix: make sidebar sticky with header and resolve all credo issues (#32)
- Changed sidebar from fixed to sticky positioning
- Wrapped sidebar and main content in flex container
- Sidebar now sticks to top along with header when scrolling
- Removed unused Ecto.Query import from gps_sync.ex

Credo improvements (17 → 0 remaining):
- Replaced all length/1 checks with empty list comparisons
- Extracted helper functions to reduce nesting depth
- Split complex functions into smaller, testable units
- Applied 'with' statements for clearer control flow
- Refactored high-complexity functions (cyclomatic complexity)
- Files improved: storm_detector, alert_notification_worker,
  alert_digest_worker, gps_sync, statistics_sync, device_sync,
  site_sync, site_correlation, reports_live, topology,
  pagerduty/client, cn_maestro/sync

Reviewed-on: graham/towerops-web#32
2026-03-15 18:19:09 -05:00

170 lines
7.1 KiB
Text

<%= if @not_found do %>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Status Page Not Found</title>
</head>
<body class="bg-gray-50 flex items-center justify-center min-h-screen">
<div class="text-center">
<h1 class="text-2xl font-bold text-gray-900">Status Page Not Found</h1>
<p class="mt-2 text-gray-600">
The requested status page does not exist or is not enabled.
</p>
</div>
</body>
</html>
<% else %>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>{@page_title}</title>
<link phx-track-static rel="stylesheet" href={~p"/assets/app.css"} />
<script defer phx-track-static src={~p"/assets/app.js"}>
</script>
<%= if @config.custom_css do %>
<style>
{@config.custom_css}
</style>
<% end %>
</head>
<body class="bg-gray-50 dark:bg-gray-950 min-h-screen">
<div class="max-w-3xl mx-auto px-4 py-8">
<%!-- Header --%>
<header class="text-center mb-8">
<%= if @config.logo_url do %>
<img src={@config.logo_url} alt={@config.company_name} class="h-10 mx-auto mb-4" />
<% end %>
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">
{@config.company_name || "System Status"}
</h1>
</header>
<%!-- Overall Status Banner --%>
<div class={"rounded-lg p-4 mb-8 text-white text-center #{overall_color(@overall_status)}"}>
<p class="text-lg font-semibold">{overall_text(@overall_status)}</p>
<p class="text-sm opacity-90 mt-1">
Last updated: {Calendar.strftime(DateTime.utc_now(), "%B %d, %Y at %H:%M UTC")}
</p>
</div>
<%!-- Active Incidents --%>
<%= if @active_incidents != [] do %>
<div class="mb-8">
<h2 class="text-lg font-semibold text-gray-900 dark:text-white mb-4">
Active Incidents
</h2>
<div class="space-y-4">
<%= for incident <- @active_incidents do %>
<div class="bg-white dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700 p-4">
<div class="flex items-start justify-between">
<div>
<h3 class={"font-semibold #{severity_color(incident.severity)}"}>
{incident.title}
</h3>
<%= if incident.body do %>
<p class="mt-1 text-sm text-gray-600 dark:text-gray-400">
{incident.body}
</p>
<% end %>
</div>
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-yellow-100 text-yellow-800 dark:bg-yellow-900/50 dark:text-yellow-300">
{incident_status_label(incident.status)}
</span>
</div>
<p class="mt-2 text-xs text-gray-500">
Started: {Calendar.strftime(incident.started_at, "%b %d, %H:%M UTC")}
</p>
</div>
<% end %>
</div>
</div>
<% end %>
<%!-- Components --%>
<div class="mb-8">
<h2 class="text-lg font-semibold text-gray-900 dark:text-white mb-4">Components</h2>
<div class="bg-white dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700 divide-y divide-gray-200 dark:divide-gray-700">
<%= if @components == [] do %>
<div class="p-4 text-center text-gray-500 dark:text-gray-400">
No components configured
</div>
<% else %>
<%= for component <- @components do %>
<div class="px-4 py-3 flex items-center justify-between">
<div>
<span class="font-medium text-gray-900 dark:text-white">
{component.name}
</span>
<%= if component.description do %>
<p class="text-xs text-gray-500 dark:text-gray-400">
{component.description}
</p>
<% end %>
</div>
<div class="flex items-center gap-2">
<div class={"w-2.5 h-2.5 rounded-full #{component_color(component.status)}"}>
</div>
<span class="text-sm text-gray-600 dark:text-gray-400">
{component_label(component.status)}
</span>
</div>
</div>
<% end %>
<% end %>
</div>
</div>
<%!-- Recent Incidents --%>
<div class="mb-8">
<h2 class="text-lg font-semibold text-gray-900 dark:text-white mb-4">
Recent Incidents
</h2>
<%= if @recent_incidents == [] do %>
<p class="text-sm text-gray-500 dark:text-gray-400 bg-white dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700 p-4 text-center">
No recent incidents — looking good!
</p>
<% else %>
<div class="space-y-3">
<%= for incident <- @recent_incidents do %>
<div class="bg-white dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700 p-4">
<div class="flex items-center justify-between">
<h3 class="text-sm font-medium text-gray-900 dark:text-white">
{incident.title}
</h3>
<span class={"text-xs px-2 py-0.5 rounded font-medium " <> if(incident.status == "resolved", do: "bg-green-100 text-green-700 dark:bg-green-900/50 dark:text-green-300", else: "bg-yellow-100 text-yellow-700 dark:bg-yellow-900/50 dark:text-yellow-300")}>
{incident_status_label(incident.status)}
</span>
</div>
<p class="mt-1 text-xs text-gray-500">
{Calendar.strftime(incident.started_at, "%b %d, %Y")}
<%= if incident.resolved_at do %>
— Resolved {Calendar.strftime(incident.resolved_at, "%b %d, %H:%M UTC")}
<% end %>
</p>
</div>
<% end %>
</div>
<% end %>
</div>
<%!-- Footer --%>
<footer class="text-center text-xs text-gray-400 dark:text-gray-600 mt-12">
<%= if @config.support_email do %>
<p>
Need help? Contact
<a
href={"mailto:#{@config.support_email}"}
class="underline hover:text-gray-600 dark:hover:text-gray-400"
>
{@config.support_email}
</a>
</p>
<% end %>
<p class="mt-1">Powered by Towerops</p>
</footer>
</div>
</body>
</html>
<% end %>