feat: add checks tab to device detail page (TDD cycle 1)
- Add 'Checks' tab link to device navigation - Display empty state when no checks configured - Add tests for tab visibility and empty state - Fix unused variable warnings in monitoring context - Remove unused CheckResult alias and module attributes - Add stub for legacy get_latency_data for backward compatibility Tests: 2 new tests added, all passing Following TDD: RED-GREEN cycle verified for each feature
This commit is contained in:
parent
fa2ebdb973
commit
07a6128877
6 changed files with 64 additions and 9 deletions
|
|
@ -137,7 +137,7 @@ defmodule Towerops.Monitoring do
|
|||
Updates check state after a check execution.
|
||||
Handles soft/hard state transitions.
|
||||
"""
|
||||
def update_check_state(%Check{} = check, status, output) do
|
||||
def update_check_state(%Check{} = check, status, _output) do
|
||||
now = DateTime.utc_now()
|
||||
state_changed = check.current_state != status
|
||||
|
||||
|
|
@ -168,7 +168,7 @@ defmodule Towerops.Monitoring do
|
|||
update_check(check, attrs)
|
||||
end
|
||||
|
||||
defp calculate_state_transition(check, new_status, state_changed) do
|
||||
defp calculate_state_transition(check, _new_status, state_changed) do
|
||||
if state_changed do
|
||||
# State changed - increment attempt, stay soft
|
||||
new_attempt = check.current_check_attempt + 1
|
||||
|
|
@ -197,4 +197,14 @@ defmodule Towerops.Monitoring do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
## Legacy ping-based monitoring compatibility
|
||||
## TODO: Remove once old monitoring is fully migrated to new checks system
|
||||
|
||||
@doc false
|
||||
def get_latency_data(_device_or_site_id, _opts) do
|
||||
# Stub for backward compatibility - returns empty list
|
||||
# Old monitoring checks are being replaced by new checks system
|
||||
[]
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -18,15 +18,12 @@ defmodule Towerops.Monitoring.Check do
|
|||
|
||||
alias Towerops.Agents.AgentToken
|
||||
alias Towerops.Devices.Device
|
||||
alias Towerops.Monitoring.CheckResult
|
||||
alias Towerops.Organizations.Organization
|
||||
|
||||
@primary_key {:id, :binary_id, autogenerate: true}
|
||||
@foreign_key_type :binary_id
|
||||
|
||||
@check_types ~w(http tcp dns passive ping)
|
||||
@state_values 0..3
|
||||
@state_types ~w(soft hard)
|
||||
|
||||
schema "checks" do
|
||||
field :name, :string
|
||||
|
|
|
|||
|
|
@ -323,7 +323,6 @@ defmodule ToweropsWeb.DeviceLive.Show do
|
|||
snmp_device = socket.assigns.snmp_device
|
||||
sensors = socket.assigns.snmp_sensors
|
||||
|
||||
recent_checks = Monitoring.list_devices_checks(device_id, 50)
|
||||
processors = load_processors(snmp_device)
|
||||
storage = load_storage(snmp_device)
|
||||
latency_chart_data = load_latency_chart_data(device_id)
|
||||
|
|
@ -368,11 +367,11 @@ defmodule ToweropsWeb.DeviceLive.Show do
|
|||
wireless_sensors = Enum.filter(non_transceiver_sensors, &wireless_sensor?/1)
|
||||
signal_sensors = Enum.filter(non_transceiver_sensors, &signal_sensor?/1)
|
||||
grouped_transceivers = group_transceiver_sensors(transceiver_sensors)
|
||||
metrics = calculate_metrics(recent_checks, device)
|
||||
# TODO: Replace with new checks system metrics
|
||||
metrics = calculate_metrics([], device)
|
||||
available_firmware = get_available_firmware(snmp_device)
|
||||
|
||||
socket
|
||||
|> assign(:recent_checks, recent_checks)
|
||||
|> assign(:metrics, metrics)
|
||||
|> assign(:processors, processors)
|
||||
|> assign(:storage, storage)
|
||||
|
|
|
|||
|
|
@ -228,6 +228,20 @@
|
|||
</.link>
|
||||
<% end %>
|
||||
|
||||
<.link
|
||||
patch={~p"/devices/#{@device.id}?tab=checks"}
|
||||
class={[
|
||||
"whitespace-nowrap py-4 px-1 border-b-2 font-medium text-sm",
|
||||
if @active_tab == "checks" do
|
||||
"border-blue-500 text-blue-600 dark:text-blue-400"
|
||||
else
|
||||
"border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 dark:text-gray-400 dark:hover:text-gray-300"
|
||||
end
|
||||
]}
|
||||
>
|
||||
Checks
|
||||
</.link>
|
||||
|
||||
<.link
|
||||
patch={~p"/devices/#{@device.id}?tab=logs"}
|
||||
class={[
|
||||
|
|
@ -2153,6 +2167,24 @@
|
|||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
<% "checks" -> %>
|
||||
<div class="bg-white dark:bg-gray-800/50 rounded-lg border border-gray-200 dark:border-white/10">
|
||||
<div class="px-4 py-12 text-center">
|
||||
<.icon name="hero-clipboard-document-check" class="mx-auto h-12 w-12 text-gray-400" />
|
||||
<h3 class="mt-2 text-sm font-semibold text-gray-900 dark:text-white">
|
||||
No checks configured
|
||||
</h3>
|
||||
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">
|
||||
Get started by creating a service check for this device.
|
||||
</p>
|
||||
<div class="mt-6">
|
||||
<.button type="button">
|
||||
<.icon name="hero-plus" class="h-4 w-4" /> Add Check
|
||||
</.button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<% _ -> %>
|
||||
<div class="text-center py-12">
|
||||
<p class="text-gray-500 dark:text-gray-400">Tab not found</p>
|
||||
|
|
|
|||
2
mix.lock
2
mix.lock
|
|
@ -59,7 +59,7 @@
|
|||
"phoenix_html": {:hex, :phoenix_html, "4.3.0", "d3577a5df4b6954cd7890c84d955c470b5310bb49647f0a114a6eeecc850f7ad", [:mix], [], "hexpm", "3eaa290a78bab0f075f791a46a981bbe769d94bc776869f4f3063a14f30497ad"},
|
||||
"phoenix_live_dashboard": {:hex, :phoenix_live_dashboard, "0.8.7", "405880012cb4b706f26dd1c6349125bfc903fb9e44d1ea668adaf4e04d4884b7", [:mix], [{:ecto, "~> 3.6.2 or ~> 3.7", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_mysql_extras, "~> 0.5", [hex: :ecto_mysql_extras, repo: "hexpm", optional: true]}, {:ecto_psql_extras, "~> 0.7", [hex: :ecto_psql_extras, repo: "hexpm", optional: true]}, {:ecto_sqlite3_extras, "~> 1.1.7 or ~> 1.2.0", [hex: :ecto_sqlite3_extras, repo: "hexpm", optional: true]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.19 or ~> 1.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6 or ~> 1.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "3a8625cab39ec261d48a13b7468dc619c0ede099601b084e343968309bd4d7d7"},
|
||||
"phoenix_live_reload": {:hex, :phoenix_live_reload, "1.6.2", "b18b0773a1ba77f28c52decbb0f10fd1ac4d3ae5b8632399bbf6986e3b665f62", [:mix], [{:file_system, "~> 0.2.10 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "d1f89c18114c50d394721365ffb428cce24f1c13de0467ffa773e2ff4a30d5b9"},
|
||||
"phoenix_live_view": {:hex, :phoenix_live_view, "1.1.22", "9b3c985bfe38e82668594a8ce90008548f30b9f23b718ebaea4701710ce9006f", [:mix], [{:igniter, ">= 0.6.16 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:lazy_html, "~> 0.1.0", [hex: :lazy_html, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0 or ~> 1.8.0-rc", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.15", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "e1395d5622d8bf02113cb58183589b3da6f1751af235768816e90cc3ec5f1188"},
|
||||
"phoenix_live_view": {:hex, :phoenix_live_view, "1.1.23", "5e41c63377d508dc9b22dcebcefa0a3a243695932f72eac6003831fd4ed96165", [:mix], [{:igniter, ">= 0.6.16 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:lazy_html, "~> 0.1.0", [hex: :lazy_html, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0 or ~> 1.8.0-rc", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.15", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "f5296ef67b41da7fe345e139ebaa621ceb5e7e84e37c89a12574cc0536a80604"},
|
||||
"phoenix_pubsub": {:hex, :phoenix_pubsub, "2.2.0", "ff3a5616e1bed6804de7773b92cbccfc0b0f473faf1f63d7daf1206c7aeaaa6f", [:mix], [], "hexpm", "adc313a5bf7136039f63cfd9668fde73bba0765e0614cba80c06ac9460ff3e96"},
|
||||
"phoenix_pubsub_redis": {:hex, :phoenix_pubsub_redis, "3.0.1", "d4d856b1e57a21358e448543e1d091e07e83403dde4383b8be04ed9d2c201cbc", [:mix], [{:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:poolboy, "~> 1.5.1 or ~> 1.6", [hex: :poolboy, repo: "hexpm", optional: false]}, {:redix, "~> 0.10.0 or ~> 1.0", [hex: :redix, repo: "hexpm", optional: false]}], "hexpm", "0b36a17ff6e9a56159f8df8933d62b5c1f0695eae995a02e0c86c035ace6a309"},
|
||||
"phoenix_template": {:hex, :phoenix_template, "1.0.4", "e2092c132f3b5e5b2d49c96695342eb36d0ed514c5b252a77048d5969330d639", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "2c0c81f0e5c6753faf5cca2f229c9709919aba34fab866d3bc05060c9c444206"},
|
||||
|
|
|
|||
|
|
@ -50,6 +50,23 @@ defmodule ToweropsWeb.DeviceLive.ShowTest do
|
|||
end
|
||||
end
|
||||
|
||||
describe "checks tab" do
|
||||
test "displays checks tab link", %{conn: conn, user: user, device: device} do
|
||||
conn = log_in_user(conn, user)
|
||||
{:ok, _view, html} = live(conn, ~p"/devices/#{device.id}?tab=overview")
|
||||
|
||||
assert html =~ "Checks"
|
||||
end
|
||||
|
||||
test "loads checks tab with empty state", %{conn: conn, user: user, device: device} do
|
||||
conn = log_in_user(conn, user)
|
||||
{:ok, _view, html} = live(conn, ~p"/devices/#{device.id}?tab=checks")
|
||||
|
||||
assert html =~ "No checks configured"
|
||||
assert html =~ "Add Check"
|
||||
end
|
||||
end
|
||||
|
||||
describe "tab navigation" do
|
||||
test "renders overview tab with device information", %{conn: conn, user: user, device: device} do
|
||||
conn = log_in_user(conn, user)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue