UI improvements
This commit is contained in:
parent
2ef85161dd
commit
156d9a47bf
11 changed files with 82 additions and 37 deletions
|
|
@ -820,16 +820,12 @@ defmodule ToweropsWeb.AgentChannel do
|
|||
defp process_sensor_reading(sensor, oid_values, timestamp) do
|
||||
case Map.get(oid_values, sensor.sensor_oid) do
|
||||
nil ->
|
||||
Logger.info("Sensor #{sensor.sensor_descr} (#{sensor.sensor_oid}): OID not in result")
|
||||
:ok
|
||||
|
||||
value ->
|
||||
with parsed_value when not is_nil(parsed_value) <- parse_float(value) do
|
||||
final_value = parsed_value / sensor.sensor_divisor
|
||||
|
||||
Logger.info(
|
||||
"Sensor #{sensor.sensor_descr}: raw=#{inspect(value)}, parsed=#{inspect(parsed_value)}, divisor=#{sensor.sensor_divisor}, final=#{inspect(final_value)}"
|
||||
)
|
||||
|
||||
Snmp.create_sensor_reading(%{
|
||||
sensor_id: sensor.id,
|
||||
value: final_value,
|
||||
|
|
|
|||
|
|
@ -205,7 +205,7 @@ defmodule ToweropsWeb.Layouts do
|
|||
class="hidden md:ml-8 md:flex md:space-x-6"
|
||||
>
|
||||
<.nav_link
|
||||
navigate={~p"/"}
|
||||
navigate={~p"/dashboard"}
|
||||
active={@active_page == "dashboard"}
|
||||
>
|
||||
Dashboard
|
||||
|
|
@ -369,7 +369,7 @@ defmodule ToweropsWeb.Layouts do
|
|||
>
|
||||
<div class="space-y-1 px-2 pb-3 pt-2 border-t border-gray-200 dark:border-white/10">
|
||||
<.mobile_nav_link
|
||||
navigate={~p"/"}
|
||||
navigate={~p"/dashboard"}
|
||||
active={@active_page == "dashboard"}
|
||||
>
|
||||
<.icon name="hero-home" class="size-5" /> Dashboard
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@
|
|||
<.form for={@form} id="device-form" phx-change="validate" phx-submit="save">
|
||||
<div class="divide-y divide-gray-200 dark:divide-white/10">
|
||||
<!-- Basic Information -->
|
||||
<div class="grid max-w-7xl grid-cols-1 gap-x-8 gap-y-10 px-4 py-16 sm:px-6 md:grid-cols-3 lg:px-8">
|
||||
<div class="grid max-w-7xl grid-cols-1 gap-x-8 gap-y-6 px-4 py-8 sm:px-6 md:grid-cols-3 lg:px-8">
|
||||
<div>
|
||||
<h2 class="text-base/7 font-semibold text-gray-900 dark:text-white">
|
||||
Basic Information
|
||||
|
|
@ -108,7 +108,7 @@
|
|||
</div>
|
||||
|
||||
<div class="md:col-span-2">
|
||||
<div class="grid grid-cols-1 gap-x-6 gap-y-8 sm:max-w-xl sm:grid-cols-6">
|
||||
<div class="grid grid-cols-1 gap-x-6 gap-y-4 sm:max-w-xl sm:grid-cols-6">
|
||||
<div class="col-span-full">
|
||||
<.input
|
||||
field={@form[:name]}
|
||||
|
|
@ -186,7 +186,7 @@
|
|||
</div>
|
||||
|
||||
<!-- Monitoring Configuration -->
|
||||
<div class="grid max-w-7xl grid-cols-1 gap-x-8 gap-y-10 px-4 py-16 sm:px-6 md:grid-cols-3 lg:px-8">
|
||||
<div class="grid max-w-7xl grid-cols-1 gap-x-8 gap-y-6 px-4 py-8 sm:px-6 md:grid-cols-3 lg:px-8">
|
||||
<div>
|
||||
<h2 class="text-base/7 font-semibold text-gray-900 dark:text-white">
|
||||
Monitoring Configuration
|
||||
|
|
@ -197,7 +197,7 @@
|
|||
</div>
|
||||
|
||||
<div class="md:col-span-2">
|
||||
<div class="grid grid-cols-1 gap-x-6 gap-y-8 sm:max-w-xl sm:grid-cols-6">
|
||||
<div class="grid grid-cols-1 gap-x-6 gap-y-4 sm:max-w-xl sm:grid-cols-6">
|
||||
<div class="col-span-full">
|
||||
<.input
|
||||
field={@form[:check_interval_seconds]}
|
||||
|
|
@ -276,7 +276,7 @@
|
|||
</div>
|
||||
|
||||
<!-- SNMP Configuration -->
|
||||
<div class="grid max-w-7xl grid-cols-1 gap-x-8 gap-y-10 px-4 py-16 sm:px-6 md:grid-cols-3 lg:px-8">
|
||||
<div class="grid max-w-7xl grid-cols-1 gap-x-8 gap-y-6 px-4 py-8 sm:px-6 md:grid-cols-3 lg:px-8">
|
||||
<div>
|
||||
<h2 class="text-base/7 font-semibold text-gray-900 dark:text-white">
|
||||
SNMP Configuration
|
||||
|
|
@ -337,7 +337,7 @@
|
|||
<!-- SNMP Fields -->
|
||||
<div
|
||||
:if={@monitoring_mode == "snmp_and_icmp"}
|
||||
class="grid grid-cols-1 gap-x-6 gap-y-8 sm:max-w-xl sm:grid-cols-6"
|
||||
class="grid grid-cols-1 gap-x-6 gap-y-4 sm:max-w-xl sm:grid-cols-6"
|
||||
>
|
||||
<div class="col-span-full">
|
||||
<.input
|
||||
|
|
@ -590,7 +590,7 @@
|
|||
<%= if @live_action == :edit and @is_mikrotik_device do %>
|
||||
<!-- MikroTik API Configuration -->
|
||||
<div
|
||||
class="grid max-w-7xl grid-cols-1 gap-x-8 gap-y-10 px-4 py-16 sm:px-6 md:grid-cols-3 lg:px-8"
|
||||
class="grid max-w-7xl grid-cols-1 gap-x-8 gap-y-6 px-4 py-8 sm:px-6 md:grid-cols-3 lg:px-8"
|
||||
phx-hook="MikrotikPortSync"
|
||||
id="mikrotik-config-section"
|
||||
>
|
||||
|
|
@ -621,7 +621,7 @@
|
|||
</div>
|
||||
<% end %>
|
||||
|
||||
<div class="grid grid-cols-1 gap-x-6 gap-y-8 sm:max-w-xl sm:grid-cols-6">
|
||||
<div class="grid grid-cols-1 gap-x-6 gap-y-4 sm:max-w-xl sm:grid-cols-6">
|
||||
<div class="col-span-full">
|
||||
<.input
|
||||
field={@form[:mikrotik_enabled]}
|
||||
|
|
@ -700,7 +700,7 @@
|
|||
|
||||
<!-- Danger Zone -->
|
||||
<%= if @live_action == :edit do %>
|
||||
<div class="grid max-w-7xl grid-cols-1 gap-x-8 gap-y-10 px-4 py-16 sm:px-6 md:grid-cols-3 lg:px-8 border-t border-gray-200 dark:border-white/10">
|
||||
<div class="grid max-w-7xl grid-cols-1 gap-x-8 gap-y-6 px-4 py-8 sm:px-6 md:grid-cols-3 lg:px-8 border-t border-gray-200 dark:border-white/10">
|
||||
<div>
|
||||
<h2 class="text-base/7 font-semibold text-red-600 dark:text-red-500">Danger Zone</h2>
|
||||
<p class="mt-1 text-sm/6 text-gray-500 dark:text-gray-400">
|
||||
|
|
|
|||
|
|
@ -26,7 +26,39 @@
|
|||
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<div>
|
||||
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">{@device.name}</h1>
|
||||
<div class="flex items-center gap-3">
|
||||
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">{@device.name}</h1>
|
||||
<div class={[
|
||||
"flex items-center gap-2 px-3 py-1.5 rounded-lg font-semibold text-sm transition-all",
|
||||
case @device.status do
|
||||
:up ->
|
||||
"bg-green-100 text-green-800 dark:bg-green-950/50 dark:text-green-400 ring-1 ring-green-600/20 dark:ring-green-500/30"
|
||||
|
||||
:down ->
|
||||
"bg-red-100 text-red-800 dark:bg-red-950/50 dark:text-red-400 ring-1 ring-red-600/20 dark:ring-red-500/30"
|
||||
|
||||
:unknown ->
|
||||
"bg-gray-100 text-gray-800 dark:bg-gray-800/50 dark:text-gray-400 ring-1 ring-gray-600/20 dark:ring-gray-500/30"
|
||||
end
|
||||
]}>
|
||||
<span class={[
|
||||
"h-2 w-2 rounded-full animate-pulse",
|
||||
case @device.status do
|
||||
:up -> "bg-green-600 dark:bg-green-500"
|
||||
:down -> "bg-red-600 dark:bg-red-500"
|
||||
:unknown -> "bg-gray-500 dark:bg-gray-400"
|
||||
end
|
||||
]}>
|
||||
</span>
|
||||
<span>
|
||||
{case @device.status do
|
||||
:up -> "Online"
|
||||
:down -> "Offline"
|
||||
:unknown -> "Unknown"
|
||||
end}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-3 mt-0.5">
|
||||
<span class="text-sm text-gray-600 dark:text-gray-400 font-mono">
|
||||
{@device.ip_address}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
<div class="border-b border-gray-200 pb-5 dark:border-white/5">
|
||||
<div class="mb-4">
|
||||
<.link
|
||||
navigate={~p"/orgs/#{@organization.slug}"}
|
||||
navigate={~p"/dashboard"}
|
||||
class="inline-flex items-center gap-1 text-sm text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white"
|
||||
>
|
||||
<.icon name="hero-arrow-left" class="h-4 w-4" /> Back to Dashboard
|
||||
|
|
@ -29,7 +29,7 @@
|
|||
>
|
||||
<div class="divide-y divide-gray-200 dark:divide-white/10">
|
||||
<!-- Organization Name Section -->
|
||||
<div class="grid max-w-7xl grid-cols-1 gap-x-8 gap-y-10 px-4 py-16 sm:px-6 md:grid-cols-3 lg:px-8">
|
||||
<div class="grid max-w-7xl grid-cols-1 gap-x-8 gap-y-6 px-4 py-8 sm:px-6 md:grid-cols-3 lg:px-8">
|
||||
<div>
|
||||
<h2 class="text-base/7 font-semibold text-gray-900 dark:text-white">
|
||||
Organization Name
|
||||
|
|
@ -51,7 +51,7 @@
|
|||
|
||||
<%= if @user_orgs_count > 1 do %>
|
||||
<!-- Default Organization Section -->
|
||||
<div class="grid max-w-7xl grid-cols-1 gap-x-8 gap-y-10 px-4 py-16 sm:px-6 md:grid-cols-3 lg:px-8">
|
||||
<div class="grid max-w-7xl grid-cols-1 gap-x-8 gap-y-6 px-4 py-8 sm:px-6 md:grid-cols-3 lg:px-8">
|
||||
<div>
|
||||
<h2 class="text-base/7 font-semibold text-gray-900 dark:text-white">
|
||||
Default Organization
|
||||
|
|
@ -91,7 +91,7 @@
|
|||
<% end %>
|
||||
|
||||
<!-- Use Sites Section -->
|
||||
<div class="grid max-w-7xl grid-cols-1 gap-x-8 gap-y-10 px-4 py-16 sm:px-6 md:grid-cols-3 lg:px-8">
|
||||
<div class="grid max-w-7xl grid-cols-1 gap-x-8 gap-y-6 px-4 py-8 sm:px-6 md:grid-cols-3 lg:px-8">
|
||||
<div>
|
||||
<h2 class="text-base/7 font-semibold text-gray-900 dark:text-white">
|
||||
Site Organization
|
||||
|
|
@ -135,7 +135,7 @@
|
|||
</div>
|
||||
|
||||
<!-- SNMP Configuration Section -->
|
||||
<div class="grid max-w-7xl grid-cols-1 gap-x-8 gap-y-10 px-4 py-16 sm:px-6 md:grid-cols-3 lg:px-8">
|
||||
<div class="grid max-w-7xl grid-cols-1 gap-x-8 gap-y-6 px-4 py-8 sm:px-6 md:grid-cols-3 lg:px-8">
|
||||
<div>
|
||||
<h2 class="text-base/7 font-semibold text-gray-900 dark:text-white">
|
||||
SNMP Configuration
|
||||
|
|
@ -272,7 +272,7 @@
|
|||
|
||||
<%= if @current_scope.user.is_superuser do %>
|
||||
<!-- MikroTik API Configuration Section -->
|
||||
<div class="grid max-w-7xl grid-cols-1 gap-x-8 gap-y-10 px-4 py-16 sm:px-6 md:grid-cols-3 lg:px-8">
|
||||
<div class="grid max-w-7xl grid-cols-1 gap-x-8 gap-y-6 px-4 py-8 sm:px-6 md:grid-cols-3 lg:px-8">
|
||||
<div>
|
||||
<h2 class="text-base/7 font-semibold text-gray-900 dark:text-white">
|
||||
MikroTik API Configuration
|
||||
|
|
@ -363,7 +363,7 @@
|
|||
|
||||
<!-- Default Agent Section -->
|
||||
<%= if @available_agents != [] do %>
|
||||
<div class="grid max-w-7xl grid-cols-1 gap-x-8 gap-y-10 px-4 py-16 sm:px-6 md:grid-cols-3 lg:px-8">
|
||||
<div class="grid max-w-7xl grid-cols-1 gap-x-8 gap-y-6 px-4 py-8 sm:px-6 md:grid-cols-3 lg:px-8">
|
||||
<div>
|
||||
<h2 class="text-base/7 font-semibold text-gray-900 dark:text-white">
|
||||
Default Agent
|
||||
|
|
@ -441,7 +441,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<% else %>
|
||||
<div class="grid max-w-7xl grid-cols-1 gap-x-8 gap-y-10 px-4 py-16 sm:px-6 md:grid-cols-3 lg:px-8">
|
||||
<div class="grid max-w-7xl grid-cols-1 gap-x-8 gap-y-6 px-4 py-8 sm:px-6 md:grid-cols-3 lg:px-8">
|
||||
<div>
|
||||
<h2 class="text-base/7 font-semibold text-gray-900 dark:text-white">
|
||||
Default Agent
|
||||
|
|
@ -468,7 +468,7 @@
|
|||
<!-- Save Button at Bottom -->
|
||||
<div class="flex items-center justify-end gap-x-6 border-t border-gray-200 px-4 py-4 sm:px-6 lg:px-8 dark:border-white/10">
|
||||
<.link
|
||||
navigate={~p"/orgs/#{@organization.slug}"}
|
||||
navigate={~p"/dashboard"}
|
||||
class="text-sm font-semibold text-gray-900 dark:text-white"
|
||||
>
|
||||
Cancel
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ defmodule ToweropsWeb.OrgLive.Index do
|
|||
@moduledoc false
|
||||
use ToweropsWeb, :live_view
|
||||
|
||||
alias Towerops.Accounts
|
||||
alias Towerops.Organizations
|
||||
|
||||
@impl true
|
||||
|
|
@ -19,4 +20,14 @@ defmodule ToweropsWeb.OrgLive.Index do
|
|||
def handle_params(_params, _url, socket) do
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_event("select_org", %{"org-id" => org_id}, socket) do
|
||||
user = socket.assigns.current_scope.user
|
||||
|
||||
# Update user's default organization
|
||||
{:ok, _user} = Accounts.update_user_profile(user, %{default_organization_id: org_id})
|
||||
|
||||
{:noreply, push_navigate(socket, to: ~p"/dashboard")}
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -12,10 +12,12 @@
|
|||
</.header>
|
||||
|
||||
<div :if={@organizations != []} class="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
|
||||
<.link
|
||||
<button
|
||||
:for={org <- @organizations}
|
||||
navigate={~p"/orgs/#{org.slug}"}
|
||||
class="relative overflow-hidden rounded-lg border border-gray-200 bg-white p-6 shadow-sm hover:shadow-md transition-shadow dark:border-white/10 dark:bg-gray-800/50 cursor-pointer"
|
||||
type="button"
|
||||
phx-click="select_org"
|
||||
phx-value-org-id={org.id}
|
||||
class="relative overflow-hidden rounded-lg border border-gray-200 bg-white p-6 shadow-sm hover:shadow-md transition-shadow dark:border-white/10 dark:bg-gray-800/50 cursor-pointer text-left"
|
||||
>
|
||||
<h2 class="text-xl font-semibold text-gray-900 dark:text-white">{org.name}</h2>
|
||||
<p class="mt-2 text-sm text-gray-600 dark:text-gray-400">
|
||||
|
|
@ -23,7 +25,7 @@
|
|||
|> to_string()
|
||||
|> String.capitalize()}
|
||||
</p>
|
||||
</.link>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div :if={@organizations == []} class="text-center py-16">
|
||||
|
|
|
|||
|
|
@ -39,11 +39,11 @@ defmodule ToweropsWeb.OrgLive.New do
|
|||
opts = if is_superuser, do: [bypass_limits: true], else: []
|
||||
|
||||
case Organizations.create_organization(org_params, user.id, opts) do
|
||||
{:ok, organization} ->
|
||||
{:ok, _organization} ->
|
||||
{:noreply,
|
||||
socket
|
||||
|> put_flash(:info, "Organization created successfully")
|
||||
|> push_navigate(to: ~p"/orgs/#{organization.slug}")}
|
||||
|> push_navigate(to: ~p"/dashboard")}
|
||||
|
||||
{:error, %Ecto.Changeset{} = changeset} ->
|
||||
{:noreply, assign(socket, :form, to_form(changeset))}
|
||||
|
|
|
|||
|
|
@ -293,7 +293,6 @@ defmodule ToweropsWeb.Router do
|
|||
scope "/orgs/:org_slug", ToweropsWeb do
|
||||
pipe_through [:browser, :require_authenticated_user, :load_current_organization]
|
||||
|
||||
live "/", DashboardLive, :index
|
||||
live "/settings", Org.SettingsLive, :index
|
||||
end
|
||||
end
|
||||
|
|
@ -309,6 +308,9 @@ defmodule ToweropsWeb.Router do
|
|||
scope "/", ToweropsWeb do
|
||||
pipe_through [:browser, :require_authenticated_user]
|
||||
|
||||
# Dashboard route
|
||||
live "/dashboard", DashboardLive, :index
|
||||
|
||||
# Alert routes
|
||||
live "/alerts", AlertLive.Index, :index
|
||||
|
||||
|
|
|
|||
|
|
@ -323,8 +323,8 @@ defmodule ToweropsWeb.UserAuth do
|
|||
# Get user's organizations (ordered by most recently joined first)
|
||||
case Towerops.Organizations.list_user_organizations(user.id) do
|
||||
[_first_org | _] ->
|
||||
# Devices page
|
||||
~p"/devices"
|
||||
# Dashboard page
|
||||
~p"/dashboard"
|
||||
|
||||
[] ->
|
||||
# No organizations yet, go to org list
|
||||
|
|
@ -883,10 +883,10 @@ defmodule ToweropsWeb.UserAuth do
|
|||
ip_address: ip
|
||||
})
|
||||
|
||||
# Devices page
|
||||
# Dashboard page
|
||||
redirect_path =
|
||||
case Towerops.Organizations.list_user_organizations(superuser.id) do
|
||||
[_first_org | _] -> ~p"/devices"
|
||||
[_first_org | _] -> ~p"/dashboard"
|
||||
[] -> ~p"/orgs"
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ Devices Tested & Working
|
|||
|
||||
2026-02-05
|
||||
* Fix SNMP v3 polling
|
||||
* UI Simplification
|
||||
* Add device status to device page
|
||||
|
||||
2026-02-04
|
||||
* Feature: SNMP v3 support
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue