Dashboard: timezone-aware timestamps, site health as table, remove dead code
This commit is contained in:
parent
2cbf6db8de
commit
3049411337
3 changed files with 104 additions and 52 deletions
|
|
@ -181,8 +181,21 @@ defmodule ToweropsWeb.TimeHelpers do
|
|||
end
|
||||
|
||||
@doc """
|
||||
Formats a DateTime into a short date string in UTC.
|
||||
Formats a DateTime into a short "Mon DD HH:MM" string in the given timezone.
|
||||
"""
|
||||
@spec format_short_datetime(DateTime.t() | nil, String.t()) :: String.t()
|
||||
def format_short_datetime(nil, _timezone), do: "—"
|
||||
|
||||
def format_short_datetime(datetime, timezone) do
|
||||
case to_user_timezone(datetime, timezone || "UTC") do
|
||||
{:ok, shifted_dt, _tz_abbr} ->
|
||||
Calendar.strftime(shifted_dt, "%b %d %H:%M")
|
||||
|
||||
{:error, _} ->
|
||||
Calendar.strftime(datetime, "%b %d %H:%M")
|
||||
end
|
||||
end
|
||||
|
||||
@spec format_date(DateTime.t() | nil) :: String.t()
|
||||
def format_date(datetime), do: format_date(datetime, "UTC")
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,9 @@ defmodule ToweropsWeb.DashboardLive do
|
|||
socket
|
||||
|> assign(:page_title, organization.name)
|
||||
|> assign(:device_count, device_count)
|
||||
|> assign(:insight_source, nil)}
|
||||
|> assign(:insight_source, nil)
|
||||
|> assign(:timezone, socket.assigns.current_scope.timezone)
|
||||
|> assign(:time_format, socket.assigns.current_scope.time_format)}
|
||||
end
|
||||
|
||||
@impl true
|
||||
|
|
@ -259,10 +261,6 @@ defmodule ToweropsWeb.DashboardLive do
|
|||
end
|
||||
end
|
||||
|
||||
defp site_health_border(:red), do: "border-red-300 bg-red-50/50 dark:border-red-800 dark:bg-red-950/20"
|
||||
defp site_health_border(:yellow), do: "border-yellow-300 bg-yellow-50/50 dark:border-yellow-800 dark:bg-yellow-950/20"
|
||||
defp site_health_border(_), do: "border-gray-200 bg-white dark:border-white/10 dark:bg-gray-800/50"
|
||||
|
||||
defp site_health_dot(:red), do: "bg-red-500"
|
||||
defp site_health_dot(:yellow), do: "bg-yellow-500"
|
||||
defp site_health_dot(_), do: "bg-green-500"
|
||||
|
|
@ -287,4 +285,8 @@ defmodule ToweropsWeb.DashboardLive do
|
|||
|
||||
defp format_qoe(nil), do: "—"
|
||||
defp format_qoe(score), do: :erlang.float_to_binary(score / 1, decimals: 1)
|
||||
|
||||
defp format_short_datetime(datetime, timezone) do
|
||||
ToweropsWeb.TimeHelpers.format_short_datetime(datetime, timezone)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
<%= if assigns[:last_updated] do %>
|
||||
<div class="flex items-center gap-3 text-xs text-gray-400 dark:text-gray-500">
|
||||
<span>
|
||||
Updated {Calendar.strftime(@last_updated, "%H:%M:%S UTC")}
|
||||
Updated {ToweropsWeb.TimeHelpers.format_datetime(@last_updated, @timezone, @time_format)}
|
||||
</span>
|
||||
<button
|
||||
type="button"
|
||||
|
|
@ -419,7 +419,7 @@
|
|||
<% end %>
|
||||
</td>
|
||||
<td class="px-4 py-2.5 whitespace-nowrap text-xs text-gray-500 dark:text-gray-400">
|
||||
<.timestamp datetime={alert.triggered_at} />
|
||||
<.timestamp datetime={alert.triggered_at} timezone={@timezone} />
|
||||
</td>
|
||||
<td class="px-4 py-2.5 text-right">
|
||||
<%= if is_nil(alert.acknowledged_at) && is_nil(alert.resolved_at) do %>
|
||||
|
|
@ -570,7 +570,7 @@
|
|||
</span>
|
||||
</div>
|
||||
<span class="flex-shrink-0 text-xs text-gray-400 dark:text-gray-500">
|
||||
{Calendar.strftime(change.changed_at, "%b %d %H:%M")}
|
||||
{format_short_datetime(change.changed_at, @timezone)}
|
||||
</span>
|
||||
</.link>
|
||||
<% end %>
|
||||
|
|
@ -592,48 +592,85 @@
|
|||
View all →
|
||||
</.link>
|
||||
</div>
|
||||
<div class="grid gap-3 sm:grid-cols-2 lg:grid-cols-3">
|
||||
<.link
|
||||
:for={site <- @site_impact_summaries}
|
||||
navigate={~p"/sites/#{site.site_id}"}
|
||||
class={[
|
||||
"rounded-lg border px-4 py-3 transition hover:shadow-sm",
|
||||
site_health_border(site.site_health)
|
||||
]}
|
||||
>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class={[
|
||||
"h-2 w-2 rounded-full flex-shrink-0",
|
||||
site_health_dot(site.site_health)
|
||||
]}>
|
||||
</span>
|
||||
<span class="text-sm font-semibold text-gray-900 dark:text-white truncate">
|
||||
{site.name}
|
||||
</span>
|
||||
</div>
|
||||
<div class="mt-1.5 flex items-center gap-3 text-xs text-gray-500 dark:text-gray-400">
|
||||
<span>{site.device_count} devices</span>
|
||||
<%= if site.devices_down > 0 do %>
|
||||
<span class="text-red-600 dark:text-red-400">{site.devices_down} down</span>
|
||||
<% end %>
|
||||
<%= if site.avg_qoe do %>
|
||||
<span class="text-purple-600 dark:text-purple-400">
|
||||
QoE {format_qoe(site.avg_qoe)}
|
||||
</span>
|
||||
<% end %>
|
||||
<%= if site.subscribers do %>
|
||||
<span>{site.subscribers} subs</span>
|
||||
<% end %>
|
||||
</div>
|
||||
<%= if site.subscribers_affected > 0 do %>
|
||||
<div class="mt-1 text-xs text-red-600 dark:text-red-400">
|
||||
{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 %>
|
||||
</div>
|
||||
<% end %>
|
||||
</.link>
|
||||
<div class="overflow-hidden rounded-lg border border-gray-200 dark:border-white/10">
|
||||
<table class="min-w-full divide-y divide-gray-200 dark:divide-white/10">
|
||||
<thead class="bg-gray-50 dark:bg-gray-800/80">
|
||||
<tr>
|
||||
<th class="px-4 py-2 text-left text-xs font-medium uppercase tracking-wide text-gray-500 dark:text-gray-400">
|
||||
Site
|
||||
</th>
|
||||
<th class="px-4 py-2 text-right text-xs font-medium uppercase tracking-wide text-gray-500 dark:text-gray-400">
|
||||
Devices
|
||||
</th>
|
||||
<th class="px-4 py-2 text-right text-xs font-medium uppercase tracking-wide text-gray-500 dark:text-gray-400">
|
||||
Down
|
||||
</th>
|
||||
<th class="px-4 py-2 text-right text-xs font-medium uppercase tracking-wide text-gray-500 dark:text-gray-400">
|
||||
QoE
|
||||
</th>
|
||||
<th class="px-4 py-2 text-right text-xs font-medium uppercase tracking-wide text-gray-500 dark:text-gray-400">
|
||||
Subscribers
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-gray-100 bg-white dark:divide-white/5 dark:bg-gray-800/50">
|
||||
<tr
|
||||
:for={site <- @site_impact_summaries}
|
||||
class="hover:bg-gray-50 dark:hover:bg-white/5"
|
||||
>
|
||||
<td class="px-4 py-2.5">
|
||||
<.link
|
||||
navigate={~p"/sites/#{site.site_id}"}
|
||||
class="flex items-center gap-2 text-sm font-medium text-gray-900 hover:text-blue-600 dark:text-white dark:hover:text-blue-400"
|
||||
>
|
||||
<span class={[
|
||||
"h-2 w-2 rounded-full flex-shrink-0",
|
||||
site_health_dot(site.site_health)
|
||||
]}>
|
||||
</span>
|
||||
{site.name}
|
||||
</.link>
|
||||
</td>
|
||||
<td class="px-4 py-2.5 text-right text-sm text-gray-600 dark:text-gray-400">
|
||||
{site.device_count}
|
||||
</td>
|
||||
<td class="px-4 py-2.5 text-right text-sm">
|
||||
<%= if site.devices_down > 0 do %>
|
||||
<span class="text-red-600 dark:text-red-400">{site.devices_down}</span>
|
||||
<% else %>
|
||||
<span class="text-gray-400 dark:text-gray-500">0</span>
|
||||
<% end %>
|
||||
</td>
|
||||
<td class="px-4 py-2.5 text-right text-sm">
|
||||
<%= if site.avg_qoe do %>
|
||||
<span class={
|
||||
cond do
|
||||
site.avg_qoe < 2.0 -> "text-red-600 dark:text-red-400"
|
||||
site.avg_qoe < 4.0 -> "text-yellow-600 dark:text-yellow-400"
|
||||
true -> "text-green-600 dark:text-green-400"
|
||||
end
|
||||
}>
|
||||
{format_qoe(site.avg_qoe)}
|
||||
</span>
|
||||
<% else %>
|
||||
<span class="text-gray-400 dark:text-gray-500">—</span>
|
||||
<% end %>
|
||||
</td>
|
||||
<td class="px-4 py-2.5 text-right text-sm text-gray-600 dark:text-gray-400">
|
||||
<%= if site.subscribers do %>
|
||||
{site.subscribers}
|
||||
<%= if site.subscribers_affected > 0 do %>
|
||||
<span class="text-red-600 dark:text-red-400">
|
||||
({format_number(site.subscribers_affected)} affected)
|
||||
</span>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<span class="text-gray-400 dark:text-gray-500">—</span>
|
||||
<% end %>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
|
|
@ -663,7 +700,7 @@
|
|||
{activity.summary}
|
||||
</p>
|
||||
<span class="flex-shrink-0 text-xs text-gray-400 dark:text-gray-500">
|
||||
{Calendar.strftime(activity.timestamp, "%b %d %H:%M")}
|
||||
{format_short_datetime(activity.timestamp, @timezone)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue