add steps
This commit is contained in:
		| @@ -6,7 +6,7 @@ defmodule Memex.Pipelines.Pipeline do | |||||||
|   import Ecto.Changeset |   import Ecto.Changeset | ||||||
|   import MemexWeb.Gettext |   import MemexWeb.Gettext | ||||||
|   alias Ecto.{Changeset, UUID} |   alias Ecto.{Changeset, UUID} | ||||||
|   alias Memex.{Accounts.User, Pipelines.Step} |   alias Memex.{Accounts.User, Pipelines.Steps.Step} | ||||||
|  |  | ||||||
|   @primary_key {:id, :binary_id, autogenerate: true} |   @primary_key {:id, :binary_id, autogenerate: true} | ||||||
|   @foreign_key_type :binary_id |   @foreign_key_type :binary_id | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| defmodule Memex.Pipelines.Step do | defmodule Memex.Pipelines.Steps.Step do | ||||||
|   @moduledoc """ |   @moduledoc """ | ||||||
|   Represents a step taken while executing a pipeline |   Represents a step taken while executing a pipeline | ||||||
|   """ |   """ | ||||||
| @@ -46,16 +46,25 @@ defmodule Memex.Pipelines.Step do | |||||||
|     |> validate_required([:title, :content, :user_id, :position]) |     |> validate_required([:title, :content, :user_id, :position]) | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   @spec update_changeset(t(), attrs :: map(), position :: non_neg_integer(), User.t()) :: |   @spec update_changeset(t(), attrs :: map(), User.t()) :: | ||||||
|           changeset() |           changeset() | ||||||
|   def update_changeset( |   def update_changeset( | ||||||
|         %{user_id: user_id} = step, |         %{user_id: user_id} = step, | ||||||
|         attrs, |         attrs, | ||||||
|         position, |  | ||||||
|         %User{id: user_id} |         %User{id: user_id} | ||||||
|       ) do |       ) do | ||||||
|     step |     step | ||||||
|     |> cast(attrs, [:title, :content]) |     |> cast(attrs, [:title, :content]) | ||||||
|  |     |> validate_required([:title, :content, :user_id, :position]) | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   @spec position_changeset(t(), position :: non_neg_integer(), User.t()) :: changeset() | ||||||
|  |   def position_changeset( | ||||||
|  |         %{user_id: user_id} = step, | ||||||
|  |         position, | ||||||
|  |         %User{id: user_id} | ||||||
|  |       ) do | ||||||
|  |     step | ||||||
|     |> change(position: position) |     |> change(position: position) | ||||||
|     |> validate_required([:title, :content, :user_id, :position]) |     |> validate_required([:title, :content, :user_id, :position]) | ||||||
|   end |   end | ||||||
|   | |||||||
| @@ -4,8 +4,9 @@ defmodule Memex.Pipelines.Steps do | |||||||
|   """ |   """ | ||||||
|  |  | ||||||
|   import Ecto.Query, warn: false |   import Ecto.Query, warn: false | ||||||
|  |   alias Ecto.Multi | ||||||
|   alias Memex.{Accounts.User, Repo} |   alias Memex.{Accounts.User, Repo} | ||||||
|   alias Memex.Pipelines.{Pipeline, Step} |   alias Memex.Pipelines.{Pipeline, Steps.Step} | ||||||
|  |  | ||||||
|   @doc """ |   @doc """ | ||||||
|   Returns the list of steps. |   Returns the list of steps. | ||||||
| @@ -105,11 +106,11 @@ defmodule Memex.Pipelines.Steps do | |||||||
|       {:error, %Ecto.Changeset{}} |       {:error, %Ecto.Changeset{}} | ||||||
|  |  | ||||||
|   """ |   """ | ||||||
|   @spec update_step(Step.t(), attrs :: map(), position :: non_neg_integer(), User.t()) :: |   @spec update_step(Step.t(), attrs :: map(), User.t()) :: | ||||||
|           {:ok, Step.t()} | {:error, Step.changeset()} |           {:ok, Step.t()} | {:error, Step.changeset()} | ||||||
|   def update_step(%Step{} = step, attrs, position, user) do |   def update_step(%Step{} = step, attrs, user) do | ||||||
|     step |     step | ||||||
|     |> Step.update_changeset(attrs, position, user) |     |> Step.update_changeset(attrs, user) | ||||||
|     |> Repo.update() |     |> Repo.update() | ||||||
|   end |   end | ||||||
|  |  | ||||||
| @@ -130,11 +131,31 @@ defmodule Memex.Pipelines.Steps do | |||||||
|   """ |   """ | ||||||
|   @spec delete_step(Step.t(), User.t()) :: {:ok, Step.t()} | {:error, Step.changeset()} |   @spec delete_step(Step.t(), User.t()) :: {:ok, Step.t()} | {:error, Step.changeset()} | ||||||
|   def delete_step(%Step{user_id: user_id} = step, %{id: user_id}) do |   def delete_step(%Step{user_id: user_id} = step, %{id: user_id}) do | ||||||
|     step |> Repo.delete() |     delete_step(step) | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def delete_step(%Step{} = step, %{role: :admin}) do |   def delete_step(%Step{} = step, %{role: :admin}) do | ||||||
|     step |> Repo.delete() |     delete_step(step) | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   defp delete_step(step) do | ||||||
|  |     Multi.new() | ||||||
|  |     |> Multi.delete(:delete_step, step) | ||||||
|  |     |> Multi.update_all( | ||||||
|  |       :reorder_steps, | ||||||
|  |       fn %{delete_step: %{position: position, pipeline_id: pipeline_id}} -> | ||||||
|  |         from s in Step, | ||||||
|  |           where: s.pipeline_id == ^pipeline_id, | ||||||
|  |           where: s.position > ^position, | ||||||
|  |           update: [set: [position: s.position - 1]] | ||||||
|  |       end, | ||||||
|  |       [] | ||||||
|  |     ) | ||||||
|  |     |> Repo.transaction() | ||||||
|  |     |> case do | ||||||
|  |       {:ok, %{delete_step: step}} -> {:ok, step} | ||||||
|  |       {:error, :delete_step, changeset, _changes_so_far} -> {:error, changeset} | ||||||
|  |     end | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   @doc """ |   @doc """ | ||||||
| @@ -149,11 +170,69 @@ defmodule Memex.Pipelines.Steps do | |||||||
|       %Ecto.Changeset{data: %Step{}} |       %Ecto.Changeset{data: %Step{}} | ||||||
|  |  | ||||||
|   """ |   """ | ||||||
|   @spec change_step(Step.t(), position :: non_neg_integer(), Pipeline.t(), User.t()) :: |   @spec change_step(Step.t(), User.t()) :: Step.changeset() | ||||||
|           Step.changeset() |   @spec change_step(Step.t(), attrs :: map(), User.t()) :: Step.changeset() | ||||||
|   @spec change_step(Step.t(), attrs :: map(), position :: non_neg_integer(), User.t()) :: |   def change_step(%Step{} = step, attrs \\ %{}, user) do | ||||||
|           Step.changeset() |     step |> Step.update_changeset(attrs, user) | ||||||
|   def change_step(%Step{} = step, attrs \\ %{}, position, user) do |   end | ||||||
|     step |> Step.update_changeset(attrs, position, user) |  | ||||||
|  |   @spec reorder_step(Step.t(), :up | :down, User.t()) :: | ||||||
|  |           {:ok, Step.t()} | {:error, Step.changeset()} | ||||||
|  |   def reorder_step(%Step{position: 0} = step, :up, _user), do: {:error, step} | ||||||
|  |  | ||||||
|  |   def reorder_step( | ||||||
|  |         %Step{position: position, pipeline_id: pipeline_id, user_id: user_id} = step, | ||||||
|  |         :up, | ||||||
|  |         %{id: user_id} = user | ||||||
|  |       ) do | ||||||
|  |     Multi.new() | ||||||
|  |     |> Multi.update_all( | ||||||
|  |       :reorder_steps, | ||||||
|  |       from(s in Step, | ||||||
|  |         where: s.pipeline_id == ^pipeline_id, | ||||||
|  |         where: s.position == ^position - 1, | ||||||
|  |         update: [set: [position: ^position]] | ||||||
|  |       ), | ||||||
|  |       [] | ||||||
|  |     ) | ||||||
|  |     |> Multi.update( | ||||||
|  |       :update_step, | ||||||
|  |       step |> Step.position_changeset(position - 1, user) | ||||||
|  |     ) | ||||||
|  |     |> Repo.transaction() | ||||||
|  |     |> case do | ||||||
|  |       {:ok, %{update_step: step}} -> {:ok, step} | ||||||
|  |       {:error, :update_step, changeset, _changes_so_far} -> {:error, changeset} | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   def reorder_step( | ||||||
|  |         %Step{pipeline_id: pipeline_id, position: position, user_id: user_id} = step, | ||||||
|  |         :down, | ||||||
|  |         %{id: user_id} = user | ||||||
|  |       ) do | ||||||
|  |     Multi.new() | ||||||
|  |     |> Multi.one( | ||||||
|  |       :step_count, | ||||||
|  |       from(s in Step, where: s.pipeline_id == ^pipeline_id, distinct: true, select: count(s.id)) | ||||||
|  |     ) | ||||||
|  |     |> Multi.update_all( | ||||||
|  |       :reorder_steps, | ||||||
|  |       from(s in Step, | ||||||
|  |         where: s.pipeline_id == ^pipeline_id, | ||||||
|  |         where: s.position == ^position + 1, | ||||||
|  |         update: [set: [position: ^position]] | ||||||
|  |       ), | ||||||
|  |       [] | ||||||
|  |     ) | ||||||
|  |     |> Multi.update(:update_step, fn %{step_count: step_count} -> | ||||||
|  |       new_position = if position >= step_count - 1, do: position, else: position + 1 | ||||||
|  |       step |> Step.position_changeset(new_position, user) | ||||||
|  |     end) | ||||||
|  |     |> Repo.transaction() | ||||||
|  |     |> case do | ||||||
|  |       {:ok, %{update_step: step}} -> {:ok, step} | ||||||
|  |       {:error, :update_step, changeset, _changes_so_far} -> {:error, changeset} | ||||||
|  |     end | ||||||
|   end |   end | ||||||
| end | end | ||||||
|   | |||||||
							
								
								
									
										44
									
								
								lib/memex_web/components/step_content.ex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								lib/memex_web/components/step_content.ex
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,44 @@ | |||||||
|  | defmodule MemexWeb.Components.StepContent do | ||||||
|  |   @moduledoc """ | ||||||
|  |   Display the content for a step | ||||||
|  |   """ | ||||||
|  |   use MemexWeb, :component | ||||||
|  |   alias Memex.Pipelines.Steps.Step | ||||||
|  |   alias Phoenix.HTML | ||||||
|  |  | ||||||
|  |   attr :step, Step, required: true | ||||||
|  |  | ||||||
|  |   def step_content(assigns) do | ||||||
|  |     ~H""" | ||||||
|  |     <div | ||||||
|  |       id={"show-step-content-#{@step.id}"} | ||||||
|  |       class="input input-primary h-32 min-h-32 inline-block" | ||||||
|  |       phx-hook="MaintainAttrs" | ||||||
|  |       phx-update="ignore" | ||||||
|  |       readonly | ||||||
|  |       phx-no-format | ||||||
|  |     ><p class="inline"><%= add_links_to_content(@step.content) %></p></div> | ||||||
|  |     """ | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   defp add_links_to_content(content) do | ||||||
|  |     Regex.replace( | ||||||
|  |       ~r/\[\[([\p{L}\p{N}\-]+)\]\]/, | ||||||
|  |       content, | ||||||
|  |       fn _whole_match, slug -> | ||||||
|  |         link = | ||||||
|  |           HTML.Link.link( | ||||||
|  |             "[[#{slug}]]", | ||||||
|  |             to: Routes.context_show_path(Endpoint, :show, slug), | ||||||
|  |             class: "link inline", | ||||||
|  |             data: [qa: "step-context-#{slug}"] | ||||||
|  |           ) | ||||||
|  |           |> HTML.Safe.to_iodata() | ||||||
|  |           |> IO.iodata_to_binary() | ||||||
|  |  | ||||||
|  |         "</p>#{link}<p class=\"inline\">" | ||||||
|  |       end | ||||||
|  |     ) | ||||||
|  |     |> HTML.raw() | ||||||
|  |   end | ||||||
|  | end | ||||||
| @@ -1,7 +1,8 @@ | |||||||
| defmodule MemexWeb.PipelineLive.Show do | defmodule MemexWeb.PipelineLive.Show do | ||||||
|   use MemexWeb, :live_view |   use MemexWeb, :live_view | ||||||
|  |   import MemexWeb.Components.StepContent | ||||||
|   alias Memex.{Accounts.User, Pipelines, Pipelines.Pipeline} |   alias Memex.{Accounts.User, Pipelines} | ||||||
|  |   alias Memex.Pipelines.{Pipeline, Steps, Steps.Step} | ||||||
|  |  | ||||||
|   @impl true |   @impl true | ||||||
|   def mount(_params, _session, socket) do |   def mount(_params, _session, socket) do | ||||||
| @@ -10,9 +11,9 @@ defmodule MemexWeb.PipelineLive.Show do | |||||||
|  |  | ||||||
|   @impl true |   @impl true | ||||||
|   def handle_params( |   def handle_params( | ||||||
|         %{"slug" => slug}, |         %{"slug" => slug} = params, | ||||||
|         _, |         _url, | ||||||
|         %{assigns: %{live_action: live_action, current_user: current_user}} = socket |         %{assigns: %{current_user: current_user, live_action: live_action}} = socket | ||||||
|       ) do |       ) do | ||||||
|     pipeline = |     pipeline = | ||||||
|       case Pipelines.get_pipeline_by_slug(slug, current_user) do |       case Pipelines.get_pipeline_by_slug(slug, current_user) do | ||||||
| @@ -24,10 +25,45 @@ defmodule MemexWeb.PipelineLive.Show do | |||||||
|       socket |       socket | ||||||
|       |> assign(:page_title, page_title(live_action, pipeline)) |       |> assign(:page_title, page_title(live_action, pipeline)) | ||||||
|       |> assign(:pipeline, pipeline) |       |> assign(:pipeline, pipeline) | ||||||
|  |       |> assign(:steps, pipeline |> Steps.list_steps(current_user)) | ||||||
|  |       |> apply_action(live_action, params) | ||||||
|  |  | ||||||
|     {:noreply, socket} |     {:noreply, socket} | ||||||
|   end |   end | ||||||
|  |  | ||||||
|  |   defp apply_action(socket, live_action, _params) when live_action in [:show, :edit] do | ||||||
|  |     socket | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   defp apply_action( | ||||||
|  |          %{ | ||||||
|  |            assigns: %{ | ||||||
|  |              steps: steps, | ||||||
|  |              pipeline: %{id: pipeline_id}, | ||||||
|  |              current_user: %{id: current_user_id} | ||||||
|  |            } | ||||||
|  |          } = socket, | ||||||
|  |          :add_step, | ||||||
|  |          _params | ||||||
|  |        ) do | ||||||
|  |     socket | ||||||
|  |     |> assign( | ||||||
|  |       step: %Step{ | ||||||
|  |         position: steps |> Enum.count(), | ||||||
|  |         pipeline_id: pipeline_id, | ||||||
|  |         user_id: current_user_id | ||||||
|  |       } | ||||||
|  |     ) | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   defp apply_action( | ||||||
|  |          %{assigns: %{current_user: current_user}} = socket, | ||||||
|  |          :edit_step, | ||||||
|  |          %{"step_id" => step_id} | ||||||
|  |        ) do | ||||||
|  |     socket |> assign(step: step_id |> Steps.get_step!(current_user)) | ||||||
|  |   end | ||||||
|  |  | ||||||
|   @impl true |   @impl true | ||||||
|   def handle_event( |   def handle_event( | ||||||
|         "delete", |         "delete", | ||||||
| @@ -44,8 +80,51 @@ defmodule MemexWeb.PipelineLive.Show do | |||||||
|     {:noreply, socket} |     {:noreply, socket} | ||||||
|   end |   end | ||||||
|  |  | ||||||
|  |   @impl true | ||||||
|  |   def handle_event( | ||||||
|  |         "delete_step", | ||||||
|  |         %{"step-id" => step_id}, | ||||||
|  |         %{assigns: %{pipeline: %{slug: pipeline_slug}, current_user: current_user}} = socket | ||||||
|  |       ) do | ||||||
|  |     {:ok, %{title: title}} = | ||||||
|  |       step_id | ||||||
|  |       |> Steps.get_step!(current_user) | ||||||
|  |       |> Steps.delete_step(current_user) | ||||||
|  |  | ||||||
|  |     socket = | ||||||
|  |       socket | ||||||
|  |       |> put_flash(:info, gettext("%{title} deleted", title: title)) | ||||||
|  |       |> push_patch(to: Routes.pipeline_show_path(Endpoint, :show, pipeline_slug)) | ||||||
|  |  | ||||||
|  |     {:noreply, socket} | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   @impl true | ||||||
|  |   def handle_event( | ||||||
|  |         "reorder_step", | ||||||
|  |         %{"step-id" => step_id, "direction" => direction}, | ||||||
|  |         %{assigns: %{pipeline: %{slug: pipeline_slug}, current_user: current_user}} = socket | ||||||
|  |       ) do | ||||||
|  |     direction = if direction == "up", do: :up, else: :down | ||||||
|  |  | ||||||
|  |     {:ok, _step} = | ||||||
|  |       step_id | ||||||
|  |       |> Steps.get_step!(current_user) | ||||||
|  |       |> Steps.reorder_step(direction, current_user) | ||||||
|  |  | ||||||
|  |     socket = | ||||||
|  |       socket | ||||||
|  |       |> push_patch(to: Routes.pipeline_show_path(Endpoint, :show, pipeline_slug)) | ||||||
|  |  | ||||||
|  |     {:noreply, socket} | ||||||
|  |   end | ||||||
|  |  | ||||||
|   defp page_title(:show, %{slug: slug}), do: slug |   defp page_title(:show, %{slug: slug}), do: slug | ||||||
|   defp page_title(:edit, %{slug: slug}), do: gettext("edit %{slug}", slug: slug) |  | ||||||
|  |   defp page_title(live_action, %{slug: slug}) when live_action in [:edit, :edit_step], | ||||||
|  |     do: gettext("edit %{slug}", slug: slug) | ||||||
|  |  | ||||||
|  |   defp page_title(:add_step, %{slug: slug}), do: gettext("add step to %{slug}", slug: slug) | ||||||
|  |  | ||||||
|   @spec is_owner_or_admin?(Pipeline.t(), User.t()) :: boolean() |   @spec is_owner_or_admin?(Pipeline.t(), User.t()) :: boolean() | ||||||
|   defp is_owner_or_admin?(%{user_id: user_id}, %{id: user_id}), do: true |   defp is_owner_or_admin?(%{user_id: user_id}, %{id: user_id}), do: true | ||||||
|   | |||||||
| @@ -5,20 +5,22 @@ | |||||||
|  |  | ||||||
|   <p><%= if @pipeline.tags, do: @pipeline.tags |> Enum.join(", ") %></p> |   <p><%= if @pipeline.tags, do: @pipeline.tags |> Enum.join(", ") %></p> | ||||||
|  |  | ||||||
|  |   <%= if @pipeline.description do %> | ||||||
|     <textarea |     <textarea | ||||||
|       id="show-pipeline-description" |       id="show-pipeline-description" | ||||||
|     class="input input-primary h-128 min-h-128" |       class="input input-primary h-32 min-h-32" | ||||||
|       phx-hook="MaintainAttrs" |       phx-hook="MaintainAttrs" | ||||||
|       phx-update="ignore" |       phx-update="ignore" | ||||||
|       readonly |       readonly | ||||||
|       phx-no-format |       phx-no-format | ||||||
|     ><%= @pipeline.description %></textarea> |     ><%= @pipeline.description %></textarea> | ||||||
|  |   <% end %> | ||||||
|  |  | ||||||
|   <p class="self-end"> |   <p class="self-end"> | ||||||
|     <%= gettext("Visibility: %{visibility}", visibility: @pipeline.visibility) %> |     <%= gettext("Visibility: %{visibility}", visibility: @pipeline.visibility) %> | ||||||
|   </p> |   </p> | ||||||
|  |  | ||||||
|   <div class="self-end flex space-x-4"> |   <div class="pb-4 self-end flex space-x-4"> | ||||||
|     <.link class="btn btn-primary" navigate={Routes.pipeline_index_path(@socket, :index)}> |     <.link class="btn btn-primary" navigate={Routes.pipeline_index_path(@socket, :index)}> | ||||||
|       <%= dgettext("actions", "back") %> |       <%= dgettext("actions", "back") %> | ||||||
|     </.link> |     </.link> | ||||||
| @@ -42,9 +44,95 @@ | |||||||
|       </button> |       </button> | ||||||
|     <% end %> |     <% end %> | ||||||
|   </div> |   </div> | ||||||
|  |  | ||||||
|  |   <hr class="hr" /> | ||||||
|  |  | ||||||
|  |   <h2 class="pt-2 self-center text-lg"> | ||||||
|  |     <%= gettext("steps:") %> | ||||||
|  |   </h2> | ||||||
|  |  | ||||||
|  |   <%= if @steps |> Enum.empty?() do %> | ||||||
|  |     <h3 class="self-center text-md text-primary-600"> | ||||||
|  |       <%= gettext("no steps") %> | ||||||
|  |     </h3> | ||||||
|  |   <% else %> | ||||||
|  |     <%= for %{id: step_id, position: position, title: title} = step <- @steps do %> | ||||||
|  |       <div class="flex justify-between items-center space-x-4"> | ||||||
|  |         <h3 class="text-md"> | ||||||
|  |           <%= gettext("%{position}. %{title}", position: position + 1, title: title) %> | ||||||
|  |         </h3> | ||||||
|  |  | ||||||
|  |         <%= if is_owner?(@pipeline, @current_user) do %> | ||||||
|  |           <div class="flex justify-between items-center space-x-4"> | ||||||
|  |             <%= if position <= 0 do %> | ||||||
|  |               <i class="fas text-xl fa-chevron-up cursor-not-allowed opacity-25"></i> | ||||||
|  |             <% else %> | ||||||
|  |               <button | ||||||
|  |                 type="button" | ||||||
|  |                 class="cursor-pointer flex justify-center items-center" | ||||||
|  |                 phx-click="reorder_step" | ||||||
|  |                 phx-value-direction="up" | ||||||
|  |                 phx-value-step-id={step_id} | ||||||
|  |                 data-qa={"move-step-up-#{step_id}"} | ||||||
|  |               > | ||||||
|  |                 <i class="fas text-xl fa-chevron-up"></i> | ||||||
|  |               </button> | ||||||
|  |             <% end %> | ||||||
|  |  | ||||||
|  |             <%= if position >= length(@steps) - 1 do %> | ||||||
|  |               <i class="fas text-xl fa-chevron-down cursor-not-allowed opacity-25"></i> | ||||||
|  |             <% else %> | ||||||
|  |               <button | ||||||
|  |                 type="button" | ||||||
|  |                 class="cursor-pointer flex justify-center items-center" | ||||||
|  |                 phx-click="reorder_step" | ||||||
|  |                 phx-value-direction="down" | ||||||
|  |                 phx-value-step-id={step_id} | ||||||
|  |                 data-qa={"move-step-down-#{step_id}"} | ||||||
|  |               > | ||||||
|  |                 <i class="fas text-xl fa-chevron-down"></i> | ||||||
|  |               </button> | ||||||
|  |             <% end %> | ||||||
|  |  | ||||||
|  |             <.link | ||||||
|  |               class="self-end btn btn-primary" | ||||||
|  |               patch={Routes.pipeline_show_path(@socket, :edit_step, @pipeline.slug, step_id)} | ||||||
|  |               data-qa={"edit-step-#{step_id}"} | ||||||
|  |             > | ||||||
|  |               <%= dgettext("actions", "edit") %> | ||||||
|  |             </.link> | ||||||
|  |  | ||||||
|  |             <button | ||||||
|  |               type="button" | ||||||
|  |               class="btn btn-primary" | ||||||
|  |               phx-click="delete_step" | ||||||
|  |               phx-value-step-id={step_id} | ||||||
|  |               data-confirm={dgettext("prompts", "are you sure?")} | ||||||
|  |               data-qa={"delete-step-#{step_id}"} | ||||||
|  |             > | ||||||
|  |               <%= dgettext("actions", "delete") %> | ||||||
|  |             </button> | ||||||
|  |           </div> | ||||||
|  |         <% end %> | ||||||
|  |       </div> | ||||||
|  |  | ||||||
|  |       <.step_content step={step} /> | ||||||
|  |     <% end %> | ||||||
|  |   <% end %> | ||||||
|  |  | ||||||
|  |   <%= if is_owner?(@pipeline, @current_user) do %> | ||||||
|  |     <.link | ||||||
|  |       class="self-end btn btn-primary" | ||||||
|  |       patch={Routes.pipeline_show_path(@socket, :add_step, @pipeline.slug)} | ||||||
|  |       data-qa={"add-step-#{@pipeline.id}"} | ||||||
|  |     > | ||||||
|  |       <%= dgettext("actions", "add step") %> | ||||||
|  |     </.link> | ||||||
|  |   <% end %> | ||||||
| </div> | </div> | ||||||
|  |  | ||||||
| <%= if @live_action in [:edit] do %> | <%= case @live_action do %> | ||||||
|  |   <% :edit -> %> | ||||||
|     <.modal return_to={Routes.pipeline_show_path(@socket, :show, @pipeline.slug)}> |     <.modal return_to={Routes.pipeline_show_path(@socket, :show, @pipeline.slug)}> | ||||||
|       <.live_component |       <.live_component | ||||||
|         module={MemexWeb.PipelineLive.FormComponent} |         module={MemexWeb.PipelineLive.FormComponent} | ||||||
| @@ -56,4 +144,31 @@ | |||||||
|         return_to={Routes.pipeline_show_path(@socket, :show, @pipeline.slug)} |         return_to={Routes.pipeline_show_path(@socket, :show, @pipeline.slug)} | ||||||
|       /> |       /> | ||||||
|     </.modal> |     </.modal> | ||||||
|  |   <% :add_step -> %> | ||||||
|  |     <.modal return_to={Routes.pipeline_show_path(@socket, :show, @pipeline.slug)}> | ||||||
|  |       <.live_component | ||||||
|  |         module={MemexWeb.StepLive.FormComponent} | ||||||
|  |         id={@pipeline.id || :new} | ||||||
|  |         current_user={@current_user} | ||||||
|  |         title={@page_title} | ||||||
|  |         action={@live_action} | ||||||
|  |         pipeline={@pipeline} | ||||||
|  |         step={@step} | ||||||
|  |         return_to={Routes.pipeline_show_path(@socket, :show, @pipeline.slug)} | ||||||
|  |       /> | ||||||
|  |     </.modal> | ||||||
|  |   <% :edit_step -> %> | ||||||
|  |     <.modal return_to={Routes.pipeline_show_path(@socket, :show, @pipeline.slug)}> | ||||||
|  |       <.live_component | ||||||
|  |         module={MemexWeb.StepLive.FormComponent} | ||||||
|  |         id={@pipeline.id || :new} | ||||||
|  |         current_user={@current_user} | ||||||
|  |         title={@page_title} | ||||||
|  |         action={@live_action} | ||||||
|  |         pipeline={@pipeline} | ||||||
|  |         step={@step} | ||||||
|  |         return_to={Routes.pipeline_show_path(@socket, :show, @pipeline.slug)} | ||||||
|  |       /> | ||||||
|  |     </.modal> | ||||||
|  |   <% _ -> %> | ||||||
| <% end %> | <% end %> | ||||||
|   | |||||||
							
								
								
									
										74
									
								
								lib/memex_web/live/step_live/form_component.ex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								lib/memex_web/live/step_live/form_component.ex
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,74 @@ | |||||||
|  | defmodule MemexWeb.StepLive.FormComponent do | ||||||
|  |   use MemexWeb, :live_component | ||||||
|  |  | ||||||
|  |   alias Memex.Pipelines.Steps | ||||||
|  |  | ||||||
|  |   @impl true | ||||||
|  |   def update(%{step: step, current_user: current_user, pipeline: _pipeline} = assigns, socket) do | ||||||
|  |     changeset = Steps.change_step(step, current_user) | ||||||
|  |  | ||||||
|  |     {:ok, | ||||||
|  |      socket | ||||||
|  |      |> assign(assigns) | ||||||
|  |      |> assign(:changeset, changeset)} | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   @impl true | ||||||
|  |   def handle_event( | ||||||
|  |         "validate", | ||||||
|  |         %{"step" => step_params}, | ||||||
|  |         %{assigns: %{step: step, current_user: current_user}} = socket | ||||||
|  |       ) do | ||||||
|  |     changeset = | ||||||
|  |       step | ||||||
|  |       |> Steps.change_step(step_params, current_user) | ||||||
|  |       |> Map.put(:action, :validate) | ||||||
|  |  | ||||||
|  |     {:noreply, assign(socket, :changeset, changeset)} | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   def handle_event("save", %{"step" => step_params}, %{assigns: %{action: action}} = socket) do | ||||||
|  |     save_step(socket, action, step_params) | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   defp save_step( | ||||||
|  |          %{assigns: %{step: step, return_to: return_to, current_user: current_user}} = socket, | ||||||
|  |          :edit_step, | ||||||
|  |          step_params | ||||||
|  |        ) do | ||||||
|  |     case Steps.update_step(step, step_params, current_user) do | ||||||
|  |       {:ok, %{title: title}} -> | ||||||
|  |         {:noreply, | ||||||
|  |          socket | ||||||
|  |          |> put_flash(:info, gettext("%{title} saved", title: title)) | ||||||
|  |          |> push_navigate(to: return_to)} | ||||||
|  |  | ||||||
|  |       {:error, %Ecto.Changeset{} = changeset} -> | ||||||
|  |         {:noreply, assign(socket, :changeset, changeset)} | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   defp save_step( | ||||||
|  |          %{ | ||||||
|  |            assigns: %{ | ||||||
|  |              step: %{position: position}, | ||||||
|  |              return_to: return_to, | ||||||
|  |              current_user: current_user, | ||||||
|  |              pipeline: pipeline | ||||||
|  |            } | ||||||
|  |          } = socket, | ||||||
|  |          :add_step, | ||||||
|  |          step_params | ||||||
|  |        ) do | ||||||
|  |     case Steps.create_step(step_params, position, pipeline, current_user) do | ||||||
|  |       {:ok, %{title: title}} -> | ||||||
|  |         {:noreply, | ||||||
|  |          socket | ||||||
|  |          |> put_flash(:info, gettext("%{title} created", title: title)) | ||||||
|  |          |> push_navigate(to: return_to)} | ||||||
|  |  | ||||||
|  |       {:error, %Ecto.Changeset{} = changeset} -> | ||||||
|  |         {:noreply, assign(socket, changeset: changeset)} | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | end | ||||||
							
								
								
									
										34
									
								
								lib/memex_web/live/step_live/form_component.html.heex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								lib/memex_web/live/step_live/form_component.html.heex
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | |||||||
|  | <div class="h-full flex flex-col justify-start items-stretch space-y-4"> | ||||||
|  |   <.form | ||||||
|  |     :let={f} | ||||||
|  |     for={@changeset} | ||||||
|  |     id="step-form" | ||||||
|  |     phx-target={@myself} | ||||||
|  |     phx-change="validate" | ||||||
|  |     phx-submit="save" | ||||||
|  |     phx-debounce="300" | ||||||
|  |     class="flex flex-col justify-start items-stretch space-y-4" | ||||||
|  |   > | ||||||
|  |     <%= text_input(f, :title, | ||||||
|  |       class: "input input-primary", | ||||||
|  |       placeholder: gettext("title") | ||||||
|  |     ) %> | ||||||
|  |     <%= error_tag(f, :title) %> | ||||||
|  |  | ||||||
|  |     <%= textarea(f, :content, | ||||||
|  |       id: "step-form-content", | ||||||
|  |       class: "input input-primary h-64 min-h-64", | ||||||
|  |       phx_hook: "MaintainAttrs", | ||||||
|  |       phx_update: "ignore", | ||||||
|  |       placeholder: gettext("use [[context-slug]] to link to a context") | ||||||
|  |     ) %> | ||||||
|  |     <%= error_tag(f, :content) %> | ||||||
|  |  | ||||||
|  |     <div class="flex justify-center items-stretch space-x-4"> | ||||||
|  |       <%= submit(dgettext("actions", "save"), | ||||||
|  |         phx_disable_with: gettext("saving..."), | ||||||
|  |         class: "mx-auto btn btn-primary" | ||||||
|  |       ) %> | ||||||
|  |     </div> | ||||||
|  |   </.form> | ||||||
|  | </div> | ||||||
| @@ -69,6 +69,8 @@ defmodule MemexWeb.Router do | |||||||
|       live "/pipelines/new", PipelineLive.Index, :new |       live "/pipelines/new", PipelineLive.Index, :new | ||||||
|       live "/pipelines/:slug/edit", PipelineLive.Index, :edit |       live "/pipelines/:slug/edit", PipelineLive.Index, :edit | ||||||
|       live "/pipeline/:slug/edit", PipelineLive.Show, :edit |       live "/pipeline/:slug/edit", PipelineLive.Show, :edit | ||||||
|  |       live "/pipeline/:slug/add_step", PipelineLive.Show, :add_step | ||||||
|  |       live "/pipeline/:slug/:step_id", PipelineLive.Show, :edit_step | ||||||
|  |  | ||||||
|       get "/users/settings", UserSettingsController, :edit |       get "/users/settings", UserSettingsController, :edit | ||||||
|       put "/users/settings", UserSettingsController, :update |       put "/users/settings", UserSettingsController, :update | ||||||
|   | |||||||
| @@ -72,7 +72,8 @@ msgstr "" | |||||||
| #: lib/memex_web/live/note_live/index.html.heex:49 | #: lib/memex_web/live/note_live/index.html.heex:49 | ||||||
| #: lib/memex_web/live/note_live/show.html.heex:38 | #: lib/memex_web/live/note_live/show.html.heex:38 | ||||||
| #: lib/memex_web/live/pipeline_live/index.html.heex:49 | #: lib/memex_web/live/pipeline_live/index.html.heex:49 | ||||||
| #: lib/memex_web/live/pipeline_live/show.html.heex:41 | #: lib/memex_web/live/pipeline_live/show.html.heex:43 | ||||||
|  | #: lib/memex_web/live/pipeline_live/show.html.heex:113 | ||||||
| #, elixir-autogen, elixir-format | #, elixir-autogen, elixir-format | ||||||
| msgid "delete" | msgid "delete" | ||||||
| msgstr "" | msgstr "" | ||||||
| @@ -87,7 +88,8 @@ msgstr "" | |||||||
| #: lib/memex_web/live/note_live/index.html.heex:38 | #: lib/memex_web/live/note_live/index.html.heex:38 | ||||||
| #: lib/memex_web/live/note_live/show.html.heex:27 | #: lib/memex_web/live/note_live/show.html.heex:27 | ||||||
| #: lib/memex_web/live/pipeline_live/index.html.heex:38 | #: lib/memex_web/live/pipeline_live/index.html.heex:38 | ||||||
| #: lib/memex_web/live/pipeline_live/show.html.heex:30 | #: lib/memex_web/live/pipeline_live/show.html.heex:32 | ||||||
|  | #: lib/memex_web/live/pipeline_live/show.html.heex:102 | ||||||
| #, elixir-autogen, elixir-format | #, elixir-autogen, elixir-format | ||||||
| msgid "edit" | msgid "edit" | ||||||
| msgstr "" | msgstr "" | ||||||
| @@ -137,13 +139,19 @@ msgstr "" | |||||||
| #: lib/memex_web/live/context_live/form_component.html.heex:42 | #: lib/memex_web/live/context_live/form_component.html.heex:42 | ||||||
| #: lib/memex_web/live/note_live/form_component.html.heex:42 | #: lib/memex_web/live/note_live/form_component.html.heex:42 | ||||||
| #: lib/memex_web/live/pipeline_live/form_component.html.heex:42 | #: lib/memex_web/live/pipeline_live/form_component.html.heex:42 | ||||||
|  | #: lib/memex_web/live/step_live/form_component.html.heex:28 | ||||||
| #, elixir-autogen, elixir-format | #, elixir-autogen, elixir-format | ||||||
| msgid "save" | msgid "save" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: lib/memex_web/live/context_live/show.html.heex:16 | #: lib/memex_web/live/context_live/show.html.heex:16 | ||||||
| #: lib/memex_web/live/note_live/show.html.heex:23 | #: lib/memex_web/live/note_live/show.html.heex:23 | ||||||
| #: lib/memex_web/live/pipeline_live/show.html.heex:23 | #: lib/memex_web/live/pipeline_live/show.html.heex:25 | ||||||
| #, elixir-autogen, elixir-format | #, elixir-autogen, elixir-format | ||||||
| msgid "back" | msgid "back" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
|  | #: lib/memex_web/live/pipeline_live/show.html.heex:129 | ||||||
|  | #, elixir-autogen, elixir-format | ||||||
|  | msgid "add step" | ||||||
|  | msgstr "" | ||||||
|   | |||||||
| @@ -82,7 +82,7 @@ msgstr "" | |||||||
|  |  | ||||||
| #: lib/memex_web/live/context_live/show.html.heex:11 | #: lib/memex_web/live/context_live/show.html.heex:11 | ||||||
| #: lib/memex_web/live/note_live/show.html.heex:18 | #: lib/memex_web/live/note_live/show.html.heex:18 | ||||||
| #: lib/memex_web/live/pipeline_live/show.html.heex:18 | #: lib/memex_web/live/pipeline_live/show.html.heex:20 | ||||||
| #, elixir-autogen, elixir-format | #, elixir-autogen, elixir-format | ||||||
| msgid "Visibility: %{visibility}" | msgid "Visibility: %{visibility}" | ||||||
| msgstr "" | msgstr "" | ||||||
| @@ -309,6 +309,7 @@ msgstr "" | |||||||
| #: lib/memex_web/live/context_live/form_component.html.heex:43 | #: lib/memex_web/live/context_live/form_component.html.heex:43 | ||||||
| #: lib/memex_web/live/note_live/form_component.html.heex:43 | #: lib/memex_web/live/note_live/form_component.html.heex:43 | ||||||
| #: lib/memex_web/live/pipeline_live/form_component.html.heex:43 | #: lib/memex_web/live/pipeline_live/form_component.html.heex:43 | ||||||
|  | #: lib/memex_web/live/step_live/form_component.html.heex:29 | ||||||
| #, elixir-autogen, elixir-format | #, elixir-autogen, elixir-format | ||||||
| msgid "saving..." | msgid "saving..." | ||||||
| msgstr "" | msgstr "" | ||||||
| @@ -426,7 +427,7 @@ msgstr "" | |||||||
| #: lib/memex_web/live/note_live/index.ex:57 | #: lib/memex_web/live/note_live/index.ex:57 | ||||||
| #: lib/memex_web/live/note_live/show.ex:41 | #: lib/memex_web/live/note_live/show.ex:41 | ||||||
| #: lib/memex_web/live/pipeline_live/index.ex:57 | #: lib/memex_web/live/pipeline_live/index.ex:57 | ||||||
| #: lib/memex_web/live/pipeline_live/show.ex:41 | #: lib/memex_web/live/pipeline_live/show.ex:77 | ||||||
| #, elixir-autogen, elixir-format | #, elixir-autogen, elixir-format | ||||||
| msgid "%{slug} deleted" | msgid "%{slug} deleted" | ||||||
| msgstr "" | msgstr "" | ||||||
| @@ -443,7 +444,7 @@ msgstr "" | |||||||
| #: lib/memex_web/live/note_live/index.ex:23 | #: lib/memex_web/live/note_live/index.ex:23 | ||||||
| #: lib/memex_web/live/note_live/show.ex:48 | #: lib/memex_web/live/note_live/show.ex:48 | ||||||
| #: lib/memex_web/live/pipeline_live/index.ex:23 | #: lib/memex_web/live/pipeline_live/index.ex:23 | ||||||
| #: lib/memex_web/live/pipeline_live/show.ex:48 | #: lib/memex_web/live/pipeline_live/show.ex:125 | ||||||
| #, elixir-autogen, elixir-format | #, elixir-autogen, elixir-format | ||||||
| msgid "edit %{slug}" | msgid "edit %{slug}" | ||||||
| msgstr "" | msgstr "" | ||||||
| @@ -460,7 +461,7 @@ msgstr "" | |||||||
|  |  | ||||||
| #: lib/memex_web/live/context_live/show.ex:19 | #: lib/memex_web/live/context_live/show.ex:19 | ||||||
| #: lib/memex_web/live/note_live/show.ex:19 | #: lib/memex_web/live/note_live/show.ex:19 | ||||||
| #: lib/memex_web/live/pipeline_live/show.ex:19 | #: lib/memex_web/live/pipeline_live/show.ex:20 | ||||||
| #, elixir-autogen, elixir-format | #, elixir-autogen, elixir-format | ||||||
| msgid "%{slug} could not be found" | msgid "%{slug} could not be found" | ||||||
| msgstr "" | msgstr "" | ||||||
| @@ -503,3 +504,48 @@ msgstr "" | |||||||
| #, elixir-autogen, elixir-format | #, elixir-autogen, elixir-format | ||||||
| msgid "what is this?" | msgid "what is this?" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
|  | #: lib/memex_web/live/pipeline_live/show.html.heex:62 | ||||||
|  | #, elixir-autogen, elixir-format | ||||||
|  | msgid "%{position}. %{title}" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: lib/memex_web/live/step_live/form_component.ex:67 | ||||||
|  | #, elixir-autogen, elixir-format | ||||||
|  | msgid "%{title} created" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: lib/memex_web/live/pipeline_live/show.ex:96 | ||||||
|  | #, elixir-autogen, elixir-format | ||||||
|  | msgid "%{title} deleted" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: lib/memex_web/live/step_live/form_component.ex:43 | ||||||
|  | #, elixir-autogen, elixir-format | ||||||
|  | msgid "%{title} saved" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: lib/memex_web/live/pipeline_live/show.ex:127 | ||||||
|  | #, elixir-autogen, elixir-format | ||||||
|  | msgid "add step to %{slug}" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: lib/memex_web/live/pipeline_live/show.html.heex:56 | ||||||
|  | #, elixir-autogen, elixir-format | ||||||
|  | msgid "no steps" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: lib/memex_web/live/pipeline_live/show.html.heex:51 | ||||||
|  | #, elixir-autogen, elixir-format | ||||||
|  | msgid "steps:" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: lib/memex_web/live/step_live/form_component.html.heex:14 | ||||||
|  | #, elixir-autogen, elixir-format | ||||||
|  | msgid "title" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: lib/memex_web/live/step_live/form_component.html.heex:23 | ||||||
|  | #, elixir-autogen, elixir-format | ||||||
|  | msgid "use [[context-slug]] to link to a context" | ||||||
|  | msgstr "" | ||||||
|   | |||||||
| @@ -146,7 +146,8 @@ msgstr "" | |||||||
| #: lib/memex_web/live/note_live/index.html.heex:46 | #: lib/memex_web/live/note_live/index.html.heex:46 | ||||||
| #: lib/memex_web/live/note_live/show.html.heex:35 | #: lib/memex_web/live/note_live/show.html.heex:35 | ||||||
| #: lib/memex_web/live/pipeline_live/index.html.heex:46 | #: lib/memex_web/live/pipeline_live/index.html.heex:46 | ||||||
| #: lib/memex_web/live/pipeline_live/show.html.heex:38 | #: lib/memex_web/live/pipeline_live/show.html.heex:40 | ||||||
|  | #: lib/memex_web/live/pipeline_live/show.html.heex:110 | ||||||
| #, elixir-autogen, elixir-format | #, elixir-autogen, elixir-format | ||||||
| msgid "are you sure?" | msgid "are you sure?" | ||||||
| msgstr "" | msgstr "" | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| defmodule Memex.StepsTest do | defmodule Memex.StepsTest do | ||||||
|   use Memex.DataCase |   use Memex.DataCase | ||||||
|   import Memex.{PipelinesFixtures, StepsFixtures} |   import Memex.{PipelinesFixtures, StepsFixtures} | ||||||
|   alias Memex.Pipelines.{Step, Steps} |   alias Memex.Pipelines.{Steps, Steps.Step} | ||||||
|   @moduletag :steps_test |   @moduletag :steps_test | ||||||
|   @invalid_attrs %{content: nil, title: nil} |   @invalid_attrs %{content: nil, title: nil} | ||||||
|  |  | ||||||
| @@ -13,19 +13,19 @@ defmodule Memex.StepsTest do | |||||||
|       [user: user, pipeline: pipeline] |       [user: user, pipeline: pipeline] | ||||||
|     end |     end | ||||||
|  |  | ||||||
|     test "list_steps/1 returns all steps for a user", %{pipeline: pipeline, user: user} do |     test "list_steps/2 returns all steps for a user", %{pipeline: pipeline, user: user} do | ||||||
|       step_a = step_fixture(0, pipeline, user) |       step_a = step_fixture(0, pipeline, user) | ||||||
|       step_b = step_fixture(1, pipeline, user) |       step_b = step_fixture(1, pipeline, user) | ||||||
|       step_c = step_fixture(2, pipeline, user) |       step_c = step_fixture(2, pipeline, user) | ||||||
|       assert Steps.list_steps(pipeline, user) == [step_a, step_b, step_c] |       assert Steps.list_steps(pipeline, user) == [step_a, step_b, step_c] | ||||||
|     end |     end | ||||||
|  |  | ||||||
|     test "get_step!/1 returns the step with given id", %{pipeline: pipeline, user: user} do |     test "get_step!/2 returns the step with given id", %{pipeline: pipeline, user: user} do | ||||||
|       step = step_fixture(0, pipeline, user) |       step = step_fixture(0, pipeline, user) | ||||||
|       assert Steps.get_step!(step.id, user) == step |       assert Steps.get_step!(step.id, user) == step | ||||||
|     end |     end | ||||||
|  |  | ||||||
|     test "get_step!/1 only returns unlisted or public steps for other users", %{user: user} do |     test "get_step!/2 only returns unlisted or public steps for other users", %{user: user} do | ||||||
|       another_user = user_fixture() |       another_user = user_fixture() | ||||||
|       another_pipeline = pipeline_fixture(another_user) |       another_pipeline = pipeline_fixture(another_user) | ||||||
|       step = step_fixture(0, another_pipeline, another_user) |       step = step_fixture(0, another_pipeline, another_user) | ||||||
| @@ -35,7 +35,7 @@ defmodule Memex.StepsTest do | |||||||
|       end |       end | ||||||
|     end |     end | ||||||
|  |  | ||||||
|     test "create_step/1 with valid data creates a step", %{pipeline: pipeline, user: user} do |     test "create_step/4 with valid data creates a step", %{pipeline: pipeline, user: user} do | ||||||
|       valid_attrs = %{ |       valid_attrs = %{ | ||||||
|         "content" => "some content", |         "content" => "some content", | ||||||
|         "title" => "some title" |         "title" => "some title" | ||||||
| @@ -46,12 +46,12 @@ defmodule Memex.StepsTest do | |||||||
|       assert step.title == "some title" |       assert step.title == "some title" | ||||||
|     end |     end | ||||||
|  |  | ||||||
|     test "create_step/1 with invalid data returns error changeset", |     test "create_step/4 with invalid data returns error changeset", | ||||||
|          %{pipeline: pipeline, user: user} do |          %{pipeline: pipeline, user: user} do | ||||||
|       assert {:error, %Ecto.Changeset{}} = Steps.create_step(@invalid_attrs, 0, pipeline, user) |       assert {:error, %Ecto.Changeset{}} = Steps.create_step(@invalid_attrs, 0, pipeline, user) | ||||||
|     end |     end | ||||||
|  |  | ||||||
|     test "update_step/2 with valid data updates the step", %{pipeline: pipeline, user: user} do |     test "update_step/3 with valid data updates the step", %{pipeline: pipeline, user: user} do | ||||||
|       step = step_fixture(0, pipeline, user) |       step = step_fixture(0, pipeline, user) | ||||||
|  |  | ||||||
|       update_attrs = %{ |       update_attrs = %{ | ||||||
| @@ -59,36 +59,90 @@ defmodule Memex.StepsTest do | |||||||
|         "title" => "some updated title" |         "title" => "some updated title" | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       assert {:ok, %Step{} = step} = Steps.update_step(step, update_attrs, 0, user) |       assert {:ok, %Step{} = step} = Steps.update_step(step, update_attrs, user) | ||||||
|       assert step.content == "some updated content" |       assert step.content == "some updated content" | ||||||
|       assert step.title == "some updated title" |       assert step.title == "some updated title" | ||||||
|     end |     end | ||||||
|  |  | ||||||
|     test "update_step/2 with invalid data returns error changeset", %{ |     test "update_step/3 with invalid data returns error changeset", %{ | ||||||
|       pipeline: pipeline, |       pipeline: pipeline, | ||||||
|       user: user |       user: user | ||||||
|     } do |     } do | ||||||
|       step = step_fixture(0, pipeline, user) |       step = step_fixture(0, pipeline, user) | ||||||
|       assert {:error, %Ecto.Changeset{}} = Steps.update_step(step, @invalid_attrs, 0, user) |       assert {:error, %Ecto.Changeset{}} = Steps.update_step(step, @invalid_attrs, user) | ||||||
|       assert step == Steps.get_step!(step.id, user) |       assert step == Steps.get_step!(step.id, user) | ||||||
|     end |     end | ||||||
|  |  | ||||||
|     test "delete_step/1 deletes the step", %{pipeline: pipeline, user: user} do |     test "delete_step/2 deletes the step", %{pipeline: pipeline, user: user} do | ||||||
|       step = step_fixture(0, pipeline, user) |       step = step_fixture(0, pipeline, user) | ||||||
|       assert {:ok, %Step{}} = Steps.delete_step(step, user) |       assert {:ok, %Step{}} = Steps.delete_step(step, user) | ||||||
|       assert_raise Ecto.NoResultsError, fn -> Steps.get_step!(step.id, user) end |       assert_raise Ecto.NoResultsError, fn -> Steps.get_step!(step.id, user) end | ||||||
|     end |     end | ||||||
|  |  | ||||||
|     test "delete_step/1 deletes the step for an admin user", %{pipeline: pipeline, user: user} do |     test "delete_step/2 moves past steps up", %{pipeline: pipeline, user: user} do | ||||||
|  |       first_step = step_fixture(0, pipeline, user) | ||||||
|  |       second_step = step_fixture(1, pipeline, user) | ||||||
|  |       assert {:ok, %Step{}} = Steps.delete_step(first_step, user) | ||||||
|  |       assert %{position: 0} = second_step |> Repo.reload!() | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     test "delete_step/2 deletes the step for an admin user", %{pipeline: pipeline, user: user} do | ||||||
|       admin_user = admin_fixture() |       admin_user = admin_fixture() | ||||||
|       step = step_fixture(0, pipeline, user) |       step = step_fixture(0, pipeline, user) | ||||||
|       assert {:ok, %Step{}} = Steps.delete_step(step, admin_user) |       assert {:ok, %Step{}} = Steps.delete_step(step, admin_user) | ||||||
|       assert_raise Ecto.NoResultsError, fn -> Steps.get_step!(step.id, user) end |       assert_raise Ecto.NoResultsError, fn -> Steps.get_step!(step.id, user) end | ||||||
|     end |     end | ||||||
|  |  | ||||||
|  |     test "change_step/2 returns a step changeset", %{pipeline: pipeline, user: user} do | ||||||
|  |       step = step_fixture(0, pipeline, user) | ||||||
|  |       assert %Ecto.Changeset{} = Steps.change_step(step, user) | ||||||
|  |     end | ||||||
|  |  | ||||||
|     test "change_step/1 returns a step changeset", %{pipeline: pipeline, user: user} do |     test "change_step/1 returns a step changeset", %{pipeline: pipeline, user: user} do | ||||||
|       step = step_fixture(0, pipeline, user) |       step = step_fixture(0, pipeline, user) | ||||||
|       assert %Ecto.Changeset{} = Steps.change_step(step, 0, user) |       assert %Ecto.Changeset{} = Steps.change_step(step, user) | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     test "reorder_step/1 reorders steps properly", %{pipeline: pipeline, user: user} do | ||||||
|  |       [ | ||||||
|  |         %{id: first_step_id} = first_step, | ||||||
|  |         %{id: second_step_id} = second_step, | ||||||
|  |         %{id: third_step_id} = third_step | ||||||
|  |       ] = Enum.map(0..2, fn index -> step_fixture(index, pipeline, user) end) | ||||||
|  |  | ||||||
|  |       Steps.reorder_step(third_step, :up, user) | ||||||
|  |  | ||||||
|  |       assert [ | ||||||
|  |                %{id: ^first_step_id, position: 0}, | ||||||
|  |                %{id: ^third_step_id, position: 1}, | ||||||
|  |                %{id: ^second_step_id, position: 2} | ||||||
|  |              ] = Steps.list_steps(pipeline, user) | ||||||
|  |  | ||||||
|  |       Steps.reorder_step(first_step, :up, user) | ||||||
|  |  | ||||||
|  |       assert [ | ||||||
|  |                %{id: ^first_step_id, position: 0}, | ||||||
|  |                %{id: ^third_step_id, position: 1}, | ||||||
|  |                %{id: ^second_step_id, position: 2} | ||||||
|  |              ] = Steps.list_steps(pipeline, user) | ||||||
|  |  | ||||||
|  |       second_step | ||||||
|  |       |> Repo.reload!() | ||||||
|  |       |> Steps.reorder_step(:down, user) | ||||||
|  |  | ||||||
|  |       assert [ | ||||||
|  |                %{id: ^first_step_id, position: 0}, | ||||||
|  |                %{id: ^third_step_id, position: 1}, | ||||||
|  |                %{id: ^second_step_id, position: 2} | ||||||
|  |              ] = Steps.list_steps(pipeline, user) | ||||||
|  |  | ||||||
|  |       Steps.reorder_step(first_step, :down, user) | ||||||
|  |  | ||||||
|  |       assert [ | ||||||
|  |                %{id: ^third_step_id, position: 0}, | ||||||
|  |                %{id: ^first_step_id, position: 1}, | ||||||
|  |                %{id: ^second_step_id, position: 2} | ||||||
|  |              ] = Steps.list_steps(pipeline, user) | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
| end | end | ||||||
|   | |||||||
| @@ -1,8 +1,7 @@ | |||||||
| defmodule MemexWeb.PipelineLiveTest do | defmodule MemexWeb.PipelineLiveTest do | ||||||
|   use MemexWeb.ConnCase |   use MemexWeb.ConnCase | ||||||
|  |  | ||||||
|   import Phoenix.LiveViewTest |   import Phoenix.LiveViewTest | ||||||
|   import Memex.PipelinesFixtures |   import Memex.{PipelinesFixtures, StepsFixtures} | ||||||
|  |  | ||||||
|   @create_attrs %{ |   @create_attrs %{ | ||||||
|     "description" => "some description", |     "description" => "some description", | ||||||
| @@ -22,6 +21,18 @@ defmodule MemexWeb.PipelineLiveTest do | |||||||
|     "slug" => nil, |     "slug" => nil, | ||||||
|     "visibility" => nil |     "visibility" => nil | ||||||
|   } |   } | ||||||
|  |   @step_create_attrs %{ | ||||||
|  |     "content" => "some content", | ||||||
|  |     "title" => "some title" | ||||||
|  |   } | ||||||
|  |   @step_update_attrs %{ | ||||||
|  |     "content" => "some updated content", | ||||||
|  |     "title" => "some updated title" | ||||||
|  |   } | ||||||
|  |   @step_invalid_attrs %{ | ||||||
|  |     "content" => nil, | ||||||
|  |     "title" => nil | ||||||
|  |   } | ||||||
|  |  | ||||||
|   defp create_pipeline(%{user: user}) do |   defp create_pipeline(%{user: user}) do | ||||||
|     [pipeline: pipeline_fixture(user)] |     [pipeline: pipeline_fixture(user)] | ||||||
| @@ -134,5 +145,110 @@ defmodule MemexWeb.PipelineLiveTest do | |||||||
|  |  | ||||||
|       refute has_element?(index_live, "#pipeline-#{pipeline.id}") |       refute has_element?(index_live, "#pipeline-#{pipeline.id}") | ||||||
|     end |     end | ||||||
|  |  | ||||||
|  |     test "creates a step", %{conn: conn, pipeline: pipeline} do | ||||||
|  |       {:ok, show_live, _html} = live(conn, Routes.pipeline_show_path(conn, :show, pipeline.slug)) | ||||||
|  |  | ||||||
|  |       show_live | ||||||
|  |       |> element("[data-qa=\"add-step-#{pipeline.id}\"]") | ||||||
|  |       |> render_click() | ||||||
|  |  | ||||||
|  |       assert_patch(show_live, Routes.pipeline_show_path(conn, :add_step, pipeline.slug)) | ||||||
|  |  | ||||||
|  |       {:ok, _show_live, html} = | ||||||
|  |         show_live | ||||||
|  |         |> form("#step-form", step: @step_create_attrs) | ||||||
|  |         |> render_submit() | ||||||
|  |         |> follow_redirect(conn, Routes.pipeline_show_path(conn, :show, pipeline.slug)) | ||||||
|  |  | ||||||
|  |       assert html =~ "some title created" | ||||||
|  |       assert html =~ "some description" | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   describe "show with a step" do | ||||||
|  |     setup [:register_and_log_in_user, :create_pipeline] | ||||||
|  |  | ||||||
|  |     setup %{pipeline: pipeline, user: user} do | ||||||
|  |       [ | ||||||
|  |         step: step_fixture(0, pipeline, user) | ||||||
|  |       ] | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     test "updates a step", %{conn: conn, pipeline: pipeline, step: step} do | ||||||
|  |       {:ok, show_live, _html} = live(conn, Routes.pipeline_show_path(conn, :show, pipeline.slug)) | ||||||
|  |  | ||||||
|  |       show_live | ||||||
|  |       |> element("[data-qa=\"edit-step-#{step.id}\"]") | ||||||
|  |       |> render_click() | ||||||
|  |  | ||||||
|  |       assert_patch(show_live, Routes.pipeline_show_path(conn, :edit_step, pipeline.slug, step.id)) | ||||||
|  |  | ||||||
|  |       assert show_live | ||||||
|  |              |> form("#step-form", step: @step_invalid_attrs) | ||||||
|  |              |> render_change() =~ "can't be blank" | ||||||
|  |  | ||||||
|  |       {:ok, _show_live, html} = | ||||||
|  |         show_live | ||||||
|  |         |> form("#step-form", step: @step_update_attrs) | ||||||
|  |         |> render_submit() | ||||||
|  |         |> follow_redirect(conn, Routes.pipeline_show_path(conn, :show, pipeline.slug)) | ||||||
|  |  | ||||||
|  |       assert html =~ "some updated title saved" | ||||||
|  |       assert html =~ "some updated content" | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     test "deletes a step", %{conn: conn, pipeline: pipeline, step: step} do | ||||||
|  |       {:ok, show_live, _html} = live(conn, Routes.pipeline_show_path(conn, :show, pipeline.slug)) | ||||||
|  |  | ||||||
|  |       html = | ||||||
|  |         show_live | ||||||
|  |         |> element("[data-qa=\"delete-step-#{step.id}\"]") | ||||||
|  |         |> render_click() | ||||||
|  |  | ||||||
|  |       assert_patch(show_live, Routes.pipeline_show_path(conn, :show, pipeline.slug)) | ||||||
|  |  | ||||||
|  |       assert html =~ "some title deleted" | ||||||
|  |       refute html =~ "some updated content" | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   describe "show with multiple steps" do | ||||||
|  |     setup [:register_and_log_in_user, :create_pipeline] | ||||||
|  |  | ||||||
|  |     setup %{pipeline: pipeline, user: user} do | ||||||
|  |       [ | ||||||
|  |         first_step: step_fixture(%{title: "first step"}, 0, pipeline, user), | ||||||
|  |         second_step: step_fixture(%{title: "second step"}, 1, pipeline, user), | ||||||
|  |         third_step: step_fixture(%{title: "third step"}, 2, pipeline, user) | ||||||
|  |       ] | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     test "reorders a step", | ||||||
|  |          %{conn: conn, pipeline: pipeline, first_step: first_step, second_step: second_step} do | ||||||
|  |       {:ok, show_live, _html} = live(conn, Routes.pipeline_show_path(conn, :show, pipeline.slug)) | ||||||
|  |  | ||||||
|  |       html = | ||||||
|  |         show_live | ||||||
|  |         |> element("[data-qa=\"move-step-up-#{second_step.id}\"]") | ||||||
|  |         |> render_click() | ||||||
|  |  | ||||||
|  |       assert html =~ "1. second step" | ||||||
|  |       assert html =~ "2. first step" | ||||||
|  |       assert html =~ "3. third step" | ||||||
|  |  | ||||||
|  |       refute has_element?(show_live, "[data-qa=\"move-step-up-#{second_step.id}\"]") | ||||||
|  |  | ||||||
|  |       html = | ||||||
|  |         show_live | ||||||
|  |         |> element("[data-qa=\"move-step-down-#{first_step.id}\"]") | ||||||
|  |         |> render_click() | ||||||
|  |  | ||||||
|  |       assert html =~ "1. second step" | ||||||
|  |       assert html =~ "2. third step" | ||||||
|  |       assert html =~ "3. first step" | ||||||
|  |  | ||||||
|  |       refute has_element?(show_live, "[data-qa=\"move-step-down-#{first_step.id}\"]") | ||||||
|  |     end | ||||||
|   end |   end | ||||||
| end | end | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user