- 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
170 lines
7.1 KiB
Text
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 %>
|