neiam
/lambent_ex
Lambent Aether 5
public · Issues · Pulls 1 · Labels · Forks · Compare · Actions success · Badges · Packages
⭐
Log in to mark this repository.
bulk link component
9b14fb6 · gmorell · 2023-01-17 00:01
Files changed
modified
lib/lambent_ex/schema/links.ex
+63
−0
@@ -43,3 +43,66 @@ defmodule LambentEx.Schema.Links do
| 43 | 43 | |> validate_required([:name, :source_id, :source_type, :target_id, :target_type]) |
| 44 | 44 | end |
| 45 | 45 | end |
| 46 | + | |
| 47 | +defmodule LambentEx.Schema.LinksB do | |
| 48 | + @moduledoc false | |
| 49 | + | |
| 50 | + use Ecto.Schema | |
| 51 | + import Ecto.Changeset | |
| 52 | + | |
| 53 | + alias LambentEx.Schema.LinksB | |
| 54 | + | |
| 55 | + embedded_schema do | |
| 56 | + field(:name, :string) | |
| 57 | + field(:source_id, :string) | |
| 58 | + field(:source_type, Ecto.Enum, values: [:machine, :anchor], default: :machine) | |
| 59 | + field(:target_ids, {:array, :string}) | |
| 60 | + field(:target_type, Ecto.Enum, values: [:device, :group], default: :device) | |
| 61 | + end | |
| 62 | + | |
| 63 | + def change_link(%LinksB{} = link, attrs \\ %{}) do | |
| 64 | + LinksB.changeset(link, attrs) | |
| 65 | + end | |
| 66 | + | |
| 67 | + def get_name(mac, devices) do | |
| 68 | + flatd = devices | |
| 69 | + |> Enum.map(fn {iface, entries} -> | |
| 70 | + entries | |
| 71 | + end) | |
| 72 | + |> List.flatten() |> List.first | |
| 73 | + | |
| 74 | + case flatd |> Map.get(mac) do | |
| 75 | + nil -> mac | |
| 76 | + value -> value["name"] || mac | |
| 77 | + end | |
| 78 | + end | |
| 79 | + | |
| 80 | + def update_links(%LinksB{} = link, attrs, devices) do | |
| 81 | + res = | |
| 82 | + link | |
| 83 | + |> LinksB.changeset(attrs) | |
| 84 | + | |
| 85 | + case res.valid? do | |
| 86 | + true -> | |
| 87 | + for id <- res.changes[:target_ids] do | |
| 88 | + LambentEx.LinkSupervisor.start_child( | |
| 89 | + "#{res.changes[:name]}|#{id |> get_name(devices)}", | |
| 90 | + res.changes[:source_id], | |
| 91 | + id | |
| 92 | + ) | |
| 93 | + end | |
| 94 | + | |
| 95 | + {:ok, :ok} | |
| 96 | + | |
| 97 | + false -> | |
| 98 | + IO.inspect(res) | |
| 99 | + {:error, res} | |
| 100 | + end | |
| 101 | + end | |
| 102 | + | |
| 103 | + def changeset(source, params) do | |
| 104 | + source | |
| 105 | + |> cast(params, ~w(name source_id source_type target_ids target_type)a) | |
| 106 | + |> validate_required([:name, :source_id, :source_type, :target_ids, :target_type]) | |
| 107 | + end | |
| 108 | +end |
modified
lib/lambent_ex_web.ex
+1
−0
@@ -84,6 +84,7 @@ defmodule LambentExWeb do
| 84 | 84 | quote do |
| 85 | 85 | # HTML escaping functionality |
| 86 | 86 | import Phoenix.HTML |
| 87 | + import Phoenix.HTML.Form | |
| 87 | 88 | # Core UI components and translation |
| 88 | 89 | import LambentExWeb.CoreComponents |
| 89 | 90 | import LambentExWeb.Gettext |
added
lib/lambent_ex_web/live/links/bulk_form_component.ex
+100
−0
@@ -0,0 +1,100 @@
| 1 | +defmodule LambentEx.LinksLive.BulkFormComponent do | |
| 2 | + use LambentExWeb, :live_component | |
| 3 | + | |
| 4 | + alias LambentEx.Schema.LinksB | |
| 5 | + alias LambentEx.Names | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + @impl true | |
| 10 | + def update(%{link: link} = assigns, socket) do | |
| 11 | + changeset = LinksB.change_link(link) | |
| 12 | + | |
| 13 | + {:ok, | |
| 14 | + socket | |
| 15 | + |> assign(assigns) | |
| 16 | + |> assign(:changeset, changeset)} | |
| 17 | + end | |
| 18 | + | |
| 19 | + @impl true | |
| 20 | + def handle_event("validate", %{"links_b" => links_params}, socket) do | |
| 21 | + changeset = | |
| 22 | + socket.assigns.link | |
| 23 | + |> LinksB.change_link(links_params) | |
| 24 | + |> Map.put(:action, :validate) | |
| 25 | + | |
| 26 | + {:noreply, assign(socket, :changeset, changeset)} | |
| 27 | + end | |
| 28 | + | |
| 29 | + def handle_event("save", %{"links_b" => links_params}, socket) do | |
| 30 | + case LinksB.update_links(socket.assigns.link, links_params, socket.assigns.devices) do | |
| 31 | + {:ok, _link} -> | |
| 32 | + {:noreply, | |
| 33 | + socket | |
| 34 | + |> put_flash(:info, "Link Created Successfully") | |
| 35 | + |> push_navigate(to: socket.assigns.navigate)} | |
| 36 | + | |
| 37 | + {:error, %Ecto.Changeset{} = changeset} -> | |
| 38 | + {:noreply, assign(socket, :changeset, changeset)} | |
| 39 | + end | |
| 40 | + | |
| 41 | + # save_testyyyy(socket, socket.assigns.action, testyyyy_params) | |
| 42 | + # :ok | |
| 43 | + end | |
| 44 | + | |
| 45 | + def handle_event("new-name", _params, socket) do | |
| 46 | + changeset = socket.assigns.changeset |> Ecto.Changeset.change(name: Names.name()) | |
| 47 | + {:noreply, assign(socket, :changeset, changeset)} | |
| 48 | + end | |
| 49 | + | |
| 50 | + def for_selectm(array) do | |
| 51 | + array |> Enum.map(fn {k, v} -> {v[:id], v[:name]} end) | |
| 52 | + end | |
| 53 | + | |
| 54 | + defp ipp(ip), do: ip |> Tuple.to_list |> Enum.join(".") | |
| 55 | + | |
| 56 | + | |
| 57 | + def for_selectd(array) do | |
| 58 | + array | |
| 59 | + |> Enum.map(fn {iface, entries} -> | |
| 60 | + entries | |
| 61 | + |> Enum.map(fn {k, v} -> | |
| 62 | + if v["name"] == nil do | |
| 63 | + {v["ip"] |> ipp, k} | |
| 64 | + else | |
| 65 | + {v["name"], k} | |
| 66 | + end | |
| 67 | + | |
| 68 | + end) | |
| 69 | + end) | |
| 70 | + |> List.flatten() | |
| 71 | + |> Enum.sort_by(fn {k, v} -> k end) | |
| 72 | +# |> IO.inspect | |
| 73 | + end | |
| 74 | + | |
| 75 | +# def for_selectd(array) do | |
| 76 | +# array | |
| 77 | +# |> Enum.map(fn {iface, entries} -> | |
| 78 | +# entries | |
| 79 | +# |> Enum.map(fn {k, v} -> | |
| 80 | +# if v["name"] == nil do | |
| 81 | +# %{label: v["ip"] |> ipp, id: k, selected: false} | |
| 82 | +# else | |
| 83 | +# %{label: v["name"], id: k, selected: false} | |
| 84 | +# end | |
| 85 | +# | |
| 86 | +# end) | |
| 87 | +# end) | |
| 88 | +## |> List.flatten() | |
| 89 | +## |> Enum.sort_by(fn {k, v} -> k end) | |
| 90 | +# |> IO.inspect | |
| 91 | +# end | |
| 92 | + | |
| 93 | + # <.live_component | |
| 94 | + # id="target_ids" | |
| 95 | + # module={LambentExWeb.MultiSelectComponent} | |
| 96 | + # options={for_selectd(@devices)} | |
| 97 | + # form={f} | |
| 98 | + # selected={fn opts -> send(self(), {:updated_bulk_options, opts}) end} | |
| 99 | + # /> | |
| 100 | +end |
added
lib/lambent_ex_web/live/links/bulk_form_component.html.heex
+33
−0
@@ -0,0 +1,33 @@
| 1 | +<div> | |
| 2 | + <h1 class="text-3xl font-bold">New Link</h1> | |
| 3 | + <.simple_form | |
| 4 | + :let={f} | |
| 5 | + for={@changeset} | |
| 6 | + id="links-form" | |
| 7 | + phx-target={@myself} | |
| 8 | + phx-change="validate" | |
| 9 | + phx-submit="save" | |
| 10 | + > | |
| 11 | + <.input field={{f, :name}} type="text" label="Name"> | |
| 12 | + <:icon_pre> | |
| 13 | + <btn class="btn btn-accent" phx-click="new-name" phx-target={@myself}> | |
| 14 | + <i class="fa-solid fa-arrows-rotate"></i> | |
| 15 | + </btn> | |
| 16 | + </:icon_pre> | |
| 17 | + </.input> | |
| 18 | + <.input | |
| 19 | + field={{f, :source_id}} | |
| 20 | + type="select" | |
| 21 | + label="Source" | |
| 22 | + options={@machines |> for_selectm} | |
| 23 | + /> | |
| 24 | + <label>Targets</label> <br /> | |
| 25 | + <%= Phoenix.HTML.Form.multiple_select(f, :target_ids, for_selectd(@devices), class: "h-80") %> | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + <:actions> | |
| 30 | + <.button phx-disable-with="Saving...">Create Link</.button> | |
| 31 | + </:actions> | |
| 32 | + </.simple_form> | |
| 33 | +</div> |
modified
lib/lambent_ex_web/live/links/index.ex
+9
−0
@@ -47,6 +47,15 @@ defmodule LambentExWeb.LinksLive.Index do
| 47 | 47 | |> assign(:link, %LambentEx.Schema.Links{}) |
| 48 | 48 | end |
| 49 | 49 | |
| 50 | + defp apply_action(socket, :bulk, _params) do | |
| 51 | + socket | |
| 52 | + |> assign(:id, :bulk) | |
| 53 | + |> assign(:page_link, "Links") | |
| 54 | + |> assign(:page_title, "Bulk Links") | |
| 55 | + |> assign(:link, %LambentEx.Schema.LinksB{}) | |
| 56 | + end | |
| 57 | + | |
| 58 | + | |
| 50 | 59 | def handle_info({:machines_pub, machine}, socket) do |
| 51 | 60 | {:noreply, |
| 52 | 61 | socket |> assign(:machines, socket.assigns.machines |> Map.put(machine[:name], machine))} |
modified
lib/lambent_ex_web/live/links/index.html.heex
+15
−2
@@ -1,13 +1,14 @@
| 1 | 1 | <div class="overflow-x-auto"> |
| 2 | − <%= if @live_action in [:new, :edit] do %> | |
| 2 | + <%= if @live_action in [:new, :edit, :bulk] do %> | |
| 3 | 3 | <.modal |
| 4 | − :if={@live_action in [:new, :edit]} | |
| 4 | + :if={@live_action in [:new, :edit, :bulk]} | |
| 5 | 5 | return_to={~p"/cfg/links"} |
| 6 | 6 | id="new" |
| 7 | 7 | show |
| 8 | 8 | on_cancel={JS.navigate(~p"/cfg/links")} |
| 9 | 9 | > |
| 10 | 10 | <.live_component |
| 11 | + :if={@live_action in [:new, :edit]} | |
| 11 | 12 | module={LambentEx.LinksLive.FormComponent} |
| 12 | 13 | id={:new} |
| 13 | 14 | title={@page_title} |
@@ -18,6 +19,18 @@
| 18 | 19 | devices={@devices} |
| 19 | 20 | navigate={~p"/cfg/links"} |
| 20 | 21 | /> |
| 22 | + <.live_component | |
| 23 | + :if={@live_action in [:bulk]} | |
| 24 | + module={LambentEx.LinksLive.BulkFormComponent} | |
| 25 | + id={:bulk} | |
| 26 | + title={@page_title} | |
| 27 | + action={@live_action} | |
| 28 | + link={@link} | |
| 29 | + return_to={~p"/cfg/links"} | |
| 30 | + machines={@machines} | |
| 31 | + devices={@devices} | |
| 32 | + navigate={~p"/cfg/links"} | |
| 33 | + /> | |
| 21 | 34 | </.modal> |
| 22 | 35 | <% end %> |
| 23 | 36 | <table class="table table-zebra w-full"> |
added
lib/lambent_ex_web/live/vendored_components.ex
+68
−0
@@ -0,0 +1,68 @@
| 1 | +defmodule LambentExWeb.MultiSelectComponent do | |
| 2 | + # PhoenixFilesWeb.MultiSelectComponent | |
| 3 | + use LambentExWeb, :live_component | |
| 4 | + | |
| 5 | + def render(assigns) do | |
| 6 | + ~H""" | |
| 7 | + <div class="flex justify-center"> | |
| 8 | + <div class="border border-gray-200 dark:border-gray-700 w-96 h-12 pb-2 m-2 w-96 flex relative" id={"selected-options-container-#{@id}"}> | |
| 9 | + <%= for option <- @selected_options do %> | |
| 10 | + <div id={"option_#{option.label}"} class="bg-purple-500 shadow-lg rounded-lg mt-2 ml-1 text-white dark:bg-sky-500 inline-block pl-2 pr-2 text-center"> | |
| 11 | + <%= option.label %> | |
| 12 | + </div> | |
| 13 | + <% end %> | |
| 14 | + <div class="absolute right-0"> | |
| 15 | + <svg id={"#{@id}-down-icon"} xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 absolute right-0 top-3" viewBox="0 0 20 20" fill="currentColor" phx-click={JS.toggle() |> JS.toggle(to: "##{@id}-up-icon") |> JS.toggle(to: "#options-container-#{@id}")}> | |
| 16 | + <path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd" /> | |
| 17 | + </svg> | |
| 18 | + <svg id={"#{@id}-up-icon"} xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 absolute right-0 top-3 hidden" viewBox="0 0 20 20" fill="currentColor" phx-click={JS.toggle() |> JS.toggle(to: "##{@id}-down-icon") |> JS.toggle(to: "#options-container-#{@id}")}> | |
| 19 | + <path fill-rule="evenodd" d="M14.707 12.707a1 1 0 01-1.414 0L10 9.414l-3.293 3.293a1 1 0 01-1.414-1.414l4-4a1 1 0 011.414 0l4 4a1 1 0 010 1.414z" clip-rule="evenodd" /> | |
| 20 | + </svg> | |
| 21 | + </div> | |
| 22 | + </div> | |
| 23 | + <div class="hidden w-96 mt-4 p-4 ml-2 mt-16 absolute z-10 bg-stone-50 shadow-2xl rounded-lg" id={"options-container-#{@id}"}> | |
| 24 | + <%= inputs_for @form, :options, fn value -> %> | |
| 25 | + <div class="form-check"> | |
| 26 | + <label class="form-check-label inline-block text-gray-800"> | |
| 27 | + <%= checkbox value, :selected, phx_change: "checked", phx_target: @myself, value: value.data.selected, class: "form-check-input appearance-none h-4 w-4 border border-gray-300 rounded-sm bg-white checked:bg-blue-600 checked:border-blue-600 focus:outline-none transition duration-200 mt-1 align-top bg-no-repeat bg-center bg-contain float-left mr-2 cursor-pointer" %> | |
| 28 | + <%= label value, :label, value.data.label, class: "ml-2" %> | |
| 29 | + <%= hidden_input value, :label, value: value.data.label %> | |
| 30 | + </label> | |
| 31 | + </div> | |
| 32 | + <% end %> | |
| 33 | + </div> | |
| 34 | + </div> | |
| 35 | + """ | |
| 36 | + end | |
| 37 | + | |
| 38 | + def update(params, socket) do | |
| 39 | + %{options: options, form: form, selected: selected, id: id} = params | |
| 40 | + | |
| 41 | + socket = | |
| 42 | + socket | |
| 43 | + |> assign(:id, id) | |
| 44 | + |> assign(:selected_options, filter_selected_options(options)) | |
| 45 | + |> assign(:options, options) | |
| 46 | + |> assign(:form, form) | |
| 47 | + |> assign(:selected, selected) | |
| 48 | + | |
| 49 | + {:ok, socket} | |
| 50 | + end | |
| 51 | + | |
| 52 | + def handle_event("checked", %{"multi_select" => %{"options" => values}}, socket) do | |
| 53 | + [{index, %{"selected" => selected?}}] = Map.to_list(values) | |
| 54 | + index = String.to_integer(index) | |
| 55 | + current_option = Enum.at(socket.assigns.options, index) | |
| 56 | + | |
| 57 | + updated_options = | |
| 58 | + List.replace_at(socket.assigns.options, index, %{current_option | selected: selected?}) | |
| 59 | + | |
| 60 | + socket.assigns.selected.(updated_options) | |
| 61 | + | |
| 62 | + {:noreply, socket} | |
| 63 | + end | |
| 64 | + | |
| 65 | + defp filter_selected_options(options) do | |
| 66 | + Enum.filter(options, fn opt -> opt.selected == true or opt.selected == "true" end) | |
| 67 | + end | |
| 68 | +end |
modified
lib/lambent_ex_web/router.ex
+1
−0
@@ -24,6 +24,7 @@ defmodule LambentExWeb.Router do
| 24 | 24 | live "/cfg/machines/new", MachinesLive.Index, :new |
| 25 | 25 | live "/cfg/links", LinksLive.Index, :index |
| 26 | 26 | live "/cfg/links/new", LinksLive.Index, :new |
| 27 | + live "/cfg/links/bulk", LinksLive.Index, :bulk | |
| 27 | 28 | # live "/cfg/devices/:id", DevicesLive.Show, :show |
| 28 | 29 | # live "/cfg/devices/:id/show/edit", DevicesLive.Show, :edit |
| 29 | 30 | end |
Parents: 291233b