From f5a46930cd6f4b070ec02c605a8c9986752c480a Mon Sep 17 00:00:00 2001 From: Josh Kalderimis Date: Mon, 7 Apr 2025 18:17:46 +1200 Subject: [PATCH 1/4] Use hooks instead of LiveComponents --- lib/nerves_hub/devices.ex | 16 +- lib/nerves_hub_web.ex | 127 +++++++++++ .../{activity.ex => activity_tab.ex} | 25 ++- .../components/device_page/console.ex | 112 ---------- .../components/device_page/console_tab.ex | 146 +++++++++++++ .../{details.ex => details_tab.ex} | 202 +++++++++++------- .../device_page/{health.ex => health_tab.ex} | 134 +++++++----- .../{settings.ex => settings_tab.ex} | 93 ++++---- .../live/devices/show-new.html.heex | 23 +- lib/nerves_hub_web/live/devices/show.ex | 73 ++++--- .../live/devices/show.html.heex | 7 +- 11 files changed, 599 insertions(+), 359 deletions(-) rename lib/nerves_hub_web/components/device_page/{activity.ex => activity_tab.ex} (89%) delete mode 100644 lib/nerves_hub_web/components/device_page/console.ex create mode 100644 lib/nerves_hub_web/components/device_page/console_tab.ex rename lib/nerves_hub_web/components/device_page/{details.ex => details_tab.ex} (87%) rename lib/nerves_hub_web/components/device_page/{health.ex => health_tab.ex} (81%) rename lib/nerves_hub_web/components/device_page/{settings.ex => settings_tab.ex} (92%) diff --git a/lib/nerves_hub/devices.ex b/lib/nerves_hub/devices.ex index d878c1b07..ae873e37e 100644 --- a/lib/nerves_hub/devices.ex +++ b/lib/nerves_hub/devices.ex @@ -281,9 +281,9 @@ defmodule NervesHub.Devices do |> where(identifier: ^identifier) |> where(org_id: ^org_id) |> join(:left, [d], o in assoc(d, :org)) - |> join(:left, [d], dp in assoc(d, :deployment_group)) + |> join(:left, [d], dp in assoc(d, :deployment_group), as: :deployment_group) + |> preload([d, o, deployment_group: dg], org: o, deployment_group: dg) |> join_and_preload(preload_assoc) - |> preload([d, o, dp], org: o, deployment_group: dp) end defp join_and_preload(query, assocs) when is_list(assocs) do @@ -312,6 +312,18 @@ defmodule NervesHub.Devices do |> preload([latest_health: lh], latest_health: lh) end + defp join_and_preload(query, :product) do + query + |> join(:left, [d], p in assoc(d, :product), as: :product) + |> preload([product: p], product: p) + end + + defp join_and_preload(query, :firmware) do + query + |> join(:left, [d, deployment_group: dg], f in assoc(dg, :firmware), as: :firmware) + |> preload([deployment_group: dg, firmware: f], deployment_group: {dg, firmware: f}) + end + def get_device_by_x509(cert) do fingerprint = NervesHub.Certificate.fingerprint(cert) diff --git a/lib/nerves_hub_web.ex b/lib/nerves_hub_web.ex index 9f44e68ee..8c56846f9 100644 --- a/lib/nerves_hub_web.ex +++ b/lib/nerves_hub_web.ex @@ -126,6 +126,41 @@ defmodule NervesHubWeb do |> Enum.filter(fn x -> !is_nil(params[to_string(x)]) end) |> Enum.into(%{}, fn x -> {x, params[to_string(x)]} end) end + + defp setup_tab_components(socket, tabs \\ []) do + if socket.assigns[:new_ui] do + Enum.reduce(tabs, socket, fn component, socket -> + component.connect(socket) + end) + |> put_private(:tabs, tabs) + else + socket + end + end + + defp update_tab_component_hooks(socket) do + if socket.assigns[:new_ui] do + socket + |> detach_hooks() + |> attach_hooks() + else + socket + end + end + + defp detach_hooks(socket) do + socket.private[:tabs] + |> Enum.reduce(socket, fn component, socket -> + component.detach_hooks(socket) + end) + end + + defp attach_hooks(socket) do + socket.private[:tabs] + |> Enum.reduce(socket, fn component, socket -> + component.attach_hooks(socket) + end) + end end end @@ -262,6 +297,94 @@ defmodule NervesHubWeb do end end + def hooked_component({:tab_id, tab_id}) do + quote do + use Phoenix.Component + + import NervesHubWeb.Components.Icons + import NervesHubWeb.CoreComponents, only: [button: 1, input: 1, core_label: 1, error: 1] + + import NervesHubWeb.Helpers.Authorization + + import Phoenix.LiveView, + only: [ + assign_async: 3, + assign_async: 4, + allow_upload: 3, + attach_hook: 4, + detach_hook: 3, + push_patch: 2, + push_event: 3, + push_navigate: 2, + start_async: 3, + connected?: 1, + consume_uploaded_entry: 3 + ] + + alias Phoenix.Socket.Broadcast + + def halt(socket), do: {:halt, socket} + + def cont(socket), do: {:cont, socket} + + def page_title(socket, page_title), do: assign(socket, :page_title, page_title) + + def sidebar_tab(socket, tab) do + socket + |> assign(:sidebar_tab, tab) + |> assign(:tab_hint, tab) + end + + def send_toast(socket, kind, msg) do + NervesHubWeb.LiveToast.send_toast(kind, msg) + socket + end + + @tab_id unquote(tab_id) + + defp tab_hook_id(), do: "#{@tab_id}_tab" + + def connect(socket) do + attach_hook(socket, tab_hook_id(), :handle_params, &__MODULE__.hooked_params/3) + end + + def attach_hooks(%{assigns: %{tab: tab}} = socket) when tab == @tab_id do + socket + |> attach_hook(tab_hook_id(), :handle_async, &__MODULE__.hooked_async/3) + |> attach_hook(tab_hook_id(), :handle_event, &__MODULE__.hooked_event/3) + |> attach_hook(tab_hook_id(), :handle_info, &__MODULE__.hooked_info/2) + end + + def attach_hooks(socket), do: socket + + def detach_hooks(%{assigns: %{tab: tab}} = socket) do + socket + |> detach_hook(tab_hook_id(), :handle_async) + |> detach_hook(tab_hook_id(), :handle_event) + |> detach_hook(tab_hook_id(), :handle_info) + end + + def hooked_params(params, uri, socket) do + socket = assign(socket, :tab, socket.assigns.live_action) + + if socket.assigns.tab == @tab_id do + tab_params(params, uri, socket) + else + {:cont, socket} + end + end + + def tab_params(_params, _uri, socket) do + cont(socket) + end + + defoverridable tab_params: 3 + + # Routes generation with the ~p sigil + unquote(verified_routes()) + end + end + def router() do quote do use Phoenix.Router @@ -301,4 +424,8 @@ defmodule NervesHubWeb do defmacro __using__(which) when is_atom(which) do apply(__MODULE__, which, []) end + + defmacro __using__(tab_component: tab_id) do + apply(__MODULE__, :hooked_component, tab_id: tab_id) + end end diff --git a/lib/nerves_hub_web/components/device_page/activity.ex b/lib/nerves_hub_web/components/device_page/activity_tab.ex similarity index 89% rename from lib/nerves_hub_web/components/device_page/activity.ex rename to lib/nerves_hub_web/components/device_page/activity_tab.ex index 7a7d1f3cd..33d9818de 100644 --- a/lib/nerves_hub_web/components/device_page/activity.ex +++ b/lib/nerves_hub_web/components/device_page/activity_tab.ex @@ -1,15 +1,14 @@ -defmodule NervesHubWeb.Components.DevicePage.Activity do - use NervesHubWeb, :live_component +defmodule NervesHubWeb.Components.DevicePage.ActivityTab do + use NervesHubWeb, tab_component: :activity alias NervesHub.AuditLogs alias NervesHubWeb.Components.Pager - def update(assigns, socket) do + def tab_params(_params, _uri, socket) do socket - |> assign(assigns) |> logs_and_pager_assigns() - |> ok() + |> cont() end defp logs_and_pager_assigns(socket, page_number \\ 1, page_size \\ 25) do @@ -82,12 +81,12 @@ defmodule NervesHubWeb.Components.DevicePage.Activity do - + """ end - def handle_event("set-paginate-opts", %{"page-size" => page_size}, socket) do + def hooked_event("set-paginate-opts", %{"page-size" => page_size}, socket) do params = %{"page_size" => page_size, "page_number" => 1} url = @@ -96,10 +95,10 @@ defmodule NervesHubWeb.Components.DevicePage.Activity do socket |> logs_and_pager_assigns(1, String.to_integer(page_size)) |> push_patch(to: url) - |> noreply() + |> halt() end - def handle_event("paginate", %{"page" => page_num}, socket) do + def hooked_event("paginate", %{"page" => page_num}, socket) do params = %{"page_size" => socket.assigns.audit_pager.page_size, "page_number" => page_num} url = @@ -111,6 +110,12 @@ defmodule NervesHubWeb.Components.DevicePage.Activity do socket.assigns.audit_pager.page_size ) |> push_patch(to: url) - |> noreply() + |> halt() end + + def hooked_event(_name, _params, socket), do: {:cont, socket} + + def hooked_info(_name, socket), do: {:cont, socket} + + def hooked_async(_name, _async_fun_result, socket), do: {:cont, socket} end diff --git a/lib/nerves_hub_web/components/device_page/console.ex b/lib/nerves_hub_web/components/device_page/console.ex deleted file mode 100644 index 9f3bc32f5..000000000 --- a/lib/nerves_hub_web/components/device_page/console.ex +++ /dev/null @@ -1,112 +0,0 @@ -defmodule NervesHubWeb.Components.DevicePage.Console do - use NervesHubWeb, :live_component - - alias NervesHub.Tracker - alias Phoenix.LiveView.JS - - def update(%{file_upload: payload}, socket) do - if socket.assigns.user.id == payload.uploaded_by do - case payload.status do - "started" -> - send_toast(socket, :info, "Upload started.") - - "finished" -> - send_toast(socket, :info, "Upload finished.") - - _ -> - true - end - end - - ok(socket) - end - - def update(assigns, socket) do - socket - |> assign(assigns) - |> assign(:user_token, Phoenix.Token.sign(socket, "user salt", assigns.user.id)) - |> assign(:console_active?, Tracker.console_active?(assigns.device)) - |> ok() - end - - def toggle_fullscreen(js \\ %JS{}) do - js - # Dropzone - # disable relative - |> JS.toggle_class("relative", to: "#dropzone") - |> JS.toggle_class("fixed", to: "#dropzone") - |> JS.toggle_class("top-0", to: "#dropzone") - |> JS.toggle_class("left-0", to: "#dropzone") - |> JS.toggle_class("right-0", to: "#dropzone") - |> JS.toggle_class("bottom-0", to: "#dropzone") - # disable p-12 - |> JS.toggle_class("p-12", to: "#dropzone") - |> JS.toggle_class("p-6", to: "#dropzone") - |> JS.toggle_class("pt-14", to: "#dropzone") - |> JS.toggle_class("pb-4", to: "#dropzone") - |> JS.toggle_class("box-border", to: "#dropzone") - - # Immersive device information - |> JS.toggle(to: "#immersive-device") - - # Console - |> JS.toggle_class("box-border", to: "#console") - # disable w-full - |> JS.toggle_class("w-full", to: "#console") - # disable h-full - |> JS.toggle_class("h-full", to: "#console") - - # Fullscreen/Close button - # disable right-16 - |> JS.toggle_class("right-16", to: "#fullscreen") - |> JS.toggle_class("right-4", to: "#fullscreen") - # disable top-8 - |> JS.toggle_class("top-8", to: "#fullscreen") - |> JS.toggle_class("top-4", to: "#fullscreen") - |> JS.toggle_class("hidden", to: "#fullscreen svg") - end - - def render(assigns) do - ~H""" -
-
-
-
-
- - -
-
- The device console isn't currently available. -
-
- You don't have the required permissions to access a Device console. -
-
-
-
- """ - end -end diff --git a/lib/nerves_hub_web/components/device_page/console_tab.ex b/lib/nerves_hub_web/components/device_page/console_tab.ex new file mode 100644 index 000000000..3cb0faba3 --- /dev/null +++ b/lib/nerves_hub_web/components/device_page/console_tab.ex @@ -0,0 +1,146 @@ +defmodule NervesHubWeb.Components.DevicePage.ConsoleTab do + use NervesHubWeb, tab_component: :console + + alias NervesHub.Tracker + + alias Phoenix.LiveView.JS + + def tab_params(_params, _uri, socket) do + device = socket.assigns.device + + socket + |> assign_async( + :console_active?, + fn -> + {:ok, %{console_active?: Tracker.console_active?(device)}} + end, + reset: true + ) + |> cont() + end + + def hooked_info(%Broadcast{event: "file-data/start", payload: payload}, socket) do + if socket.assigns.user.id == payload.uploaded_by do + send_toast(socket, :info, "Upload started.") + else + socket + end + |> halt() + end + + def hooked_info(%Broadcast{event: "file-data/stop", payload: payload}, socket) do + if socket.assigns.user.id == payload.uploaded_by do + send_toast(socket, :info, "Upload finished.") + else + socket + end + |> halt() + end + + def hooked_info(_event, socket), do: {:cont, socket} + + def hooked_event(_event, _params, socket), do: {:cont, socket} + + def hooked_async(_name, _async_fun_result, socket), do: {:cont, socket} + + def toggle_fullscreen(js \\ %JS{}) do + js + # Dropzone + # disable relative + |> JS.toggle_class("relative", to: "#dropzone") + |> JS.toggle_class("fixed", to: "#dropzone") + |> JS.toggle_class("top-0", to: "#dropzone") + |> JS.toggle_class("left-0", to: "#dropzone") + |> JS.toggle_class("right-0", to: "#dropzone") + |> JS.toggle_class("bottom-0", to: "#dropzone") + # disable p-12 + |> JS.toggle_class("p-12", to: "#dropzone") + |> JS.toggle_class("p-6", to: "#dropzone") + |> JS.toggle_class("pt-14", to: "#dropzone") + |> JS.toggle_class("pb-4", to: "#dropzone") + |> JS.toggle_class("box-border", to: "#dropzone") + + # Immersive device information + |> JS.toggle(to: "#immersive-device") + + # Console + |> JS.toggle_class("box-border", to: "#console") + # disable w-full + |> JS.toggle_class("w-full", to: "#console") + # disable h-full + |> JS.toggle_class("h-full", to: "#console") + + # Fullscreen/Close button + # disable right-16 + |> JS.toggle_class("right-16", to: "#fullscreen") + |> JS.toggle_class("right-4", to: "#fullscreen") + # disable top-8 + |> JS.toggle_class("top-8", to: "#fullscreen") + |> JS.toggle_class("top-4", to: "#fullscreen") + |> JS.toggle_class("hidden", to: "#fullscreen svg") + end + + def render(assigns) do + token = Phoenix.Token.sign(NervesHubWeb.Endpoint, "user salt", assigns.user.id) + + assigns = Map.put(assigns, :user_token, token) + + ~H""" +
+
+ <.async_result :let={online?} assign={@console_active?}> + <:loading> +
+
+ Checking if the device is online... +
+
+ + <:failed :let={_failure}> +
+
+ There was an error checking if the device was online. +
+
+ +
+
+
+ + +
+
+ The device console isn't currently available. +
+
+ You don't have the required permissions to access a Device console. +
+
+ +
+
+ """ + end +end diff --git a/lib/nerves_hub_web/components/device_page/details.ex b/lib/nerves_hub_web/components/device_page/details_tab.ex similarity index 87% rename from lib/nerves_hub_web/components/device_page/details.ex rename to lib/nerves_hub_web/components/device_page/details_tab.ex index 547340f86..d8176b22d 100644 --- a/lib/nerves_hub_web/components/device_page/details.ex +++ b/lib/nerves_hub_web/components/device_page/details_tab.ex @@ -1,5 +1,5 @@ -defmodule NervesHubWeb.Components.DevicePage.Details do - use NervesHubWeb, :live_component +defmodule NervesHubWeb.Components.DevicePage.DetailsTab do + use NervesHubWeb, tab_component: :details require Logger @@ -16,35 +16,8 @@ defmodule NervesHubWeb.Components.DevicePage.Details do alias NervesHubWeb.Components.HealthStatus alias NervesHubWeb.Components.NewUI.DeviceLocation - def update(%{latest_metrics: latest_metrics}, socket) do + def tab_params(_params, _uri, %{assigns: %{device: device}} = socket) do socket - |> assign(:latest_metrics, latest_metrics) - |> assign_metadata() - |> ok() - end - - def update(%{update_auto_refresh_health: auto_refresh_health}, socket) do - socket - |> assign(:auto_refresh_health, auto_refresh_health) - |> ok() - end - - def update(%{firmwares: firmware}, socket) do - socket - |> assign(:firmwares, firmware) - |> send_toast(:info, "New firmware available for selection") - |> ok() - end - - def update(assigns, socket) do - device = Devices.get_complete_device(assigns.device_id) - - socket - |> assign(:device, device) - |> assign(:product, device.product) - |> assign(:org, device.org) - |> assign(:org_user, assigns.org_user) - |> assign(:user, assigns.user) |> assign(:device_connection, device.latest_connection) |> assign_support_scripts() |> assign(:firmwares, Firmwares.get_firmware_for_device(device)) @@ -52,10 +25,9 @@ defmodule NervesHubWeb.Components.DevicePage.Details do |> assign(:latest_metrics, Metrics.get_latest_metric_set(device.id)) |> assign(:alarms, Alarms.get_current_alarms_for_device(device)) |> assign(:extension_overrides, extension_overrides(device, device.product)) - |> assign(:auto_refresh_health, true) |> assign_metadata() |> assign_deployment_groups() - |> ok() + |> cont() end defp assign_metadata(%{assigns: %{device: device}} = socket) do @@ -76,16 +48,19 @@ defmodule NervesHubWeb.Components.DevicePage.Details do assign(socket, :support_scripts, scripts) end - defp assign_deployment_groups(%{assigns: %{device: %{status: :provisioned} = device}} = socket), - do: assign(socket, deployment_groups: ManagedDeployments.eligible_deployment_groups(device)) + defp assign_deployment_groups(%{assigns: %{device: %{status: :provisioned} = device}} = socket) do + assign(socket, deployment_groups: ManagedDeployments.eligible_deployment_groups(device)) + end - defp assign_deployment_groups(%{assigns: %{product: product}} = socket), - do: - assign(socket, - deployment_groups: ManagedDeployments.get_deployment_groups_by_product(product) - ) + defp assign_deployment_groups(%{assigns: %{product: product}} = socket) do + assign(socket, + deployment_groups: ManagedDeployments.get_deployment_groups_by_product(product) + ) + end def render(assigns) do + assigns = Map.put(assigns, :auto_refresh_health, !!assigns.health_check_timer) + ~H"""
@@ -196,7 +171,7 @@ defmodule NervesHubWeb.Components.DevicePage.Details do
Health
-
+
No device health information has been received.
@@ -307,7 +282,6 @@ defmodule NervesHubWeb.Components.DevicePage.Details do data-confirm="Are you sure you want to remove the device from the deployment?" aria-label="Remove device from the assigned deployment group" type="button" - phx-target={@myself} phx-click="remove-from-deployment-group" > @@ -326,7 +300,7 @@ defmodule NervesHubWeb.Components.DevicePage.Details do
-
+
@@ -432,13 +399,13 @@ defmodule NervesHubWeb.Components.DevicePage.Details do
{script.name} - -
- <.live_component :if={@tab == :details} module={DetailsPage} id="device_details" device_id={@device.id} org_user={@org_user} user={@user} /> - - <.live_component - :if={@tab == :health} - module={HealthPage} - id="device_health" - device_id={@device.id} - device_identifier={@device.identifier} - org_name={@org.name} - product_name={@product.name} - health_enabled?={@product.extensions.health && @device.extensions.health} - /> - - <.live_component :if={@tab == :activity} module={ActivityPage} id="device_activity" device={@device} org={@org} product={@product} user={@user} /> - - <.live_component :if={@tab == :console} module={ConsolePage} id="device_console" device={@device} device_connection={@device_connection} user={@user} org_user={@org_user} /> - - <.live_component :if={@tab == :settings} module={SettingsPage} id="device_settings" device={@device} org={@org} product={@product} user={@user} org_user={@org_user} /> + <.render_tab {assigns} />
diff --git a/lib/nerves_hub_web/live/devices/show.ex b/lib/nerves_hub_web/live/devices/show.ex index bea1bd88f..672015b74 100644 --- a/lib/nerves_hub_web/live/devices/show.ex +++ b/lib/nerves_hub_web/live/devices/show.ex @@ -24,11 +24,13 @@ defmodule NervesHubWeb.Live.Devices.Show do alias NervesHubWeb.Components.FwupProgress alias NervesHubWeb.Components.Utils - alias NervesHubWeb.Components.DevicePage.Activity, as: ActivityPage - alias NervesHubWeb.Components.DevicePage.Console, as: ConsolePage - alias NervesHubWeb.Components.DevicePage.Details, as: DetailsPage - alias NervesHubWeb.Components.DevicePage.Health, as: HealthPage - alias NervesHubWeb.Components.DevicePage.Settings, as: SettingsPage + alias NervesHubWeb.Components.DevicePage.ActivityTab + alias NervesHubWeb.Components.DevicePage.ConsoleTab + alias NervesHubWeb.Components.DevicePage.DetailsTab + alias NervesHubWeb.Components.DevicePage.HealthTab + alias NervesHubWeb.Components.DevicePage.SettingsTab + + @tab_components [ActivityTab, ConsoleTab, DetailsTab, HealthTab, SettingsTab] alias NervesHubWeb.Presence alias Phoenix.Socket.Broadcast @@ -64,12 +66,13 @@ defmodule NervesHubWeb.Live.Devices.Show do |> audit_log_assigns() |> assign_deployment_groups() |> setup_presence_tracking() + |> setup_tab_components(@tab_components) |> ok() end def handle_params(_params, _uri, socket) do socket - |> selected_tab() + |> update_tab_component_hooks() |> noreply() end @@ -95,11 +98,10 @@ defmodule NervesHubWeb.Live.Devices.Show do end end + # can be removed when the old UI is removed def handle_info(%Broadcast{topic: "firmware", event: "created"}, socket) do firmware = Firmwares.get_firmware_for_device(socket.assigns.device) - send_update(self(), DetailsPage, id: "device_details", firmwares: firmware) - {:noreply, assign(socket, :firmwares, firmware)} end @@ -175,9 +177,6 @@ defmodule NervesHubWeb.Live.Devices.Show do ) do latest_metrics = Metrics.get_latest_metric_set(device.id) - send_update(DetailsPage, id: "device_details", latest_metrics: latest_metrics) - send_update(HealthPage, id: "device_health", refresh_metrics: true) - socket |> assign(:latest_metrics, latest_metrics) |> assign_metadata() @@ -202,24 +201,6 @@ defmodule NervesHubWeb.Live.Devices.Show do {:noreply, assign(socket, :device, device)} end - def handle_info(%Broadcast{event: "file-data/start", payload: payload}, socket) do - send_update(self(), ConsolePage, - id: "device_console", - file_upload: Map.put(payload, :status, "started") - ) - - {:noreply, socket} - end - - def handle_info(%Broadcast{event: "file-data/stop", payload: payload}, socket) do - send_update(self(), ConsolePage, - id: "device_console", - file_upload: Map.put(payload, :status, "finished") - ) - - {:noreply, socket} - end - # Ignore unknown messages def handle_info(_unknown, socket), do: {:noreply, socket} @@ -297,13 +278,12 @@ defmodule NervesHubWeb.Live.Devices.Show do {:noreply, put_flash(socket, :info, "Device identification requested")} end + # TODO: [OLD UI] Can we removed when we remove the old UI def handle_event("toggle-health-check-auto-refresh", _value, socket) do if timer_ref = socket.assigns.health_check_timer do _ = Process.cancel_timer(timer_ref) - send_update(DetailsPage, id: "device_details", update_auto_refresh_health: false) {:noreply, assign(socket, :health_check_timer, nil)} else - send_update(DetailsPage, id: "device_details", update_auto_refresh_health: true) {:noreply, schedule_health_check_timer(socket)} end end @@ -332,7 +312,7 @@ defmodule NervesHubWeb.Live.Devices.Show do |> noreply() end - def handle_event("toggle-automatic-updates", _params, socket) do + def handle_event("toggle-deployment-firmware-updates", _params, socket) do %{org_user: org_user, user: user, device: device} = socket.assigns authorized!(:"device:toggle-updates", org_user) @@ -380,6 +360,12 @@ defmodule NervesHubWeb.Live.Devices.Show do {:noreply, assign(socket, :device, device)} end + def handle_event("set-deployment-group", %{"deployment_id" => ""}, socket) do + socket + |> put_flash(:error, "Please select a deployment group.") + |> noreply() + end + def handle_event( "set-deployment-group", %{"deployment_id" => deployment_id}, @@ -397,6 +383,12 @@ defmodule NervesHubWeb.Live.Devices.Show do |> noreply() end + def handle_event("push-update", %{"uuid" => ""}, socket) do + socket + |> put_flash(:error, "Please select a firmware you would like to send to the device.") + |> noreply() + end + def handle_event("push-update", %{"uuid" => uuid}, socket) do authorized!(:"device:push-update", socket.assigns.org_user) @@ -525,7 +517,12 @@ defmodule NervesHubWeb.Live.Devices.Show do end defp load_device(org, identifier) do - Devices.get_device_by_identifier!(org, identifier, [:latest_connection, :latest_health]) + Devices.get_device_by_identifier!(org, identifier, [ + :product, + :firmware, + :latest_connection, + :latest_health + ]) end defp setup_presence_tracking(%{assigns: %{device: device, user: user}} = socket) do @@ -691,4 +688,14 @@ defmodule NervesHubWeb.Live.Devices.Show do |> get_in([:health, :ui_polling_seconds]) |> :timer.seconds() end + + def render_tab(assigns) do + ~H""" + + + + + + """ + end end diff --git a/lib/nerves_hub_web/live/devices/show.html.heex b/lib/nerves_hub_web/live/devices/show.html.heex index 6f1ee60aa..fefd83dd8 100644 --- a/lib/nerves_hub_web/live/devices/show.html.heex +++ b/lib/nerves_hub_web/live/devices/show.html.heex @@ -35,7 +35,12 @@ Console <% end %> - From c9123c1c1ebe53998ac0e732b6d1f3e84593e72e Mon Sep 17 00:00:00 2001 From: Josh Kalderimis Date: Tue, 8 Apr 2025 14:40:39 +1200 Subject: [PATCH 2/4] Add a `cleanup` hook to remove assigns that are no longer needed --- lib/nerves_hub_web.ex | 47 +++++++++++-------- .../components/device_page/activity_tab.ex | 4 ++ .../components/device_page/console_tab.ex | 4 ++ .../components/device_page/details_tab.ex | 12 ++++- .../components/device_page/health_tab.ex | 4 ++ 5 files changed, 51 insertions(+), 20 deletions(-) diff --git a/lib/nerves_hub_web.ex b/lib/nerves_hub_web.ex index 8c56846f9..210f25a25 100644 --- a/lib/nerves_hub_web.ex +++ b/lib/nerves_hub_web.ex @@ -323,23 +323,6 @@ defmodule NervesHubWeb do alias Phoenix.Socket.Broadcast - def halt(socket), do: {:halt, socket} - - def cont(socket), do: {:cont, socket} - - def page_title(socket, page_title), do: assign(socket, :page_title, page_title) - - def sidebar_tab(socket, tab) do - socket - |> assign(:sidebar_tab, tab) - |> assign(:tab_hint, tab) - end - - def send_toast(socket, kind, msg) do - NervesHubWeb.LiveToast.send_toast(kind, msg) - socket - end - @tab_id unquote(tab_id) defp tab_hook_id(), do: "#{@tab_id}_tab" @@ -370,7 +353,12 @@ defmodule NervesHubWeb do if socket.assigns.tab == @tab_id do tab_params(params, uri, socket) else - {:cont, socket} + cleanup() + |> Enum.reduce(socket, fn key, acc -> + new_assigns = Map.delete(acc.assigns, key) + Map.put(acc, :assigns, new_assigns) + end) + |> cont() end end @@ -378,7 +366,28 @@ defmodule NervesHubWeb do cont(socket) end - defoverridable tab_params: 3 + def cleanup() do + [] + end + + defoverridable tab_params: 3, cleanup: 0 + + def halt(socket), do: {:halt, socket} + + def cont(socket), do: {:cont, socket} + + def page_title(socket, page_title), do: assign(socket, :page_title, page_title) + + def sidebar_tab(socket, tab) do + socket + |> assign(:sidebar_tab, tab) + |> assign(:tab_hint, tab) + end + + def send_toast(socket, kind, msg) do + NervesHubWeb.LiveToast.send_toast(kind, msg) + socket + end # Routes generation with the ~p sigil unquote(verified_routes()) diff --git a/lib/nerves_hub_web/components/device_page/activity_tab.ex b/lib/nerves_hub_web/components/device_page/activity_tab.ex index 33d9818de..5cbd8a4ee 100644 --- a/lib/nerves_hub_web/components/device_page/activity_tab.ex +++ b/lib/nerves_hub_web/components/device_page/activity_tab.ex @@ -11,6 +11,10 @@ defmodule NervesHubWeb.Components.DevicePage.ActivityTab do |> cont() end + def cleanup() do + [:activity, :audit_pager] + end + defp logs_and_pager_assigns(socket, page_number \\ 1, page_size \\ 25) do {logs, audit_pager} = AuditLogs.logs_for_feed(socket.assigns.device, %{ diff --git a/lib/nerves_hub_web/components/device_page/console_tab.ex b/lib/nerves_hub_web/components/device_page/console_tab.ex index 3cb0faba3..71af75784 100644 --- a/lib/nerves_hub_web/components/device_page/console_tab.ex +++ b/lib/nerves_hub_web/components/device_page/console_tab.ex @@ -19,6 +19,10 @@ defmodule NervesHubWeb.Components.DevicePage.ConsoleTab do |> cont() end + def cleanup() do + [:console_active?] + end + def hooked_info(%Broadcast{event: "file-data/start", payload: payload}, socket) do if socket.assigns.user.id == payload.uploaded_by do send_toast(socket, :info, "Upload started.") diff --git a/lib/nerves_hub_web/components/device_page/details_tab.ex b/lib/nerves_hub_web/components/device_page/details_tab.ex index d8176b22d..11b67fbd8 100644 --- a/lib/nerves_hub_web/components/device_page/details_tab.ex +++ b/lib/nerves_hub_web/components/device_page/details_tab.ex @@ -16,9 +16,17 @@ defmodule NervesHubWeb.Components.DevicePage.DetailsTab do alias NervesHubWeb.Components.HealthStatus alias NervesHubWeb.Components.NewUI.DeviceLocation + @keys_to_cleanup [ + :support_scripts, + :firmwares, + :update_information, + :alarms, + :extension_overrides, + :deployment_groups + ] + def tab_params(_params, _uri, %{assigns: %{device: device}} = socket) do socket - |> assign(:device_connection, device.latest_connection) |> assign_support_scripts() |> assign(:firmwares, Firmwares.get_firmware_for_device(device)) |> assign(:update_information, Devices.resolve_update(device)) @@ -30,6 +38,8 @@ defmodule NervesHubWeb.Components.DevicePage.DetailsTab do |> cont() end + def cleanup(), do: @keys_to_cleanup + defp assign_metadata(%{assigns: %{device: device}} = socket) do health = Devices.get_latest_health(device.id) diff --git a/lib/nerves_hub_web/components/device_page/health_tab.ex b/lib/nerves_hub_web/components/device_page/health_tab.ex index c60db1740..ba192644a 100644 --- a/lib/nerves_hub_web/components/device_page/health_tab.ex +++ b/lib/nerves_hub_web/components/device_page/health_tab.ex @@ -52,6 +52,10 @@ defmodule NervesHubWeb.Components.DevicePage.HealthTab do |> cont() end + def cleanup() do + [:time_frame, :time_frame_opts, :charts] + end + def hooked_async(_name, _async_fun_result, socket), do: {:cont, socket} def hooked_event("set-time-frame", %{"unit" => unit, "amount" => amount}, socket) do From db26bcb9cdc4bd97d67a782ab47921df3d48e60d Mon Sep 17 00:00:00 2001 From: Josh Kalderimis Date: Tue, 8 Apr 2025 16:25:24 +1200 Subject: [PATCH 3/4] Only support the new UI for all Product pages --- lib/nerves_hub_web.ex | 24 +- .../components/audit_log_feed.ex | 34 - .../components/device_header.ex | 112 --- .../components/device_health.ex | 18 - .../components/device_location.ex | 243 ++++-- .../components/device_page/console_tab.ex | 7 + .../components/device_page/details_tab.ex | 18 +- .../components/device_page/health_tab.ex | 2 +- .../components/new_ui/device_location.ex | 254 ------ .../dynamic_template_renderer.ex | 96 --- .../list_archives_template.html.heex | 215 +++-- .../list_archives_template_new.html.heex | 147 ---- .../show_archive_template.html.heex | 162 ++-- .../show_archive_template_new.html.heex | 131 --- .../upload_archive_template.html.heex | 18 - lib/nerves_hub_web/live/archives.ex | 30 +- .../live/deployment_groups/edit.ex | 170 ---- .../live/deployment_groups/edit.html.heex | 226 ----- .../deployment_groups/index-new.html.heex | 149 ---- .../live/deployment_groups/index.ex | 5 - .../live/deployment_groups/index.html.heex | 226 +++-- .../live/deployment_groups/new.ex | 92 +- .../live/deployment_groups/new.html.heex | 168 ++-- .../live/deployment_groups/newz.ex | 169 ---- .../live/deployment_groups/newz.html.heex | 119 --- .../live/deployment_groups/show-new.html.heex | 111 --- .../live/deployment_groups/show.ex | 61 -- .../live/deployment_groups/show.html.heex | 287 ++---- .../live/devices/device_health.ex | 282 ------ .../live/devices/device_health.html.heex | 65 -- .../live/devices/index-new.html.heex | 560 ------------ lib/nerves_hub_web/live/devices/index.ex | 63 -- .../live/devices/index.html.heex | 815 ++++++++++-------- .../live/devices/new-new.html.heex | 60 -- lib/nerves_hub_web/live/devices/new.html.heex | 78 +- lib/nerves_hub_web/live/devices/settings.ex | 168 ---- .../live/devices/settings.html.heex | 207 ----- .../live/devices/show-new.html.heex | 225 ----- lib/nerves_hub_web/live/devices/show.ex | 381 +------- .../live/devices/show.html.heex | 494 ++++------- lib/nerves_hub_web/live/firmware.ex | 31 +- .../list_firmware_template.html.heex | 225 +++-- .../list_firmware_template_new.html.heex | 154 ---- .../show_firmware_template.html.heex | 168 ++-- .../show_firmware_template_new.html.heex | 137 --- .../upload_firmware_template.html.heex | 26 - .../live/product/settings-new.html.heex | 212 ----- .../live/product/settings.html.heex | 365 ++++---- .../live/support_scripts/edit-new.html.heex | 60 -- .../live/support_scripts/edit.html.heex | 57 +- .../live/support_scripts/index-new.html.heex | 64 -- .../live/support_scripts/index.html.heex | 78 +- .../live/support_scripts/new-new.html.heex | 56 -- .../live/support_scripts/new.html.heex | 53 +- lib/nerves_hub_web/live_view.ex | 33 - lib/nerves_hub_web/mounts/layout_selector.ex | 11 - lib/nerves_hub_web/router.ex | 51 +- .../templates/layout/_footer.html.heex | 4 - .../live/devices/new_ui/show_test.exs | 3 +- 59 files changed, 2105 insertions(+), 6375 deletions(-) delete mode 100644 lib/nerves_hub_web/components/audit_log_feed.ex delete mode 100644 lib/nerves_hub_web/components/device_header.ex delete mode 100644 lib/nerves_hub_web/components/device_health.ex delete mode 100644 lib/nerves_hub_web/components/new_ui/device_location.ex delete mode 100644 lib/nerves_hub_web/dynamic_template_renderer.ex delete mode 100644 lib/nerves_hub_web/live/archive_templates/list_archives_template_new.html.heex delete mode 100644 lib/nerves_hub_web/live/archive_templates/show_archive_template_new.html.heex delete mode 100644 lib/nerves_hub_web/live/archive_templates/upload_archive_template.html.heex delete mode 100644 lib/nerves_hub_web/live/deployment_groups/edit.ex delete mode 100644 lib/nerves_hub_web/live/deployment_groups/edit.html.heex delete mode 100644 lib/nerves_hub_web/live/deployment_groups/index-new.html.heex delete mode 100644 lib/nerves_hub_web/live/deployment_groups/newz.ex delete mode 100644 lib/nerves_hub_web/live/deployment_groups/newz.html.heex delete mode 100644 lib/nerves_hub_web/live/deployment_groups/show-new.html.heex delete mode 100644 lib/nerves_hub_web/live/devices/device_health.ex delete mode 100644 lib/nerves_hub_web/live/devices/device_health.html.heex delete mode 100644 lib/nerves_hub_web/live/devices/index-new.html.heex delete mode 100644 lib/nerves_hub_web/live/devices/new-new.html.heex delete mode 100644 lib/nerves_hub_web/live/devices/settings.ex delete mode 100644 lib/nerves_hub_web/live/devices/settings.html.heex delete mode 100644 lib/nerves_hub_web/live/devices/show-new.html.heex delete mode 100644 lib/nerves_hub_web/live/firmware_templates/list_firmware_template_new.html.heex delete mode 100644 lib/nerves_hub_web/live/firmware_templates/show_firmware_template_new.html.heex delete mode 100644 lib/nerves_hub_web/live/firmware_templates/upload_firmware_template.html.heex delete mode 100644 lib/nerves_hub_web/live/product/settings-new.html.heex delete mode 100644 lib/nerves_hub_web/live/support_scripts/edit-new.html.heex delete mode 100644 lib/nerves_hub_web/live/support_scripts/index-new.html.heex delete mode 100644 lib/nerves_hub_web/live/support_scripts/new-new.html.heex delete mode 100644 lib/nerves_hub_web/live_view.ex delete mode 100644 lib/nerves_hub_web/mounts/layout_selector.ex diff --git a/lib/nerves_hub_web.ex b/lib/nerves_hub_web.ex index 210f25a25..99aecc931 100644 --- a/lib/nerves_hub_web.ex +++ b/lib/nerves_hub_web.ex @@ -74,7 +74,7 @@ defmodule NervesHubWeb do def updated_live_view() do quote do - use NervesHubWeb.LiveView, + use Phoenix.LiveView, layout: {NervesHubWeb.LayoutView, :live}, container: {:div, class: "h-screen"} @@ -128,24 +128,16 @@ defmodule NervesHubWeb do end defp setup_tab_components(socket, tabs \\ []) do - if socket.assigns[:new_ui] do - Enum.reduce(tabs, socket, fn component, socket -> - component.connect(socket) - end) - |> put_private(:tabs, tabs) - else - socket - end + Enum.reduce(tabs, socket, fn component, socket -> + component.connect(socket) + end) + |> put_private(:tabs, tabs) end defp update_tab_component_hooks(socket) do - if socket.assigns[:new_ui] do - socket - |> detach_hooks() - |> attach_hooks() - else - socket - end + socket + |> detach_hooks() + |> attach_hooks() end defp detach_hooks(socket) do diff --git a/lib/nerves_hub_web/components/audit_log_feed.ex b/lib/nerves_hub_web/components/audit_log_feed.ex deleted file mode 100644 index 04599422a..000000000 --- a/lib/nerves_hub_web/components/audit_log_feed.ex +++ /dev/null @@ -1,34 +0,0 @@ -defmodule NervesHubWeb.Components.AuditLogFeed do - use NervesHubWeb, :component - - import NervesHubWeb.LayoutView, only: [pagination_links: 1] - - alias NervesHubWeb.LayoutView.DateTimeFormat - - attr(:audit_logs, :list) - attr(:audit_pager, :map) - - def render(assigns) do - ~H""" -
- <%= if Enum.empty?(@audit_logs) do %> -
-

