feat: add polished empty states across all pages

This commit is contained in:
Graham McIntire 2026-02-13 19:11:54 -06:00
parent b7204b604d
commit 314ed610d3
4 changed files with 70 additions and 51 deletions

View file

@ -67,21 +67,27 @@
</div>
<%= if Enum.empty?(@alerts) do %>
<div class="text-center py-16">
<.icon name="hero-bell-slash" class="mx-auto h-12 w-12 text-gray-400 dark:text-gray-500" />
<h3 class="mt-4 text-lg font-semibold text-gray-900 dark:text-white">No alerts</h3>
<p class="mt-2 text-sm text-gray-600 dark:text-gray-400">
<%= case @filter do %>
<% "unresolved" -> %>
All clear — no unresolved alerts.
<% "critical" -> %>
No critical alerts. Nice work!
<% "resolved" -> %>
No resolved alerts yet.
<% _ -> %>
No alerts have been triggered yet.
<% end %>
</p>
<div class="flex items-center justify-center py-16">
<div class="card bg-base-100 shadow-md border border-green-200 dark:border-green-800 max-w-md w-full">
<div class="card-body items-center text-center">
<div class="rounded-full bg-green-100 dark:bg-green-900/40 p-4 mb-2">
<.icon name="hero-check-circle-solid" class="h-12 w-12 text-green-500 dark:text-green-400" />
</div>
<h3 class="card-title text-green-700 dark:text-green-400">All Clear!</h3>
<p class="text-sm text-gray-600 dark:text-gray-400">
<%= case @filter do %>
<% "unresolved" -> %>
No active alerts. Your network is looking good!
<% "critical" -> %>
No critical alerts. Nice work keeping things healthy!
<% "resolved" -> %>
No resolved alerts yet.
<% _ -> %>
No active alerts. Your network is looking good!
<% end %>
</p>
</div>
</div>
</div>
<% else %>
<%!-- Grouped by Site --%>

View file

@ -2,6 +2,7 @@ defmodule ToweropsWeb.DashboardLive do
@moduledoc false
use ToweropsWeb, :live_view
alias Towerops.ActivityFeed
alias Towerops.Alerts
alias Towerops.Dashboard
alias Towerops.Devices
@ -40,6 +41,12 @@ defmodule ToweropsWeb.DashboardLive do
|> load_dashboard_data(organization.id)}
end
@impl true
def handle_event("refresh_dashboard", _params, socket) do
organization = socket.assigns.current_scope.organization
{:noreply, load_dashboard_data(socket, organization.id)}
end
@impl true
def handle_event("dismiss_insight", %{"id" => insight_id}, socket) do
case Preseem.dismiss_insight(insight_id) do

View file

@ -142,29 +142,32 @@
<%!-- Insights list --%>
<div class="mt-6" id="insights-list">
<%= if @insights == [] do %>
<div
id="empty-state"
class="rounded-lg border-2 border-dashed border-gray-300 p-12 text-center dark:border-gray-700"
>
<.icon
name="hero-light-bulb"
class="mx-auto h-12 w-12 text-gray-400 dark:text-gray-500"
/>
<h3 class="mt-2 text-sm font-semibold text-gray-900 dark:text-white">
No insights found
</h3>
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">
<%= cond do %>
<% @filter_status == "dismissed" -> %>
No dismissed insights.
<% @filter_source != nil -> %>
No {@filter_source} insights right now.
<% @filter_urgency != nil -> %>
No {@filter_urgency} insights right now.
<% true -> %>
No active insights. Your network is looking healthy!
<% end %>
</p>
<div id="empty-state" class="flex items-center justify-center py-8">
<div class="card bg-base-100 shadow-md border border-base-200 dark:border-white/10 max-w-md w-full">
<div class="card-body items-center text-center">
<div class="rounded-full bg-amber-100 dark:bg-amber-900/40 p-4 mb-2">
<.icon name="hero-light-bulb-solid" class="h-12 w-12 text-amber-500 dark:text-amber-400" />
</div>
<h3 class="card-title text-gray-900 dark:text-white">No Insights Yet</h3>
<p class="text-sm text-gray-600 dark:text-gray-400">
<%= cond do %>
<% @filter_status == "dismissed" -> %>
No dismissed insights.
<% @filter_source != nil -> %>
No {@filter_source} insights right now.
<% @filter_urgency != nil -> %>
No {@filter_urgency} insights right now.
<% true -> %>
Connect Preseem or enable SNMP monitoring to get started.
<% end %>
</p>
<div class="card-actions mt-2">
<.link navigate={~p"/settings"} class="btn btn-primary btn-sm">
<.icon name="hero-cog-6-tooth" class="h-4 w-4" /> Connect Integration
</.link>
</div>
</div>
</div>
</div>
<% else %>
<%!-- Select all bar --%>

View file

@ -14,19 +14,22 @@
</.header>
<%= if @sites == [] do %>
<div class="text-center py-16">
<.icon
name="hero-building-office"
class="mx-auto h-12 w-12 text-gray-400 dark:text-gray-500"
/>
<h3 class="mt-4 text-lg font-semibold text-gray-900 dark:text-white">No sites</h3>
<p class="mt-2 text-sm text-gray-600 dark:text-gray-400">
Get started by creating your first site.
</p>
<div class="mt-6">
<.button navigate={~p"/sites/new"} variant="primary">
<.icon name="hero-plus" class="h-5 w-5" /> New Site
</.button>
<div class="flex items-center justify-center py-16">
<div class="card bg-base-100 shadow-md border border-base-200 dark:border-white/10 max-w-md w-full">
<div class="card-body items-center text-center">
<div class="rounded-full bg-blue-100 dark:bg-blue-900/40 p-4 mb-2">
<.icon name="hero-map-pin-solid" class="h-12 w-12 text-blue-500 dark:text-blue-400" />
</div>
<h3 class="card-title text-gray-900 dark:text-white">No Sites Yet</h3>
<p class="text-sm text-gray-600 dark:text-gray-400">
Create your first site to organize devices by tower location.
</p>
<div class="card-actions mt-2">
<.button navigate={~p"/sites/new"} variant="primary">
<.icon name="hero-plus" class="h-4 w-4" /> Add Site
</.button>
</div>
</div>
</div>
</div>
<% else %>