From c4e301a84c132aa95a3338703b682d7cea9433f2 Mon Sep 17 00:00:00 2001
From: Graham McIntire
| + <.icon name="hero-check-circle" class="h-4 w-4 mx-auto" /> + | +Date | +Original Size | +Compressed Size | +Ratio | +Source | +Actions | +
|---|---|---|---|---|---|---|
| + = 2 + } + title={ + if MapSet.member?(@selected_backup_ids, backup.id), + do: "Selected for comparison", + else: + if(MapSet.size(@selected_backup_ids) >= 2, + do: "Maximum 2 backups can be selected", + else: "Select to compare" + ) + } + class="h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-blue-600 disabled:opacity-50 disabled:cursor-not-allowed dark:border-gray-600 dark:bg-gray-700" + /> + | ++ {ToweropsWeb.TimeHelpers.format_iso8601(backup.backed_up_at, @timezone)} + | ++ {format_bytes(backup.config_size_bytes)} + | ++ {format_bytes(backup.compressed_size_bytes)} + | ++ {compression_ratio}% + | ++ + {String.replace(backup.trigger_source, "_", " ") |> String.capitalize()} + + | +
+
+
+ <%= if ToweropsWeb.Permissions.owner?(@current_scope) do %>
+ <.button
+ phx-click="delete_backup"
+ phx-value-id={backup.id}
+ data-confirm={
+ t(
+ "Are you sure you want to delete this backup? This action cannot be undone."
+ )
+ }
+ variant="danger"
+ >
+ <.icon name="hero-trash" class="h-4 w-4" /> Delete
+
+ <% end %>
+
+ |
+
+ Automatic backups run daily at {ToweropsWeb.TimeHelpers.format_utc_hour( + 7, + @timezone + )}. +
+ <% else %> ++ {t("Assign an agent to enable automatic backups.")} +
+ <% end %> ++ {t("Enable MikroTik API in device settings to enable configuration backups.")} +
++ {t( + "Run discovery to automatically detect sensors, interfaces, and other monitorable items, or add a service check manually." + )} +
+| + {t("Name")} + | ++ {t("Status")} + | ++ {t("Value")} + | ++ {t("Last Checked")} + | ++ Actions + | +
|---|---|---|---|---|
| + {check.name} + | ++ {render_status_badge(check.current_state)} + | ++ <%= if check.last_check_at do %> + {get_latest_value(check)} + <% else %> + + Pending + + <% end %> + | ++ <%= if check.last_check_at do %> + {format_relative_time(check.last_check_at)} + <% else %> + + Never + + <% end %> + | +
+
+ <.link
+ navigate={
+ ~p"/devices/#{@device.id}/graph/check?check_id=#{check.id}&range=24h"
+ }
+ class="text-blue-600 hover:text-blue-900 dark:text-blue-400 dark:hover:text-blue-300 text-sm font-medium"
+ >
+ {t("Graph")}
+
+ <%= if check.check_type in ["http", "tcp", "dns", "ssl"] do %>
+
+
+ <% end %>
+
+ |
+
| + {t("Plan")} + | ++ {t("Status")} + | ++ {t("MRR")} + | +
|---|---|---|
| + {sub.product_name || "---"} + | ++ + "bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-400" + + "suspended" -> + "bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-400" + + _ -> + "bg-gray-100 text-gray-800 dark:bg-gray-900/30 dark:text-gray-400" + end + ]}> + {sub.status || "unknown"} + + | ++ <%= if sub.mrr_amount do %> + ${Decimal.round(sub.mrr_amount, 2)} + + {sub.currency || ""} + + <% else %> + --- + <% end %> + | +
+ {t("No Gaiia inventory item linked to this device.")} +
+| Device | +IP Address | +Interface | +Link Type | +Confidence | +Status | +
|---|---|---|---|---|---|
|
+ <%= if peer_device do %>
+ <.link
+ navigate={~p"/devices/#{peer_device.id}"}
+ class="font-medium text-blue-600 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-300"
+ >
+ {peer_device.name}
+
+ <% else %>
+
+ {link.discovered_remote_name || link.discovered_remote_mac ||
+ "Unknown"}
+
+ <% end %>
+ |
+ + <%= if peer_device do %> + {peer_device.ip_address} + <% else %> + {link.discovered_remote_ip || "-"} + <% end %> + | ++ <%= if link.source_interface do %> + {link.source_interface.if_name} + <% else %> + - + <% end %> + | ++ + "bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200" + + link.link_type == "mac_match" -> + "bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200" + + link.link_type == "arp_inference" -> + "bg-orange-100 text-orange-800 dark:bg-orange-900 dark:text-orange-200" + + true -> + "bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-200" + end + ]}> + {link.link_type} + + | +
+
+
+
+
+
+ {trunc(link.confidence * 100)}%
+
+ = 0.9 -> "bg-green-500"
+ link.confidence >= 0.7 -> "bg-blue-500"
+ link.confidence >= 0.5 -> "bg-yellow-500"
+ true -> "bg-red-500"
+ end
+ ]}
+ style={"width: #{trunc(link.confidence * 100)}%"}
+ >
+
+ |
+ + <%= if peer_device do %> + + {t("Managed")} + + <% else %> + + {t("Discovered")} + + <% end %> + | +
| # | +Name | +Status | +Speed | +IP Address | +MAC | +Capacity | +Utilization | +In | +Out | +Errors | +
|---|---|---|---|---|---|---|---|---|---|---|
| + {interface.if_index} + | +
+ <.link
+ navigate={
+ ~p"/devices/#{@device.id}/graph/traffic?interface_id=#{interface.id}"
+ }
+ class="hover:text-blue-600 dark:hover:text-blue-400"
+ >
+
+ {interface.if_name || interface.if_descr}
+
+ <%= if interface.if_alias do %>
+
+ {interface.if_alias}
+
+ <% end %>
+
+ |
+ + + + + {String.upcase(interface.if_oper_status || "unknown")} + + + | ++ {format_speed(interface.if_speed)} + | +
+ <% interface_ips =
+ Map.get(@ip_addresses_by_interface, interface.id, []) %>
+ <%= if Enum.empty?(interface_ips) do %>
+ -
+ <% else %>
+
+ <%= for ip <- interface_ips do %>
+
+ <% end %>
+
+ {ip.ip_address}
+ <%= if ip.prefix_length do %>
+ /{ip.prefix_length}
+ <% end %>
+ <.icon
+ name="hero-clipboard-document"
+ class="h-3 w-3 inline ml-0.5 opacity-0 group-hover/iip:opacity-100 transition-opacity text-gray-400"
+ />
+
+ <% end %>
+ |
+ + + {interface.if_phys_address} + <.icon + name="hero-clipboard-document" + class="h-3 w-3 inline ml-0.5 opacity-0 group-hover/mac:opacity-100 transition-opacity text-gray-400" + /> + + - + | +
+ <%= if interface.configured_capacity_bps do %>
+
+
+ {format_speed(interface.configured_capacity_bps)}
+
+
+ {capacity_source_label(interface.capacity_source)}
+
+
+
+ <% else %>
+
+ <% end %>
+ |
+
+ <%= if interface.utilization do %>
+
+
+ <% else %>
+ -
+ <% end %>
+
+
+
+ {Float.round(interface.utilization.utilization_pct, 1)}%
+
+
+
+ |
+ + {if interface.latest_stat && interface.latest_stat.if_in_octets, + do: format_bytes(interface.latest_stat.if_in_octets), + else: "-"} + | ++ {if interface.latest_stat && interface.latest_stat.if_out_octets, + do: format_bytes(interface.latest_stat.if_out_octets), + else: "-"} + | ++ <% total_errors = + interface.latest_stat && + (interface.latest_stat.if_in_errors || 0) + + (interface.latest_stat.if_out_errors || 0) + + (interface.latest_stat.if_in_discards || 0) + + (interface.latest_stat.if_out_discards || 0) %> + <%= if total_errors && total_errors > 0 do %> + <.link + navigate={ + ~p"/devices/#{@device.id}/graph/interface_errors?interface_id=#{interface.id}" + } + class="text-red-600 dark:text-red-400 hover:underline" + > + {total_errors} + + <% else %> + - + <% end %> + | +
No interfaces discovered
++ {insight.title} +
++ {insight.description} +
+| + {t("Time")} + | ++ {t("Latency (ms)")} + | ++ {t("P95 Latency")} + | ++ {t("Jitter (ms)")} + | ++ {t("Loss (%)")} + | ++ {t("Throughput")} + | ++ {t("Subscribers")} + | +
|---|---|---|---|---|---|---|
| + {ToweropsWeb.TimeHelpers.format_iso8601( + metric.recorded_at, + @current_scope.timezone, + @current_scope.time_format + )} + | ++ {if metric.avg_latency, + do: Float.round(metric.avg_latency, 1), + else: "---"} + | ++ {if metric.p95_latency, + do: Float.round(metric.p95_latency, 1), + else: "---"} + | ++ {if metric.avg_jitter, + do: Float.round(metric.avg_jitter, 1), + else: "---"} + | ++ {if metric.avg_loss, do: Float.round(metric.avg_loss, 2), else: "---"} + | ++ {if metric.avg_throughput, + do: "#{Float.round(metric.avg_throughput, 1)} Mbps", + else: "---"} + | ++ {metric.subscriber_count || "---"} + | +
+ {t("No Preseem data linked to this device.")} +
++ {length(@wireless_clients)} clients — {@wireless_matched_count} subscribers matched + <%= if @wireless_last_updated do %> + · Last updated {format_relative_time(@wireless_last_updated)} + <% end %> +
+| MAC Address | +IP Address | +Subscriber | +Signal | +SNR | +Distance | +TX Rate | +RX Rate | +Uptime | +
|---|---|---|---|---|---|---|---|---|
| + + {client.mac_address} + <.icon + name="hero-clipboard-document" + class="h-3 w-3 inline ml-0.5 opacity-0 group-hover/mac:opacity-100 transition-opacity text-gray-400" + /> + + | ++ <%= if client.ip_address do %> + + {client.ip_address} + <.icon + name="hero-clipboard-document" + class="h-3 w-3 inline ml-0.5 opacity-0 group-hover/ip:opacity-100 transition-opacity text-gray-400" + /> + + <% else %> + — + <% end %> + | ++ <%= if subscriber_link && subscriber_link.gaiia_account do %> + + {subscriber_link.gaiia_account.account_name} + + <% else %> + — + <% end %> + | ++ <%= if client.signal_strength do %> + <.signal_badge value={client.signal_strength} /> + <% else %> + — + <% end %> + | ++ <%= if client.snr do %> + <.snr_badge value={client.snr} /> + <% else %> + — + <% end %> + | ++ <%= if client.distance do %> + {client.distance}m + <% else %> + — + <% end %> + | ++ {format_rate(client.tx_rate)} + | ++ {format_rate(client.rx_rate)} + | ++ <%= if client.uptime_seconds do %> + {format_uptime(client.uptime_seconds * 100)} + <% else %> + — + <% end %> + | +
+ {t("No clients are currently connected to this access point.")} +
+