bulk link component

9b14fb6 · gmorell · 2023-01-17 00:01

14 files +290 -2

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