diff --git a/lib/atomic_web/components/multi_select.ex b/lib/atomic_web/components/multi_select.ex index 0ea7efa67..97e19960e 100644 --- a/lib/atomic_web/components/multi_select.ex +++ b/lib/atomic_web/components/multi_select.ex @@ -7,7 +7,7 @@ defmodule AtomicWeb.Components.MultiSelect do @target - the target to send the event to The component events are: - toggle_option - toggles the selected state of an item. This event should be defined in the component that you passed in the @target attribute. + toggle-option - toggles the selected state of an item. This event should be defined in the component that you passed in the @target attribute. """ use AtomicWeb, :live_component @@ -56,7 +56,7 @@ defmodule AtomicWeb.Components.MultiSelect do style="display: none;" > <%= for item <- @items do %> -
  • +
  • <%= item.label %> diff --git a/lib/atomic_web/live/activity_live/form_component.ex b/lib/atomic_web/live/activity_live/form_component.ex index ee4d30a89..2f606e534 100644 --- a/lib/atomic_web/live/activity_live/form_component.ex +++ b/lib/atomic_web/live/activity_live/form_component.ex @@ -1,10 +1,146 @@ defmodule AtomicWeb.ActivityLive.FormComponent do use AtomicWeb, :live_component + alias Atomic.Activities + alias Atomic.Uploader + alias AtomicWeb.Components.{ImageUploader, MultiSelect} + import AtomicWeb.Components.Field @impl true - def mount(socket) do - {:ok, socket} + def update(%{activity: activity} = assigns, socket) do + changeset = Activities.change_activity(activity) + speakers = list_speakers(assigns) + + {:ok, + socket + |> assign(assigns) + |> assign_form(changeset) + |> assign(:speakers, speakers) + |> assign(:selected_speakers, Enum.count(speakers, & &1.selected)) + |> allow_upload(:image, accept: Uploader.extensions_whitelist(), max_entries: 1)} + end + + @impl true + def handle_event("validate", %{"activity" => activity_params}, socket) do + changeset = + socket.assigns.activity + |> Activities.change_activity(activity_params) + |> Map.put(:action, :validate) + + {:noreply, assign_form(socket, changeset)} + end + + @impl true + def handle_event("save", %{"activity" => activity_params}, socket) do + speakers = + List.foldl(socket.assigns.speakers, [], fn speaker, acc -> + if speaker.selected do + [speaker.id | acc] + else + acc + end + end) + + activity_params = + activity_params + |> Map.put("speakers", speakers) + |> Map.put("organization_id", socket.assigns.current_organization.id) + + save_activity(socket, socket.assigns.action, activity_params) + end + + @impl true + def handle_event("toggle-option", %{"id" => id}, socket) do + updated_speakers = + Enum.map(socket.assigns.speakers, fn option -> + if option.id == id do + %{option | selected: !option.selected} + else + option + end + end) + + {:noreply, + socket + |> assign(:speakers, updated_speakers) + |> assign(:selected_speakers, Enum.count(updated_speakers, & &1.selected))} + end + + @impl true + def handle_event("cancel-image", %{"ref" => ref}, socket) do + {:noreply, cancel_upload(socket, :image, ref)} + end + + defp save_activity(socket, :new, activity_params) do + # TODO: Let user choose whether to create a post + case Activities.create_activity_with_post(activity_params, &consume_image_data(socket, &1)) do + {:ok, _activity} -> + {:noreply, + socket + |> put_flash(:info, "Activity created successfully!") + |> push_navigate(to: socket.assigns.return_to)} + + {:error, %Ecto.Changeset{} = changeset} -> + {:noreply, assign_form(socket, changeset)} + end + end + + defp save_activity(socket, :edit, activity_params) do + case Activities.update_activity( + socket.assigns.activity, + activity_params, + &consume_image_data(socket, &1) + ) do + {:ok, _activity} -> + {:noreply, + socket + |> put_flash(:info, "Activity updated successfully!") + |> push_navigate(to: socket.assigns.return_to)} + + {:error, %Ecto.Changeset{} = changeset} -> + {:noreply, assign_form(socket, changeset)} + end + end + + defp consume_image_data(socket, activity) do + consume_uploaded_entries(socket, :image, fn %{path: path}, entry -> + Activities.update_activity_image(activity, %{ + "image" => %Plug.Upload{ + content_type: entry.content_type, + filename: entry.client_name, + path: path + } + }) + end) + |> case do + [{:ok, activity}] -> + {:ok, activity} + + _errors -> + {:ok, activity} + end + end + + defp list_speakers(assigns) do + activity = assigns.activity + + organization_speakers = + Activities.list_speakers_by_organization_id(assigns.current_organization.id) + + Enum.map(organization_speakers, fn s -> + selected = + if(is_list(activity.speakers), do: Enum.member?(activity.speakers, s), else: false) + + %{ + id: s.id, + label: s.name, + selected: selected + } + end) + end + + defp assign_form(socket, %Ecto.Changeset{} = changeset) do + assign(socket, :form, to_form(changeset)) end end diff --git a/lib/atomic_web/live/activity_live/form_component.html.heex b/lib/atomic_web/live/activity_live/form_component.html.heex index 7d8b785db..d11bf3be9 100644 --- a/lib/atomic_web/live/activity_live/form_component.html.heex +++ b/lib/atomic_web/live/activity_live/form_component.html.heex @@ -1,32 +1,65 @@ -
    -

    Simple form

    - <.form for={@form} phx-submit="submit"> - <.field required field={@form[:text]} placeholder="Text" phx-debounce="blur" label="Text" /> - - <.field label="Textarea" field={@form[:field_name]} type="textarea" /> - - <.field type="select" field={@form[:select]} options={["Option 1", "Option 2", "Option 3"]} /> - - <.field type="checkbox-group" field={@form[:checkbox_group]} options={[{"Option 1", "1"}, {"Option 2", "2"}, {"Option 3", "3"}]} /> - - <.field type="radio-group" field={@form[:radio_group]} group_layout="row" options={[{"Option 1", "1"}, {"Option 2", "2"}, {"Option 3", "3"}]} /> - - <.field type="switch" field={@form[:switch]} /> - <.field type="checkbox" field={@form[:checkbox]} /> - <.field type="color" field={@form[:color]} /> - <.field type="date" field={@form[:date]} /> - <.field type="datetime-local" field={@form[:datetime_local]} /> - <.field type="email" field={@form[:email]} /> - <.field type="file" field={@form[:file]} /> - <.field type="hidden" field={@form[:hidden]} /> - <.field type="month" field={@form[:month]} /> - <.field type="number" field={@form[:number]} /> - <.field type="password" field={@form[:password]} /> - <.field type="range" field={@form[:range]} /> - <.field type="search" field={@form[:search]} /> - <.field type="tel" field={@form[:tel]} /> - <.field type="time" field={@form[:time]} /> - <.field type="url" field={@form[:url]} /> - <.field type="week" field={@form[:week]} /> +
    + <.form id="activity-form" for={@form} phx-change="validate" phx-submit="save" phx-target={@myself}> +
    +
    +
    + <.field type="text" field={@form[:title]} placeholder="Choose a title" phx-debounce="blur" /> +
    +
    +
    + +
    +
    + <.live_component module={ImageUploader} id="uploader" uploads={@uploads} target={@myself} /> +
    + +
    +
    +
    + <.field type="datetime-local" field={@form[:start]} label="Starting date" /> +
    + +
    + <.field type="datetime-local" field={@form[:finish]} label="Ending date" /> +
    +
    + +
    +
    + <.field type="number" field={@form[:minimum_entries]} label="Minimum entries" placeholder="Choose minimum entries" /> +
    + +
    + <.field type="number" field={@form[:maximum_entries]} label="Maximum entries" placeholder="Choose maximum entries" /> +
    +
    + +
    + <.inputs_for :let={fl} field={@form[:location]}> +
    + <.field type="text" field={fl[:name]} label="Location" placeholder="Choose location name" required /> +
    + +
    + <.field type="url" field={fl[:url]} label="URL" placeholder="Choose an URL" /> +
    + +
    + +
    + <.live_component module={MultiSelect} id="speakers" items={@speakers} selected_items={@selected_speakers} target={@myself} /> +
    + +
    + <.field type="textarea" field={@form[:description]} label="Description" placeholder="Choose description" rows={15} /> +
    +
    +
    + +
    +
    + <.button type="submit" phx-disable-with="Saving...">Save +
    +
    diff --git a/lib/atomic_web/live/activity_live/new.ex b/lib/atomic_web/live/activity_live/new.ex index 0545ee28f..8ff319c9c 100644 --- a/lib/atomic_web/live/activity_live/new.ex +++ b/lib/atomic_web/live/activity_live/new.ex @@ -6,8 +6,7 @@ defmodule AtomicWeb.ActivityLive.New do @impl true def mount(_params, _session, socket) do - changeset = Activity.changeset(%Activity{}) - {:ok, assign(socket, form: to_form(changeset))} + {:ok, assign(socket, activity: %Activity{})} end @impl true diff --git a/lib/atomic_web/live/activity_live/new.html.heex b/lib/atomic_web/live/activity_live/new.html.heex index 7806618d8..bd9884938 100644 --- a/lib/atomic_web/live/activity_live/new.html.heex +++ b/lib/atomic_web/live/activity_live/new.html.heex @@ -1,3 +1,3 @@
    - <.live_component module={AtomicWeb.ActivityLive.FormComponent} id={:new} action={@live_action} form={@form} current_organization={@current_organization} return_to={Routes.activity_index_path(@socket, :index)} /> + <.live_component module={AtomicWeb.ActivityLive.FormComponent} id={:new} title={@page_title} action={@live_action} activity={@activity} current_organization={@current_organization} return_to={Routes.activity_index_path(@socket, :index)} />