2022-08-18 21:34:27 +08:00
|
|
|
defmodule LivebookWeb.HubLive.FlyComponent do
|
|
|
|
use LivebookWeb, :live_component
|
|
|
|
|
2022-08-23 05:12:54 +08:00
|
|
|
import Ecto.Changeset, only: [get_field: 2, add_error: 3]
|
|
|
|
|
|
|
|
alias Livebook.EctoTypes.HexColor
|
2022-08-18 21:34:27 +08:00
|
|
|
alias Livebook.Hubs.{Fly, FlyClient}
|
|
|
|
|
|
|
|
@impl true
|
|
|
|
def update(assigns, socket) do
|
|
|
|
{:ok,
|
|
|
|
socket
|
|
|
|
|> assign(assigns)
|
|
|
|
|> load_data()}
|
|
|
|
end
|
|
|
|
|
|
|
|
@impl true
|
|
|
|
def render(assigns) do
|
|
|
|
~H"""
|
|
|
|
<div>
|
|
|
|
<.form
|
|
|
|
id={@id}
|
|
|
|
class="flex flex-col space-y-4"
|
|
|
|
let={f}
|
2022-08-23 05:12:54 +08:00
|
|
|
for={@changeset}
|
|
|
|
phx-submit="save"
|
|
|
|
phx-change="validate"
|
2022-08-18 21:34:27 +08:00
|
|
|
phx-target={@myself}
|
|
|
|
phx-debounce="blur"
|
|
|
|
>
|
|
|
|
<div class="flex flex-col space-y-1">
|
|
|
|
<h3 class="text-gray-800 font-semibold">
|
|
|
|
Access Token
|
|
|
|
</h3>
|
|
|
|
<%= password_input(f, :access_token,
|
|
|
|
phx_change: "fetch_data",
|
|
|
|
phx_debounce: "blur",
|
|
|
|
phx_target: @myself,
|
2022-08-23 05:12:54 +08:00
|
|
|
value: access_token(@changeset),
|
2022-08-18 21:34:27 +08:00
|
|
|
disabled: @operation == :edit,
|
|
|
|
class: "input w-full",
|
|
|
|
autofocus: true,
|
|
|
|
spellcheck: "false",
|
|
|
|
autocomplete: "off"
|
|
|
|
) %>
|
2022-08-23 05:12:54 +08:00
|
|
|
<%= error_tag(f, :access_token) %>
|
2022-08-18 21:34:27 +08:00
|
|
|
</div>
|
|
|
|
|
|
|
|
<%= if length(@apps) > 0 do %>
|
|
|
|
<div class="flex flex-col space-y-1">
|
|
|
|
<h3 class="text-gray-800 font-semibold">
|
|
|
|
Application
|
|
|
|
</h3>
|
|
|
|
<%= select(f, :application_id, @select_options,
|
|
|
|
class: "input",
|
|
|
|
disabled: @operation == :edit
|
|
|
|
) %>
|
2022-08-23 05:12:54 +08:00
|
|
|
<%= error_tag(f, :application_id) %>
|
2022-08-18 21:34:27 +08:00
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-3">
|
|
|
|
<div class="flex flex-col space-y-1">
|
|
|
|
<h3 class="text-gray-800 font-semibold">
|
|
|
|
Name
|
|
|
|
</h3>
|
2022-08-23 05:12:54 +08:00
|
|
|
<%= text_input(f, :hub_name, class: "input") %>
|
|
|
|
<%= error_tag(f, :hub_name) %>
|
2022-08-18 21:34:27 +08:00
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="flex flex-col space-y-1">
|
|
|
|
<h3 class="text-gray-800 font-semibold">
|
|
|
|
Color
|
|
|
|
</h3>
|
|
|
|
|
|
|
|
<div class="flex space-x-4 items-center">
|
|
|
|
<div
|
|
|
|
class="border-[3px] rounded-lg p-1 flex justify-center items-center"
|
2022-08-23 05:12:54 +08:00
|
|
|
style={"border-color: #{hub_color(@changeset)}"}
|
2022-08-18 21:34:27 +08:00
|
|
|
>
|
2022-08-23 05:12:54 +08:00
|
|
|
<div class="rounded h-5 w-5" style={"background-color: #{hub_color(@changeset)}"} />
|
2022-08-18 21:34:27 +08:00
|
|
|
</div>
|
|
|
|
<div class="relative grow">
|
|
|
|
<%= text_input(f, :hub_color,
|
|
|
|
class: "input",
|
|
|
|
spellcheck: "false",
|
|
|
|
maxlength: 7
|
|
|
|
) %>
|
|
|
|
<button
|
|
|
|
class="icon-button absolute right-2 top-1"
|
|
|
|
type="button"
|
|
|
|
phx-click="randomize_color"
|
|
|
|
phx-target={@myself}
|
|
|
|
>
|
|
|
|
<.remix_icon icon="refresh-line" class="text-xl" />
|
|
|
|
</button>
|
2022-08-23 05:12:54 +08:00
|
|
|
<%= error_tag(f, :hub_color) %>
|
2022-08-18 21:34:27 +08:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
2022-08-23 05:12:54 +08:00
|
|
|
<%= submit("Save",
|
|
|
|
class: "button-base button-blue",
|
|
|
|
phx_disable_with: "Saving...",
|
|
|
|
disabled: not @valid?
|
|
|
|
) %>
|
2022-08-18 21:34:27 +08:00
|
|
|
<% end %>
|
|
|
|
</.form>
|
|
|
|
</div>
|
|
|
|
"""
|
|
|
|
end
|
|
|
|
|
|
|
|
defp load_data(%{assigns: %{operation: :new}} = socket) do
|
2022-08-23 05:12:54 +08:00
|
|
|
assign(socket,
|
|
|
|
changeset: Fly.change_hub(%Fly{}),
|
|
|
|
selected_app: nil,
|
|
|
|
select_options: [],
|
|
|
|
apps: [],
|
|
|
|
valid?: false
|
|
|
|
)
|
2022-08-18 21:34:27 +08:00
|
|
|
end
|
|
|
|
|
2022-08-23 05:12:54 +08:00
|
|
|
defp load_data(%{assigns: %{operation: :edit, hub: hub}} = socket) do
|
|
|
|
{:ok, apps} = FlyClient.fetch_apps(hub.access_token)
|
|
|
|
params = Map.from_struct(hub)
|
2022-08-18 21:34:27 +08:00
|
|
|
|
2022-08-23 05:12:54 +08:00
|
|
|
assign(socket,
|
|
|
|
changeset: Fly.change_hub(hub, params),
|
|
|
|
selected_app: hub,
|
|
|
|
select_options: select_options(apps),
|
|
|
|
apps: apps,
|
|
|
|
valid?: true
|
|
|
|
)
|
2022-08-18 21:34:27 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
@impl true
|
|
|
|
def handle_event("fetch_data", %{"fly" => %{"access_token" => token}}, socket) do
|
|
|
|
case FlyClient.fetch_apps(token) do
|
|
|
|
{:ok, apps} ->
|
|
|
|
opts = select_options(apps)
|
|
|
|
|
2022-08-23 05:12:54 +08:00
|
|
|
changeset =
|
|
|
|
socket.assigns.changeset
|
|
|
|
|> Fly.changeset(%{access_token: token, hub_color: HexColor.random()})
|
|
|
|
|> clean_errors()
|
|
|
|
|
|
|
|
{:noreply,
|
|
|
|
assign(socket,
|
|
|
|
changeset: changeset,
|
|
|
|
valid?: changeset.valid?,
|
|
|
|
select_options: opts,
|
|
|
|
apps: apps
|
|
|
|
)}
|
2022-08-18 21:34:27 +08:00
|
|
|
|
|
|
|
{:error, _} ->
|
2022-08-23 05:12:54 +08:00
|
|
|
changeset =
|
|
|
|
socket.assigns.changeset
|
|
|
|
|> Fly.changeset()
|
|
|
|
|> clean_errors()
|
|
|
|
|> put_action()
|
|
|
|
|> add_error(:access_token, "is invalid")
|
|
|
|
|
|
|
|
send(self(), {:flash, :error, "Failed to fetch Applications"})
|
|
|
|
|
|
|
|
{:noreply,
|
|
|
|
assign(socket,
|
|
|
|
changeset: changeset,
|
|
|
|
valid?: changeset.valid?,
|
|
|
|
select_options: [],
|
|
|
|
apps: []
|
|
|
|
)}
|
2022-08-18 21:34:27 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def handle_event("randomize_color", _, socket) do
|
2022-08-23 05:12:54 +08:00
|
|
|
changeset =
|
|
|
|
socket.assigns.changeset
|
|
|
|
|> clean_errors()
|
|
|
|
|> Fly.change_hub(%{hub_color: HexColor.random()})
|
|
|
|
|> put_action()
|
2022-08-18 21:34:27 +08:00
|
|
|
|
2022-08-23 05:12:54 +08:00
|
|
|
{:noreply, assign(socket, changeset: changeset, valid?: changeset.valid?)}
|
|
|
|
end
|
2022-08-18 21:34:27 +08:00
|
|
|
|
2022-08-23 05:12:54 +08:00
|
|
|
def handle_event("save", %{"fly" => params}, socket) do
|
|
|
|
{:noreply, save_fly(socket, socket.assigns.operation, params)}
|
2022-08-18 21:34:27 +08:00
|
|
|
end
|
|
|
|
|
2022-08-23 05:12:54 +08:00
|
|
|
def handle_event("validate", %{"fly" => attrs}, socket) do
|
|
|
|
params = Map.merge(socket.assigns.changeset.params, attrs)
|
2022-08-18 21:34:27 +08:00
|
|
|
|
2022-08-23 05:12:54 +08:00
|
|
|
application_id = params["application_id"]
|
|
|
|
selected_app = Enum.find(socket.assigns.apps, &(&1.application_id == application_id))
|
|
|
|
opts = select_options(socket.assigns.apps, application_id)
|
2022-08-18 21:34:27 +08:00
|
|
|
|
2022-08-23 05:12:54 +08:00
|
|
|
changeset =
|
|
|
|
if selected_app do
|
|
|
|
Fly.change_hub(selected_app, params)
|
2022-08-18 21:34:27 +08:00
|
|
|
else
|
2022-08-23 05:12:54 +08:00
|
|
|
Fly.changeset(socket.assigns.changeset, params)
|
2022-08-18 21:34:27 +08:00
|
|
|
end
|
|
|
|
|
2022-08-23 05:12:54 +08:00
|
|
|
{:noreply,
|
|
|
|
assign(socket,
|
|
|
|
changeset: changeset,
|
|
|
|
valid?: changeset.valid?,
|
|
|
|
selected_app: selected_app,
|
|
|
|
select_options: opts
|
|
|
|
)}
|
2022-08-18 21:34:27 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
defp select_options(hubs, app_id \\ nil) do
|
|
|
|
disabled_option = [key: "Select one application", value: "", selected: true, disabled: true]
|
|
|
|
|
|
|
|
options =
|
|
|
|
for fly <- hubs do
|
|
|
|
[
|
|
|
|
key: "#{fly.organization_name} - #{fly.application_id}",
|
|
|
|
value: fly.application_id,
|
|
|
|
selected: fly.application_id == app_id
|
|
|
|
]
|
|
|
|
end
|
|
|
|
|
2022-08-23 05:12:54 +08:00
|
|
|
[disabled_option] ++ options
|
2022-08-18 21:34:27 +08:00
|
|
|
end
|
|
|
|
|
2022-08-23 05:12:54 +08:00
|
|
|
defp save_fly(socket, :new, params) do
|
|
|
|
case Fly.create_hub(socket.assigns.selected_app, params) do
|
|
|
|
{:ok, fly} ->
|
|
|
|
changeset =
|
|
|
|
fly
|
|
|
|
|> Fly.change_hub(params)
|
|
|
|
|> put_action()
|
|
|
|
|
|
|
|
socket
|
|
|
|
|> assign(changeset: changeset, valid?: changeset.valid?)
|
|
|
|
|> put_flash(:success, "Hub created successfully")
|
|
|
|
|> push_redirect(to: Routes.hub_path(socket, :edit, fly.id))
|
|
|
|
|
|
|
|
{:error, changeset} ->
|
|
|
|
assign(socket, changeset: put_action(changeset), valid?: changeset.valid?)
|
2022-08-18 21:34:27 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-08-23 05:12:54 +08:00
|
|
|
defp save_fly(socket, :edit, params) do
|
|
|
|
id = socket.assigns.selected_app.id
|
2022-08-18 21:34:27 +08:00
|
|
|
|
2022-08-23 05:12:54 +08:00
|
|
|
case Fly.update_hub(socket.assigns.selected_app, params) do
|
|
|
|
{:ok, fly} ->
|
|
|
|
changeset =
|
|
|
|
fly
|
|
|
|
|> Fly.change_hub(params)
|
|
|
|
|> put_action()
|
|
|
|
|
|
|
|
socket
|
|
|
|
|> assign(changeset: changeset, selected_app: fly, valid?: changeset.valid?)
|
|
|
|
|> put_flash(:success, "Hub updated successfully")
|
|
|
|
|> push_redirect(to: Routes.hub_path(socket, :edit, id))
|
|
|
|
|
|
|
|
{:error, changeset} ->
|
|
|
|
assign(socket, changeset: changeset, valid?: changeset.valid?)
|
|
|
|
end
|
2022-08-18 21:34:27 +08:00
|
|
|
end
|
2022-08-23 05:12:54 +08:00
|
|
|
|
|
|
|
defp clean_errors(changeset), do: %{changeset | errors: []}
|
|
|
|
defp put_action(changeset, action \\ :validate), do: %{changeset | action: action}
|
|
|
|
|
|
|
|
defp hub_color(changeset), do: get_field(changeset, :hub_color)
|
|
|
|
defp access_token(changeset), do: get_field(changeset, :access_token)
|
2022-08-18 21:34:27 +08:00
|
|
|
end
|