diff --git a/lib/mindwendel/brainstormings/brainstorming.ex b/lib/mindwendel/brainstormings/brainstorming.ex index 80ee5152..f8eab963 100644 --- a/lib/mindwendel/brainstormings/brainstorming.ex +++ b/lib/mindwendel/brainstormings/brainstorming.ex @@ -23,7 +23,7 @@ defmodule Mindwendel.Brainstormings.Brainstorming do belongs_to :creating_user, User has_many :ideas, Idea has_many :lanes, Lane, preload_order: [asc: :position_order] - has_many :labels, IdeaLabel + has_many :labels, IdeaLabel, on_replace: :delete many_to_many :users, User, join_through: BrainstormingUser many_to_many :moderating_users, User, join_through: BrainstormingModeratingUser @@ -40,11 +40,21 @@ defmodule Mindwendel.Brainstormings.Brainstorming do :filter_labels_ids ]) |> validate_required([:name]) - |> cast_assoc(:labels) + |> cast_assoc(:labels, + with: &child_label_changeset/3, + drop_param: :labels_drop, + sort_param: :labels_sort + ) |> shorten_name |> gen_admin_url_id(brainstorming) end + defp child_label_changeset(child, changes, position) do + child + |> change(position_order: position) + |> IdeaLabel.changeset(changes) + end + def changeset_with_upated_last_accessed_at(brainstorming) do brainstorming |> change(%{last_accessed_at: DateTime.truncate(DateTime.utc_now(), :second)}) diff --git a/lib/mindwendel/brainstormings/idea_label.ex b/lib/mindwendel/brainstormings/idea_label.ex index a62c9766..5106afea 100644 --- a/lib/mindwendel/brainstormings/idea_label.ex +++ b/lib/mindwendel/brainstormings/idea_label.ex @@ -21,17 +21,15 @@ defmodule Mindwendel.Brainstormings.IdeaLabel do timestamps() end - def changeset(idea_label, params \\ %{}) - - def changeset(idea_label, %{delete: true}) do - %{Ecto.Changeset.change(idea_label, delete: true) | action: :delete} - |> no_assoc_constraint(:idea_idea_labels, message: "idea label associated with idea") - end - def changeset(idea_label, params) do idea_label |> cast(params, [:name, :color]) |> validate_required([:name]) |> validate_format(:color, ~r/^#[0-9a-f]{6}$/) + |> foreign_key_constraint(:ideas, + name: "idea_idea_labels_idea_label_id_fkey", + message: "idea label associated with idea" + ) + |> no_assoc_constraint(:idea_idea_labels, message: "idea label associated with idea") end end diff --git a/lib/mindwendel/brainstormings/idea_label_factory.ex b/lib/mindwendel/brainstormings/idea_label_factory.ex deleted file mode 100644 index b0ed5dfc..00000000 --- a/lib/mindwendel/brainstormings/idea_label_factory.ex +++ /dev/null @@ -1,27 +0,0 @@ -defmodule Mindwendel.Brainstormings.IdeaLabelFactory do - alias Mindwendel.Brainstormings.Brainstorming - - def build_idea_label(list) when is_list(list) do - rem( - length(list), - length(idea_label_variants()) - ) - |> idea_label_variant() - end - - def build_idea_label(%Brainstorming{labels: brainstorming_labels}) do - build_idea_label(brainstorming_labels) - end - - def build_idea_label(nil) do - idea_label_variant(0) - end - - def idea_label_variant(variant_number) when is_integer(variant_number) do - Enum.at(idea_label_variants(), variant_number) - end - - def idea_label_variants do - Brainstorming.idea_label_factory() - end -end diff --git a/lib/mindwendel_web/live/admin/brainstorming_live/edit.ex b/lib/mindwendel_web/live/admin/brainstorming_live/edit.ex index 3f3f9bb3..495c3a05 100644 --- a/lib/mindwendel_web/live/admin/brainstorming_live/edit.ex +++ b/lib/mindwendel_web/live/admin/brainstorming_live/edit.ex @@ -3,7 +3,6 @@ defmodule MindwendelWeb.Admin.BrainstormingLive.Edit do alias Mindwendel.Brainstormings alias Mindwendel.Brainstormings.Brainstorming - alias Mindwendel.Brainstormings.IdeaLabelFactory alias Mindwendel.Brainstormings.IdeaLabel alias Mindwendel.Repo @@ -43,12 +42,14 @@ defmodule MindwendelWeb.Admin.BrainstormingLive.Edit do do: {:noreply, assign(socket, uri: URI.parse(uri))} def handle_event("save", %{"brainstorming" => brainstorming_params}, socket) do + # why aren't we taking this from the socket? brainstorming = Brainstormings.get_brainstorming_by!(%{ admin_url_id: socket.assigns.brainstorming.admin_url_id }) |> Repo.preload(labels: from(idea_label in IdeaLabel, order_by: idea_label.position_order)) + # this changeset is too much?! changeset = Brainstorming.changeset(brainstorming, brainstorming_params) case Brainstormings.update_brainstorming(brainstorming, brainstorming_params) do @@ -76,85 +77,6 @@ defmodule MindwendelWeb.Admin.BrainstormingLive.Edit do end end - def handle_event("add_idea_label", _params, socket) do - brainstorming = socket.assigns.brainstorming - - idea_label_new = IdeaLabelFactory.build_idea_label(brainstorming) - - brainstorming_labels = - (brainstorming.labels ++ - [ - %{ - idea_label_new - | position_order: length(brainstorming.labels) + 1 - } - ]) - |> Enum.map(&Map.from_struct/1) - - case Brainstormings.update_brainstorming(brainstorming, %{labels: brainstorming_labels}) do - {:ok, brainstorming} -> - reset_changeset_timer_ref = reset_changeset_timer(socket) - - { - :noreply, - socket - |> assign(:brainstorming, brainstorming) - |> assign(:form, to_form(Brainstorming.changeset(brainstorming, %{}))) - |> assign(:reset_changeset_timer_ref, reset_changeset_timer_ref) - |> clear_flash() - } - - {:error, changeset} -> - cancel_changeset_timer(socket) - - { - :noreply, - socket - |> assign(form: to_form(changeset)) - |> put_flash(:error, gettext("Your brainstorming was not saved.")) - } - end - end - - def handle_event("remove_idea_label", %{"value" => idea_label_id}, socket) do - brainstorming = socket.assigns.brainstorming - - brainstorming_labels = - brainstorming.labels - |> Enum.map(fn label -> - if label.id == idea_label_id do - %{label | delete: true} - else - label - end - end) - |> Enum.map(&Map.from_struct/1) - - case Brainstormings.update_brainstorming(brainstorming, %{labels: brainstorming_labels}) do - {:ok, brainstorming} -> - reset_changeset_timer_ref = reset_changeset_timer(socket) - - { - :noreply, - socket - |> assign(:brainstorming, brainstorming) - |> assign(:form, to_form(Brainstorming.changeset(brainstorming, %{}))) - |> assign(:reset_changeset_timer_ref, reset_changeset_timer_ref) - |> clear_flash() - } - - {:error, changeset} -> - cancel_changeset_timer(socket) - - { - :noreply, - socket - |> assign(form: to_form(changeset)) - |> put_flash(:error, gettext("Your brainstorming was not saved.")) - } - end - end - def handle_event("empty", %{"value" => brainstorming_admin_url_id}, socket) when brainstorming_admin_url_id == socket.assigns.brainstorming.admin_url_id do brainstorming = socket.assigns.brainstorming @@ -164,6 +86,7 @@ defmodule MindwendelWeb.Admin.BrainstormingLive.Edit do {:noreply, push_navigate(socket, to: ~p"/brainstormings/#{brainstorming.id}")} end + # what is this timer? Maybe this should be FE only? defp cancel_changeset_timer(socket) do if socket.assigns[:reset_changeset_timer_ref], do: Process.cancel_timer(socket.assigns.reset_changeset_timer_ref) diff --git a/lib/mindwendel_web/live/admin/brainstorming_live/edit.html.heex b/lib/mindwendel_web/live/admin/brainstorming_live/edit.html.heex index b2646fe8..2eecd910 100644 --- a/lib/mindwendel_web/live/admin/brainstorming_live/edit.html.heex +++ b/lib/mindwendel_web/live/admin/brainstorming_live/edit.html.heex @@ -95,6 +95,7 @@
<.inputs_for :let={f_label} field={@form[:labels]}>
+ <.input type="color" field={f_label[:color]} @@ -108,24 +109,35 @@ field={f_label[:name]} placeholder={gettext("Type the label name")} /> + <.button type="button" - phx-click="remove_idea_label" - value={f_label.data.id} class="btn-outline-secondary" + name="brainstorming[labels_drop][]" + value={f_label.index} + phx-click={JS.dispatch("change")} > <%= gettext("Remove idea label") %> + <.error :for={msg <- Enum.map(f_label[:name].errors, &translate_error(&1))}> <%= msg %>
+ +
- <.button type="button" class="btn btn-secondary" phx-click="add_idea_label"> + <.button + type="button" + class="btn btn-secondary" + name="brainstorming[labels_sort][]" + value="new" + phx-click={JS.dispatch("change")} + > <%= gettext("Add idea label") %>
diff --git a/test/mindwendel/brainstormings/brainstorming_test.exs b/test/mindwendel/brainstormings/brainstorming_test.exs new file mode 100644 index 00000000..93e81608 --- /dev/null +++ b/test/mindwendel/brainstormings/brainstorming_test.exs @@ -0,0 +1,58 @@ +defmodule Mindwendel.Brainstormings.BrainstormingTest do + use Mindwendel.DataCase, async: true + + alias Mindwendel.Brainstormings.Brainstorming + alias Mindwendel.Brainstormings.IdeaLabel + # should be aliases in data case + alias Mindwendel.Factory + + # improve ecto sandbox/test warnings when just in a describe + describe "freaking relationships and on_replace stuff" do + test "oof" do + labels = [ + %IdeaLabel{name: "cyan", color: "#0dcaf0", position_order: 0} + ] + + brainstorming = Factory.insert!(:brainstorming, labels: labels) + + # make it break we forein kay constraint + brainstorming_label_first = List.first(brainstorming.labels) + + Factory.insert!(:idea, + brainstorming: brainstorming, + idea_labels: [ + brainstorming_label_first + ] + ) + + attrs = %{ + "labels" => %{ + "0" => %{ + "color" => "#0dcaf0", + "name" => "cyan" + } + }, + "labels_drop" => ["0"], + # 0? 1? + "labels_sort" => ["0"] + } + + assert length(brainstorming.labels) == 1 + + IO.puts("\n\n\n\n\n\n") + IO.puts("CHANGESET") + IO.puts("\n\n\n\n\n\n") + + changeset = Brainstorming.changeset(brainstorming, attrs) + + IO.puts("\n\n\n\n\n\n") + IO.puts("UPDATE") + IO.puts("\n\n\n\n\n\n") + + # boomzies + assert {:ok, new_brain} = Repo.update(changeset) + + assert Enum.empty?(new_brain.labels) + end + end +end diff --git a/test/mindwendel_web/live/admin/brainstorming_live/edit_test.exs b/test/mindwendel_web/live/admin/brainstorming_live/edit_test.exs index ce89c6e2..2730780f 100644 --- a/test/mindwendel_web/live/admin/brainstorming_live/edit_test.exs +++ b/test/mindwendel_web/live/admin/brainstorming_live/edit_test.exs @@ -64,16 +64,16 @@ defmodule MindwendelWeb.Admin.BrainstormingLive.EditTest do {:ok, edit_live_view, _html} = live(conn, ~p"/admin/brainstormings/#{brainstorming.admin_url_id}/edit") - edit_live_view - |> element("button", "Add idea label") - |> render_click() + add_label(edit_live_view) + + # we had 5, now we have 6 (0-based index) + assert edit_live_view + |> element("input#brainstorming_labels_5_name") + |> has_element? - assert edit_live_view |> element("input#brainstorming_labels_5_name") |> has_element? refute edit_live_view |> element("input#brainstorming_labels_6_name") |> has_element? - edit_live_view - |> element("button", "Add idea label") - |> render_click() + add_label(edit_live_view) assert edit_live_view |> element("input#brainstorming_labels_6_name") |> has_element? end @@ -101,26 +101,28 @@ defmodule MindwendelWeb.Admin.BrainstormingLive.EditTest do {:ok, edit_live_view, _html} = live(conn, ~p"/admin/brainstormings/#{brainstorming.admin_url_id}/edit") - brainstorming_label_first = Enum.at(brainstorming.labels, 0) + brainstorming_label_first = List.first(brainstorming.labels) - edit_live_view - |> element("button[value=\"#{brainstorming_label_first.id}\"]", "Remove") - |> render_click() + drop_label(edit_live_view, 0) - assert edit_live_view |> element("input#brainstorming_labels_0_name") |> has_element? + assert edit_live_view + |> element(~s{input[name="brainstorming[labels_sort][]"][value="0"]}) + |> has_element? refute edit_live_view - |> element("button[value=#{brainstorming_label_first.id}]", "Remove") + |> element("input[value=#{brainstorming_label_first.name}]") |> has_element? - refute edit_live_view |> element("input#brainstorming_labels_4_name") |> has_element? + refute edit_live_view + |> element(~s{input[name="brainstorming[labels_sort][]"][value="4"]}) + |> has_element? end test "does not remove idea label when idea is attached to this label", %{ conn: conn, brainstorming: brainstorming } do - brainstorming_label_first = Enum.at(brainstorming.labels, 0) + brainstorming_label_first = List.first(brainstorming.labels) _idea = Factory.insert!(:idea, @@ -133,9 +135,16 @@ defmodule MindwendelWeb.Admin.BrainstormingLive.EditTest do {:ok, edit_live_view, _html} = live(conn, ~p"/admin/brainstormings/#{brainstorming.admin_url_id}/edit") - assert edit_live_view - |> element(html_selector_remove_idea_label_button(brainstorming_label_first), "Remove") - |> render_click() + IO.puts("\n\n\n\n\n\n\n\n\n\n\n") + IO.puts("erroring") + IO.puts("\n\n\n\n\n\n\n\n\n\n\n") + + # check with the changeset that it errors + drop_label(edit_live_view, 0) + + IO.puts("post erroring") + + edit_live_view |> element("form#form-labels") |> render() |> IO.puts() # It should still be there because the idea label is still connected iwth an idea and therefore cannot be deleted. @@ -178,7 +187,22 @@ defmodule MindwendelWeb.Admin.BrainstormingLive.EditTest do end end + # simulates clicking the button + # https://elixirforum.com/t/tests-with-render-click-phx-click-js-dispatch-change-fail-best-way-to-fix-testing-inputs-for/67387 + defp add_label(live_view) do + live_view + |> element("form#form-labels") + |> render_change(%{"brainstorming[labels_sort][]" => "new"}) + end + + # index is 0 based + defp drop_label(live_view, label_index) do + live_view + |> element("form#form-labels") + |> render_change(%{"brainstorming[labels_drop][]" => label_index}) + end + defp html_selector_remove_idea_label_button(idea_label) do - "button[value=\"#{idea_label.id}\"]" + "button[value=\"#{idea_label.position_order}\"]" end end