No activity

-
- <% else %> -
-
-
-

{audit_log.description}

-
- {DateTimeFormat.from_now(audit_log.inserted_at)} at <%= audit_log.inserted_at %> - {if audit_log.reference_id, do: "(Ref: #{audit_log.reference_id})"} -
-
-
- {pagination_links(@audit_pager)} - <% end %> -
- """ - end -end diff --git a/lib/nerves_hub_web/components/device_header.ex b/lib/nerves_hub_web/components/device_header.ex deleted file mode 100644 index 3886ab25a..000000000 --- a/lib/nerves_hub_web/components/device_header.ex +++ /dev/null @@ -1,112 +0,0 @@ -defmodule NervesHubWeb.Components.DeviceHeader do - use NervesHubWeb, :component - - alias NervesHub.Devices - - attr(:org, :any) - attr(:product, :any) - attr(:device, :any) - attr(:device_connection, :any) - - def render(assigns) do - ~H""" -

{@device.identifier}

- - <%= if @device.description do %> -

{@device.description}

- <% end %> - -
-
-
-
Status
-

- {get_status(@device_connection)} - - <%= if get_status(@device_connection) == "offline" do %> - offline - <% else %> - online - <% end %> - -

-
-
-
Last connected
-

Never

-

- - - - - Device connected: - - - {@device_connection.established_at} - - - - - Last heartbeat: - - - {@device_connection.last_seen_at} - - - - - Disconnected at: - - - {@device_connection.disconnected_at} - - - - -

