diff --git a/lib/towerops_web/components/layouts.ex b/lib/towerops_web/components/layouts.ex index 290ae145..84f4a519 100644 --- a/lib/towerops_web/components/layouts.ex +++ b/lib/towerops_web/components/layouts.ex @@ -59,9 +59,9 @@ defmodule ToweropsWeb.Layouts do """ attr :flash, :map, required: true, doc: "the map of flash messages" - attr :current_organization, :map, + attr :current_organization, :any, default: nil, - doc: "the current organization" + doc: "the current organization (can be nil for pages without org context)" attr :active_page, :string, default: nil, diff --git a/lib/towerops_web/live/dashboard_live.html.heex b/lib/towerops_web/live/dashboard_live.html.heex index 76ec6e0a..0d3eb052 100644 --- a/lib/towerops_web/live/dashboard_live.html.heex +++ b/lib/towerops_web/live/dashboard_live.html.heex @@ -8,142 +8,201 @@ <:subtitle>Welcome to {@current_organization.name} -
- <.link - navigate={~p"/orgs/#{@current_organization.slug}/sites"} - class="rounded-lg border border-zinc-200 bg-white p-6 shadow-sm transition-shadow hover:shadow-md dark:border-zinc-800 dark:bg-zinc-900" - > -

Sites

-

{@sites_count}

-

Total sites

- - - <.link - navigate={~p"/orgs/#{@current_organization.slug}/equipment"} - class="rounded-lg border border-zinc-200 bg-white p-6 shadow-sm transition-shadow hover:shadow-md dark:border-zinc-800 dark:bg-zinc-900" - > -

Equipment

-

{@equipment_count}

-
-
- - {@equipment_up} Up -
-
- - {@equipment_down} Down -
-
- - {@equipment_unknown} Unknown + <%= if @sites_count == 0 do %> +
+ <.icon + name="hero-light-bulb" + class="mx-auto h-16 w-16 text-blue-500 dark:text-blue-400" + /> +

+ Let's get started! +

+

+ Follow these steps to set up your network monitoring: +

+
+
+
+
+ 1 +
+
+

Create a Site

+

+ Sites represent your physical locations (offices, data centers, towers, etc.) +

+
+
+
+
+ 2 +
+
+

Add Equipment

+

+ Add network devices (routers, switches, servers) to your sites +

+
+
+
+
+ 3 +
+
+

+ Monitor & Receive Alerts +

+

+ Towerops will automatically monitor your equipment and alert you of issues +

+
+
- - - <.link - navigate={~p"/orgs/#{@current_organization.slug}/alerts"} - class="rounded-lg border border-zinc-200 bg-white p-6 shadow-sm transition-shadow hover:shadow-md dark:border-zinc-800 dark:bg-zinc-900" - > -

Active Alerts

-

0 && "text-red-600 dark:text-red-500") || - "text-zinc-900 dark:text-zinc-100" - ]}> - {length(@active_alerts)} -

-

Requires attention

- - -
-

System Status

-
- <%= if @equipment_down == 0 do %> - <.icon name="hero-check-circle" class="h-5 w-5" /> - All Systems Operational - <% else %> - <.icon name="hero-exclamation-triangle" class="h-5 w-5" /> - {@equipment_down} Equipment Down - <% end %> +
+ <.button navigate={~p"/orgs/#{@current_organization.slug}/sites/new"} variant="primary"> + <.icon name="hero-plus" class="h-5 w-5" /> Create Your First Site +
-
+ <% else %> +
+ <.link + navigate={~p"/orgs/#{@current_organization.slug}/sites"} + class="rounded-lg border border-zinc-200 bg-white p-6 shadow-sm transition-shadow hover:shadow-md dark:border-zinc-800 dark:bg-zinc-900" + > +

Sites

+

{@sites_count}

+

Total sites

+ + + <.link + navigate={~p"/orgs/#{@current_organization.slug}/equipment"} + class="rounded-lg border border-zinc-200 bg-white p-6 shadow-sm transition-shadow hover:shadow-md dark:border-zinc-800 dark:bg-zinc-900" + > +

Equipment

+

{@equipment_count}

+
+
+ + {@equipment_up} Up +
+
+ + {@equipment_down} Down +
+
+ + {@equipment_unknown} Unknown +
+
+ + + <.link + navigate={~p"/orgs/#{@current_organization.slug}/alerts"} + class="rounded-lg border border-zinc-200 bg-white p-6 shadow-sm transition-shadow hover:shadow-md dark:border-zinc-800 dark:bg-zinc-900" + > +

Active Alerts

+

0 && "text-red-600 dark:text-red-500") || + "text-zinc-900 dark:text-zinc-100" + ]}> + {length(@active_alerts)} +

+

Requires attention

+ + +
+

System Status

+
+ <%= if @equipment_down == 0 do %> + <.icon name="hero-check-circle" class="h-5 w-5" /> + All Systems Operational + <% else %> + <.icon name="hero-exclamation-triangle" class="h-5 w-5" /> + {@equipment_down} Equipment Down + <% end %> +
+
+
+ + <%= if length(@active_alerts) > 0 do %> +
+
+

Active Alerts

+ <.link + navigate={~p"/orgs/#{@current_organization.slug}/alerts"} + class="text-sm font-medium text-blue-600 hover:text-blue-700 dark:text-blue-500 dark:hover:text-blue-400" + > + View All Alerts → + +
+ +
+ <%= for alert <- Enum.take(@active_alerts, 5) do %> +
+ <.icon + name="hero-exclamation-triangle" + class="h-5 w-5 flex-shrink-0 text-red-600 dark:text-red-500" + /> +
+

+ <.link + navigate={ + ~p"/orgs/#{@current_organization.slug}/equipment/#{alert.equipment.id}" + } + class="hover:text-blue-600 hover:underline dark:hover:text-blue-400" + > + {alert.equipment.name} + +

+

{alert.message}

+
+ + {Calendar.strftime(alert.triggered_at, "%H:%M")} + +
+ <% end %> + + <%= if length(@active_alerts) > 5 do %> +
+ + {length(@active_alerts) - 5} more alerts +
+ <% end %> +
+
+ <% end %> - <%= if length(@active_alerts) > 0 do %>
-
-

Active Alerts

+

Quick Actions

+
+ <.link + navigate={~p"/orgs/#{@current_organization.slug}/sites"} + class="inline-flex items-center gap-2 rounded-lg bg-blue-600 px-4 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-600 focus:ring-offset-2 dark:bg-blue-500 dark:hover:bg-blue-600" + > + <.icon name="hero-building-office" class="h-5 w-5" /> + Manage Sites + + <.link + navigate={~p"/orgs/#{@current_organization.slug}/equipment"} + class="inline-flex items-center gap-2 rounded-lg bg-blue-600 px-4 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-600 focus:ring-offset-2 dark:bg-blue-500 dark:hover:bg-blue-600" + > + <.icon name="hero-server" class="h-5 w-5" /> + Manage Equipment + <.link navigate={~p"/orgs/#{@current_organization.slug}/alerts"} - class="text-sm font-medium text-blue-600 hover:text-blue-700 dark:text-blue-500 dark:hover:text-blue-400" + class="inline-flex items-center gap-2 rounded-lg border border-zinc-300 bg-white px-4 py-2.5 text-sm font-semibold text-zinc-700 shadow-sm hover:bg-zinc-50 focus:outline-none focus:ring-2 focus:ring-blue-600 focus:ring-offset-2 dark:border-zinc-700 dark:bg-zinc-800 dark:text-zinc-200 dark:hover:bg-zinc-700" > - View All Alerts → + <.icon name="hero-bell" class="h-5 w-5" /> + View Alerts
- -
- <%= for alert <- Enum.take(@active_alerts, 5) do %> -
- <.icon - name="hero-exclamation-triangle" - class="h-5 w-5 flex-shrink-0 text-red-600 dark:text-red-500" - /> -
-

- <.link - navigate={ - ~p"/orgs/#{@current_organization.slug}/equipment/#{alert.equipment.id}" - } - class="hover:text-blue-600 hover:underline dark:hover:text-blue-400" - > - {alert.equipment.name} - -

-

{alert.message}

-
- - {Calendar.strftime(alert.triggered_at, "%H:%M")} - -
- <% end %> - - <%= if length(@active_alerts) > 5 do %> -
- + {length(@active_alerts) - 5} more alerts -
- <% end %> -
<% end %> - -
-

Quick Actions

-
- <.link - navigate={~p"/orgs/#{@current_organization.slug}/sites"} - class="inline-flex items-center gap-2 rounded-lg bg-blue-600 px-4 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-600 focus:ring-offset-2 dark:bg-blue-500 dark:hover:bg-blue-600" - > - <.icon name="hero-building-office" class="h-5 w-5" /> - Manage Sites - - <.link - navigate={~p"/orgs/#{@current_organization.slug}/equipment"} - class="inline-flex items-center gap-2 rounded-lg bg-blue-600 px-4 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-600 focus:ring-offset-2 dark:bg-blue-500 dark:hover:bg-blue-600" - > - <.icon name="hero-server" class="h-5 w-5" /> - Manage Equipment - - <.link - navigate={~p"/orgs/#{@current_organization.slug}/alerts"} - class="inline-flex items-center gap-2 rounded-lg border border-zinc-300 bg-white px-4 py-2.5 text-sm font-semibold text-zinc-700 shadow-sm hover:bg-zinc-50 focus:outline-none focus:ring-2 focus:ring-blue-600 focus:ring-offset-2 dark:border-zinc-700 dark:bg-zinc-800 dark:text-zinc-200 dark:hover:bg-zinc-700" - > - <.icon name="hero-bell" class="h-5 w-5" /> - View Alerts - -
-
diff --git a/lib/towerops_web/live/equipment_live/form.ex b/lib/towerops_web/live/equipment_live/form.ex index f9d525e7..47df5c07 100644 --- a/lib/towerops_web/live/equipment_live/form.ex +++ b/lib/towerops_web/live/equipment_live/form.ex @@ -12,12 +12,20 @@ defmodule ToweropsWeb.EquipmentLive.Form do organization = socket.assigns.current_organization sites = Sites.list_organization_sites(organization.id) - {:ok, - socket - |> assign(:organization, organization) - |> assign(:available_sites, sites) - |> assign(:preselected_site_id, params["site_id"]) - |> assign(:snmp_test_result, nil)} + # Redirect to sites page if no sites exist + if Enum.empty?(sites) do + {:ok, + socket + |> put_flash(:info, "Please create a site before adding equipment.") + |> push_navigate(to: ~p"/orgs/#{organization.slug}/sites/new")} + else + {:ok, + socket + |> assign(:organization, organization) + |> assign(:available_sites, sites) + |> assign(:preselected_site_id, params["site_id"]) + |> assign(:snmp_test_result, nil)} + end end @impl true diff --git a/lib/towerops_web/live/equipment_live/index.ex b/lib/towerops_web/live/equipment_live/index.ex index 4ae77a85..c985b388 100644 --- a/lib/towerops_web/live/equipment_live/index.ex +++ b/lib/towerops_web/live/equipment_live/index.ex @@ -3,16 +3,19 @@ defmodule ToweropsWeb.EquipmentLive.Index do use ToweropsWeb, :live_view alias Towerops.Equipment + alias Towerops.Sites @impl true def mount(_params, _session, socket) do organization = socket.assigns.current_organization equipment = Equipment.list_organization_equipment(organization.id) + sites = Sites.list_organization_sites(organization.id) {:ok, socket |> assign(:page_title, "Equipment") - |> assign(:equipment, equipment)} + |> assign(:equipment, equipment) + |> assign(:has_sites, length(sites) > 0)} end @impl true diff --git a/lib/towerops_web/live/equipment_live/index.html.heex b/lib/towerops_web/live/equipment_live/index.html.heex index 62ed9e46..66c6e5bb 100644 --- a/lib/towerops_web/live/equipment_live/index.html.heex +++ b/lib/towerops_web/live/equipment_live/index.html.heex @@ -7,72 +7,100 @@ {@page_title} <:subtitle>Monitor and manage all your network equipment <:actions> - <.button navigate={~p"/orgs/#{@current_organization.slug}/equipment/new"} variant="primary"> + <.button + :if={@has_sites} + navigate={~p"/orgs/#{@current_organization.slug}/equipment/new"} + variant="primary" + > <.icon name="hero-plus" class="h-5 w-5" /> New Equipment - <%= if @equipment == [] do %> + <%= if !@has_sites do %>
- <.icon name="hero-server" class="mx-auto h-12 w-12 text-zinc-400 dark:text-zinc-600" /> -

No equipment

+ <.icon + name="hero-building-office" + class="mx-auto h-12 w-12 text-zinc-400 dark:text-zinc-600" + /> +

+ Create a site first +

- Get started by adding your first equipment. + Before adding equipment, you need to create at least one site location. +

+

+ Sites help you organize your equipment by physical location.

- <.button - navigate={~p"/orgs/#{@current_organization.slug}/equipment/new"} - variant="primary" - > - <.icon name="hero-plus" class="h-5 w-5" /> New Equipment + <.button navigate={~p"/orgs/#{@current_organization.slug}/sites/new"} variant="primary"> + <.icon name="hero-plus" class="h-5 w-5" /> Create Your First Site
<% else %> - <.table id="equipment" rows={@equipment}> - <:col :let={eq} label="Name"> - {eq.name} - - <:col :let={eq} label="IP Address"> - {eq.ip_address} - - <:col :let={eq} label="Site"> - <.link - navigate={~p"/orgs/#{@current_organization.slug}/sites/#{eq.site.id}"} - class="text-blue-600 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300" - > - {eq.site.name} - - - <:col :let={eq} label="Status"> - - {eq.status |> to_string() |> String.upcase()} - - - <:col :let={eq} label="Last Checked"> - - <%= if eq.last_checked_at do %> - {Calendar.strftime(eq.last_checked_at, "%Y-%m-%d %H:%M")} - <% else %> - Never - <% end %> - - - <:action :let={eq}> - <.link - navigate={~p"/orgs/#{@current_organization.slug}/equipment/#{eq.id}"} - class="text-blue-600 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300" - > - View - - - + <%= if @equipment == [] do %> +
+ <.icon name="hero-server" class="mx-auto h-12 w-12 text-zinc-400 dark:text-zinc-600" /> +

No equipment

+

+ Get started by adding your first equipment. +

+
+ <.button + navigate={~p"/orgs/#{@current_organization.slug}/equipment/new"} + variant="primary" + > + <.icon name="hero-plus" class="h-5 w-5" /> New Equipment + +
+
+ <% else %> + <.table id="equipment" rows={@equipment}> + <:col :let={eq} label="Name"> + {eq.name} + + <:col :let={eq} label="IP Address"> + {eq.ip_address} + + <:col :let={eq} label="Site"> + <.link + navigate={~p"/orgs/#{@current_organization.slug}/sites/#{eq.site.id}"} + class="text-blue-600 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300" + > + {eq.site.name} + + + <:col :let={eq} label="Status"> + + {eq.status |> to_string() |> String.upcase()} + + + <:col :let={eq} label="Last Checked"> + + <%= if eq.last_checked_at do %> + {Calendar.strftime(eq.last_checked_at, "%Y-%m-%d %H:%M")} + <% else %> + Never + <% end %> + + + <:action :let={eq}> + <.link + navigate={~p"/orgs/#{@current_organization.slug}/equipment/#{eq.id}"} + class="text-blue-600 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300" + > + View + + + + <% end %> <% end %>