-
-
-
Version
- <%= if is_nil(@device.firmware_metadata) do %> -

Unknown

- <% else %> - <.link navigate={~p"/org/#{@org.name}/#{@product.name}/firmware/#{@device.firmware_metadata.uuid}"} class="badge ff-m mt-0"> - {@device.firmware_metadata.version} ({String.slice(@device.firmware_metadata.uuid, 0..7)}) - - <% end %> -
-
-
Firmware Updates
-

- <%= cond do %> - <% @device.updates_enabled == false -> %> - Disabled - - Firmware blocked icon - - <% Devices.device_in_penalty_box?(@device) -> %> - In Penalty Box - - Firmware penalty box icon - - - Clear - - <% true -> %> - Enabled - - Firmware enabled icon - - <% end %> -

-
-
-
- """ - end - - defp get_status(%{status: :connected}), do: "online" - defp get_status(_), do: "offline" -end diff --git a/lib/nerves_hub_web/components/device_health.ex b/lib/nerves_hub_web/components/device_health.ex deleted file mode 100644 index 52fb61212..000000000 --- a/lib/nerves_hub_web/components/device_health.ex +++ /dev/null @@ -1,18 +0,0 @@ -defmodule NervesHubWeb.Components.DeviceHealth do - use NervesHubWeb, :component - - def health_section(assigns) do - ~H""" - - {String.replace(key, "_", " ")} - - {cond do - is_map(value) -> health_section(%{data: value}) - is_float(value) -> round(value) - true -> to_string(value) - end} - - - """ - end -end diff --git a/lib/nerves_hub_web/components/device_location.ex b/lib/nerves_hub_web/components/device_location.ex index 0fc5a8569..65a1451c9 100644 --- a/lib/nerves_hub_web/components/device_location.ex +++ b/lib/nerves_hub_web/components/device_location.ex @@ -1,7 +1,13 @@ defmodule NervesHubWeb.Components.DeviceLocation do use NervesHubWeb, :component + alias Phoenix.LiveView.JS + attr(:location, :any) + attr(:enabled_device, :any) + attr(:enabled_product, :any) + attr(:enable_location_editor, :boolean) + attr(:target, :any) # catch all to add the mapbox token def render(assigns) when not is_map_key(assigns, :mapbox_access_token) do @@ -15,92 +21,233 @@ defmodule NervesHubWeb.Components.DeviceLocation do # mapbox token is nil, maps aren't enabled def render(%{mapbox_access_token: nil} = assigns) do ~H""" -
- <.location_header tooltip="The devices location is determined by looking up the request IP of the device using a GeoIP database." /> +
+
+
Location
+
+ +
+
+
+
Device maps haven't been enabled on your platform.
+
Please contact your platform admin.
+
+
+
+ """ + end -
- Device maps haven't been enabled on your platform. + # disabled in product settings + def render(%{enabled_product: false} = assigns) do + ~H""" +
+
+
Location
-
- Please contact your platform admin. + +
+
+
+
Device maps have been disabled in your product settings.
+
To enable this feature, please contact your product admin.
+
""" end - # location information is nil, geo location might not be enabled - def render(%{location: nil}) do - render(%{location: %{}}) + # disabled in device settings + def render(%{enabled_device: false} = assigns) do + ~H""" +
+
+
Location
+
+ +
+
+
+
Device maps have been disabled for this device.
+
To enable this feature, please contact your product admin.
+
+
+
+ """ end - # location information is empty, geo location might not be enabled - def render(%{location: location} = assigns) - when map_size(location) == 0 do + # yay, we have a location and map key, lets display a map + def render(%{enable_location_editor: true} = assigns) do ~H""" -
- <.location_header tooltip="The devices location is announced by the device after it connects." /> +
+
+
Location
+
+ Please select the devices location. +
+
-
- No location information found. +
-
- Please check if the Geo extension has been included in your firmware. + +
+ <.button phx-click="discard-location-changes" phx-target={assigns[:target]}>Discard changes + <.button phx-click="save-location-changes" phx-target={assigns[:target]} style="primary">Save changes
""" end - # the IP address was a reserved IP (https://en.wikipedia.org/wiki/Reserved_IP_addresses) - # maps can't be shown for reserved IPs + # location information is nil or empty, geo location might not be enabled + def render(%{location: location} = assigns) + when is_nil(location) or map_size(location) == 0 do + ~H""" +
+
+
Location
+
The devices location is announced by the device after it connects.
+
+ +
+
+
+
No location information found.
+
Please check if the Geo extension has been included in your firmware.
+
+ <.button phx-click="enable-location-editor" phx-target={@target}>Manually set the location +
+
+
+
+ """ + end + + # TODO: is this an API we need to document in link? def render(%{location: %{"error_code" => _} = location}) do assigns = %{location: location} ~H""" -
- <.location_header tooltip="The devices location is announced by the device after it connects." /> - -
- An error occurred during location resolution : {@location["error_code"]} +
+
+
Location
+
The devices location is announced by the device after it connects.
-
- {@location["error_description"]} + +
+
+
+
An error occurred during location resolution : {@location["error_code"]}
+
{@location["error_description"]}
+
""" end - ### - # TODO: add support for marker and increased zoom when source is gps - ### - # yay, we have a location and map key, lets display a map def render(%{location: location} = assigns) do assigns = %{ lat: location["latitude"], lng: location["longitude"], source: location["source"], - zoom: 10, - size: "463x250", - mapbox_access_token: assigns.mapbox_access_token + zoom: if(String.downcase(location["source"]) == "gps", do: 15, else: 13), + mapbox_access_token: assigns.mapbox_access_token, + target: assigns[:target] } - ~H""" -
- <.location_header tooltip={"The devices location was determined via #{@source}"} /> + source_information = + case location["source"] do + "manual" -> "The location was manually configured." + _ -> "The location was determined via #{location["source"]} resolution." + end - -
- """ - end + assigns = Map.put(assigns, :source_information, source_information) - attr(:tooltip, :string) - - defp location_header(assigns) do ~H""" -
- Device location - - {@tooltip} +
+
+
Location
+
+ {@source_information} +
+
+ + + +
+
+ +
+
""" end diff --git a/lib/nerves_hub_web/components/device_page/console_tab.ex b/lib/nerves_hub_web/components/device_page/console_tab.ex index 71af75784..518093626 100644 --- a/lib/nerves_hub_web/components/device_page/console_tab.ex +++ b/lib/nerves_hub_web/components/device_page/console_tab.ex @@ -3,6 +3,7 @@ defmodule NervesHubWeb.Components.DevicePage.ConsoleTab do alias NervesHub.Tracker + alias Phoenix.LiveView.AsyncResult alias Phoenix.LiveView.JS def tab_params(_params, _uri, socket) do @@ -23,6 +24,12 @@ defmodule NervesHubWeb.Components.DevicePage.ConsoleTab do [:console_active?] end + def hooked_info(%Broadcast{event: "console_joined"}, socket) do + socket + |> assign(:console_active?, AsyncResult.ok(true)) + |> halt() + end + def hooked_info(%Broadcast{event: "file-data/start", payload: payload}, socket) do if socket.assigns.user.id == payload.uploaded_by do send_toast(socket, :info, "Upload started.") diff --git a/lib/nerves_hub_web/components/device_page/details_tab.ex b/lib/nerves_hub_web/components/device_page/details_tab.ex index 11b67fbd8..d7cc7b1f9 100644 --- a/lib/nerves_hub_web/components/device_page/details_tab.ex +++ b/lib/nerves_hub_web/components/device_page/details_tab.ex @@ -14,7 +14,7 @@ defmodule NervesHubWeb.Components.DevicePage.DetailsTab do alias NervesHub.Scripts alias NervesHubWeb.Components.HealthStatus - alias NervesHubWeb.Components.NewUI.DeviceLocation + alias NervesHubWeb.Components.DeviceLocation @keys_to_cleanup [ :support_scripts, @@ -524,6 +524,12 @@ defmodule NervesHubWeb.Components.DevicePage.DetailsTab do |> halt() end + def hooked_event("set-deployment-group", %{"deployment_id" => ""}, socket) do + socket + |> send_toast(:error, "Please select a deployment group.") + |> halt() + end + def hooked_event("set-deployment-group", %{"deployment_id" => deployment_id}, socket) do %{user: user, device: device, deployment_groups: deployment_groups} = socket.assigns @@ -636,6 +642,16 @@ defmodule NervesHubWeb.Components.DevicePage.DetailsTab do def hooked_event(_event, _params, socket), do: {:cont, socket} + def hooked_info(%Broadcast{event: "location:updated"}, socket) do + %{device: device, org: org} = socket.assigns + + {:ok, device} = Devices.get_device_by_identifier(org, device.identifier, :latest_connection) + + socket + |> assign(:device, device) + |> halt() + end + def hooked_info( %Broadcast{event: "health_check_report"}, %{assigns: %{device: device}} = socket diff --git a/lib/nerves_hub_web/components/device_page/health_tab.ex b/lib/nerves_hub_web/components/device_page/health_tab.ex index ba192644a..8993f1d1f 100644 --- a/lib/nerves_hub_web/components/device_page/health_tab.ex +++ b/lib/nerves_hub_web/components/device_page/health_tab.ex @@ -256,7 +256,7 @@ defmodule NervesHubWeb.Components.DevicePage.HealthTab do types(charts) != types(data) -> push_patch(socket, - to: ~p"/org/#{org.name}/#{product.name}/devices/#{device.identifier}/healthz" + to: ~p"/org/#{org.name}/#{product.name}/devices/#{device.identifier}/health" ) true -> diff --git a/lib/nerves_hub_web/components/new_ui/device_location.ex b/lib/nerves_hub_web/components/new_ui/device_location.ex deleted file mode 100644 index 4f9742bb2..000000000 --- a/lib/nerves_hub_web/components/new_ui/device_location.ex +++ /dev/null @@ -1,254 +0,0 @@ -defmodule NervesHubWeb.Components.NewUI.DeviceLocation do - use NervesHubWeb, :component - - alias Phoenix.LiveView.JS - - attr(:location, :any) - attr(:enabled_device, :any) - attr(:enabled_product, :any) - attr(:enable_location_editor, :boolean) - attr(:target, :any) - - # catch all to add the mapbox token - def render(assigns) when not is_map_key(assigns, :mapbox_access_token) do - token = Application.get_env(:nerves_hub, :mapbox_access_token) - - assigns - |> Map.put(:mapbox_access_token, token) - |> render() - end - - # mapbox token is nil, maps aren't enabled - def render(%{mapbox_access_token: nil} = assigns) do - ~H""" -
-
-
Location
-
- -
-
-
-
Device maps haven't been enabled on your platform.
-
Please contact your platform admin.
-
-
-
- """ - end - - # disabled in product settings - def render(%{enabled_product: false} = assigns) do - ~H""" -
-
-
Location
-
- -
-
-
-
Device maps have been disabled in your product settings.
-
To enable this feature, please contact your product admin.
-
-
-
- """ - end - - # disabled in device settings - def render(%{enabled_device: false} = assigns) do - ~H""" -
-
-
Location
-
- -
-
-
-
Device maps have been disabled for this device.
-
To enable this feature, please contact your product admin.
-
-
-
- """ - end - - # yay, we have a location and map key, lets display a map - def render(%{enable_location_editor: true} = assigns) do - ~H""" -
-
-
Location
-
- Please select the devices location. -
-
- -
-
- -
- <.button phx-click="discard-location-changes" phx-target={assigns[:target]}>Discard changes - <.button phx-click="save-location-changes" phx-target={assigns[:target]} style="primary">Save changes -
-
- """ - end - - # location information is nil or empty, geo location might not be enabled - def render(%{location: location} = assigns) - when is_nil(location) or map_size(location) == 0 do - ~H""" -
-
-
Location
-
The devices location is announced by the device after it connects.
-
- -
-
-
-
No location information found.
-
Please check if the Geo extension has been included in your firmware.
-
- <.button phx-click="enable-location-editor" phx-target={@target}>Manually set the location -
-
-
-
- """ - end - - # TODO: is this an API we need to document in link? - def render(%{location: %{"error_code" => _} = location}) do - assigns = %{location: location} - - ~H""" -
-
-
Location
-
The devices location is announced by the device after it connects.
-
- -
-
-
-
An error occurred during location resolution : {@location["error_code"]}
-
{@location["error_description"]}
-
-
-
- """ - end - - # yay, we have a location and map key, lets display a map - def render(%{location: location} = assigns) do - assigns = %{ - lat: location["latitude"], - lng: location["longitude"], - source: location["source"], - zoom: if(String.downcase(location["source"]) == "gps", do: 15, else: 13), - mapbox_access_token: assigns.mapbox_access_token, - target: assigns[:target] - } - - source_information = - case location["source"] do - "manual" -> "The location was manually configured." - _ -> "The location was determined via #{location["source"]} resolution." - end - - assigns = Map.put(assigns, :source_information, source_information) - - ~H""" -
-
-
Location
-
- {@source_information} -
-
- - - -
-
- -
-
-
- """ - end -end diff --git a/lib/nerves_hub_web/dynamic_template_renderer.ex b/lib/nerves_hub_web/dynamic_template_renderer.ex deleted file mode 100644 index 5fd9e12ab..000000000 --- a/lib/nerves_hub_web/dynamic_template_renderer.ex +++ /dev/null @@ -1,96 +0,0 @@ -defmodule NervesHubWeb.DynamicTemplateRenderer do - @moduledoc """ - Switches in a -new.html.heex template if `new_ui` has been enabled in `runtime.exs` - and the template exists. - """ - - require Logger - - defmacro __before_compile__(%{module: module, file: file} = env) do - render? = Module.defines?(module, {:render, 1}) - root = Path.dirname(file) - filename = template_filename(module) - templates = Phoenix.Template.find_all(root, filename) - - case {render?, templates} do - {true, [template | _]} -> - IO.warn( - "ignoring template #{inspect(template)} because the LiveView " <> - "#{inspect(env.module)} defines a render/1 function", - Macro.Env.stacktrace(env) - ) - - :ok - - {true, []} -> - :ok - - {false, [template]} -> - custom_multi_template_processing(template, filename, module, root) - - {false, [_ | _]} -> - IO.warn( - "multiple templates were found for #{inspect(env.module)}: #{inspect(templates)}", - Macro.Env.stacktrace(env) - ) - - :ok - - {false, []} -> - template = Path.join(root, filename <> ".heex") - - quote do - @external_resource unquote(template) - end - end - end - - defp custom_multi_template_processing(template, filename, module, root) do - ext = template |> Path.extname() |> String.trim_leading(".") |> String.to_atom() - engine = Map.fetch!(Phoenix.Template.engines(), ext) - ast = engine.compile(template, filename) - - new_filename = template_filename(module, "-new") - new_templates = Phoenix.Template.find_all(root, new_filename) - - case new_templates do - [new_template] -> - new_ext = - new_template |> Path.extname() |> String.trim_leading(".") |> String.to_atom() - - new_engine = Map.fetch!(Phoenix.Template.engines(), new_ext) - new_ast = new_engine.compile(new_template, filename) - - quote do - @file unquote(template) - @external_resource unquote(template) - @external_resource unquote(new_template) - def render(var!(assigns)) when is_map(var!(assigns)) do - if Application.get_env(:nerves_hub, :new_ui) && var!(assigns)[:new_ui] do - unquote(new_ast) - else - unquote(ast) - end - end - end - - _ -> - quote do - @file unquote(template) - @external_resource unquote(template) - def render(var!(assigns)) when is_map(var!(assigns)) do - unquote(ast) - end - end - end - end - - defp template_filename(module, suffix \\ "") do - module - |> Module.split() - |> List.last() - |> Macro.underscore() - |> Kernel.<>(suffix) - |> Kernel.<>(".html") - end -end diff --git a/lib/nerves_hub_web/live/archive_templates/list_archives_template.html.heex b/lib/nerves_hub_web/live/archive_templates/list_archives_template.html.heex index eacaea50e..6e48369cb 100644 --- a/lib/nerves_hub_web/live/archive_templates/list_archives_template.html.heex +++ b/lib/nerves_hub_web/live/archive_templates/list_archives_template.html.heex @@ -1,87 +1,134 @@ -<%= if Enum.empty?(@archives) do %> -
-

{@product.name} doesn’t have any archives yet

-
- <.link patch={~p"/org/#{@org.name}/#{@product.name}/archives/upload"} class="btn btn-outline-light" aria-label="Upload archive"> - - Upload Archive - +
+ + - - - - - - - - - - - - - - - - - - - - - -
UUIDVersionPlatformArchitectureArchive keyUploaded on
-
UUID
-
- <.link patch={~p"/org/#{@org.name}/#{@product.name}/archives/#{archive.uuid}"} class="badge ff-m">{archive.uuid} -
-
-
Version
- {archive.version} -
-
Platform
- {archive.platform} -
-
Architecture
- {archive.architecture} -
-
Archive key
-
- {format_signed(archive, @org_keys)} -
-
-
Uploaded on
-
- <%= if is_nil(archive.inserted_at) do %> - Never - <% else %> - {archive.inserted_at} - <% end %> -
-
-
Actions
- -
-<% end %> + diff --git a/lib/nerves_hub_web/live/archive_templates/list_archives_template_new.html.heex b/lib/nerves_hub_web/live/archive_templates/list_archives_template_new.html.heex deleted file mode 100644 index a427e360e..000000000 --- a/lib/nerves_hub_web/live/archive_templates/list_archives_template_new.html.heex +++ /dev/null @@ -1,147 +0,0 @@ -
- -
- - - - diff --git a/lib/nerves_hub_web/live/archive_templates/show_archive_template.html.heex b/lib/nerves_hub_web/live/archive_templates/show_archive_template.html.heex index e11463c2a..7aedc374a 100644 --- a/lib/nerves_hub_web/live/archive_templates/show_archive_template.html.heex +++ b/lib/nerves_hub_web/live/archive_templates/show_archive_template.html.heex @@ -1,60 +1,118 @@ -
- <.link patch={~p"/org/#{@org.name}/#{@product.name}/archives"} class="back-link"> - All Archives - -
- <.link class="btn btn-outline-light btn-action" aria-label="Download" href={~p"/org/#{@org.name}/#{@product.name}/archives/#{@archive.uuid}/download"} download> - - Download - - <.link class="btn btn-outline-light btn-action" aria-label="Delete" phx-click="delete-archive" data-confirm="Are you sure you want to delete this archive? This can not be undone."> - - Delete +
+
+ <.link navigate={~p"/org/#{@org.name}/#{@product.name}/archives"} class="back-link flex gap-2.5 items-center"> + + + + All Archives + / + {@archive.uuid}
-

Archive {@archive.version}

+