diff --git a/lib/memex/pipelines/pipeline.ex b/lib/memex/pipelines/pipeline.ex
index 17b0d71..1a86d74 100644
--- a/lib/memex/pipelines/pipeline.ex
+++ b/lib/memex/pipelines/pipeline.ex
@@ -6,7 +6,7 @@ defmodule Memex.Pipelines.Pipeline do
import Ecto.Changeset
import MemexWeb.Gettext
alias Ecto.{Changeset, UUID}
- alias Memex.{Accounts.User, Pipelines.Step}
+ alias Memex.{Accounts.User, Pipelines.Steps.Step}
@primary_key {:id, :binary_id, autogenerate: true}
@foreign_key_type :binary_id
diff --git a/lib/memex/pipelines/step.ex b/lib/memex/pipelines/step.ex
index f7df400..18727de 100644
--- a/lib/memex/pipelines/step.ex
+++ b/lib/memex/pipelines/step.ex
@@ -1,4 +1,4 @@
-defmodule Memex.Pipelines.Step do
+defmodule Memex.Pipelines.Steps.Step do
@moduledoc """
Represents a step taken while executing a pipeline
"""
@@ -46,16 +46,25 @@ defmodule Memex.Pipelines.Step do
|> validate_required([:title, :content, :user_id, :position])
end
- @spec update_changeset(t(), attrs :: map(), position :: non_neg_integer(), User.t()) ::
+ @spec update_changeset(t(), attrs :: map(), User.t()) ::
changeset()
def update_changeset(
%{user_id: user_id} = step,
attrs,
- position,
%User{id: user_id}
) do
step
|> 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)
|> validate_required([:title, :content, :user_id, :position])
end
diff --git a/lib/memex/pipelines/steps.ex b/lib/memex/pipelines/steps.ex
index 0967a44..d33de67 100644
--- a/lib/memex/pipelines/steps.ex
+++ b/lib/memex/pipelines/steps.ex
@@ -4,8 +4,9 @@ defmodule Memex.Pipelines.Steps do
"""
import Ecto.Query, warn: false
+ alias Ecto.Multi
alias Memex.{Accounts.User, Repo}
- alias Memex.Pipelines.{Pipeline, Step}
+ alias Memex.Pipelines.{Pipeline, Steps.Step}
@doc """
Returns the list of steps.
@@ -105,11 +106,11 @@ defmodule Memex.Pipelines.Steps do
{: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()}
- def update_step(%Step{} = step, attrs, position, user) do
+ def update_step(%Step{} = step, attrs, user) do
step
- |> Step.update_changeset(attrs, position, user)
+ |> Step.update_changeset(attrs, user)
|> Repo.update()
end
@@ -130,11 +131,31 @@ defmodule Memex.Pipelines.Steps do
"""
@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
- step |> Repo.delete()
+ delete_step(step)
end
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
@doc """
@@ -149,11 +170,69 @@ defmodule Memex.Pipelines.Steps do
%Ecto.Changeset{data: %Step{}}
"""
- @spec change_step(Step.t(), position :: non_neg_integer(), Pipeline.t(), User.t()) ::
- Step.changeset()
- @spec change_step(Step.t(), attrs :: map(), position :: non_neg_integer(), User.t()) ::
- Step.changeset()
- def change_step(%Step{} = step, attrs \\ %{}, position, user) do
- step |> Step.update_changeset(attrs, position, user)
+ @spec change_step(Step.t(), User.t()) :: Step.changeset()
+ @spec change_step(Step.t(), attrs :: map(), User.t()) :: Step.changeset()
+ def change_step(%Step{} = step, attrs \\ %{}, user) do
+ step |> Step.update_changeset(attrs, user)
+ end
+
+ @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
diff --git a/lib/memex_web/components/step_content.ex b/lib/memex_web/components/step_content.ex
new file mode 100644
index 0000000..75a8595
--- /dev/null
+++ b/lib/memex_web/components/step_content.ex
@@ -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"""
+
<%= add_links_to_content(@step.content) %>
+ """
+ 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()
+
+ "#{link}"
+ end
+ )
+ |> HTML.raw()
+ end
+end
diff --git a/lib/memex_web/live/pipeline_live/show.ex b/lib/memex_web/live/pipeline_live/show.ex
index bc2a609..8fe07a7 100644
--- a/lib/memex_web/live/pipeline_live/show.ex
+++ b/lib/memex_web/live/pipeline_live/show.ex
@@ -1,7 +1,8 @@
defmodule MemexWeb.PipelineLive.Show do
use MemexWeb, :live_view
-
- alias Memex.{Accounts.User, Pipelines, Pipelines.Pipeline}
+ import MemexWeb.Components.StepContent
+ alias Memex.{Accounts.User, Pipelines}
+ alias Memex.Pipelines.{Pipeline, Steps, Steps.Step}
@impl true
def mount(_params, _session, socket) do
@@ -10,9 +11,9 @@ defmodule MemexWeb.PipelineLive.Show do
@impl true
def handle_params(
- %{"slug" => slug},
- _,
- %{assigns: %{live_action: live_action, current_user: current_user}} = socket
+ %{"slug" => slug} = params,
+ _url,
+ %{assigns: %{current_user: current_user, live_action: live_action}} = socket
) do
pipeline =
case Pipelines.get_pipeline_by_slug(slug, current_user) do
@@ -24,10 +25,45 @@ defmodule MemexWeb.PipelineLive.Show do
socket
|> assign(:page_title, page_title(live_action, pipeline))
|> assign(:pipeline, pipeline)
+ |> assign(:steps, pipeline |> Steps.list_steps(current_user))
+ |> apply_action(live_action, params)
{:noreply, socket}
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
def handle_event(
"delete",
@@ -44,8 +80,51 @@ defmodule MemexWeb.PipelineLive.Show do
{:noreply, socket}
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(: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()
defp is_owner_or_admin?(%{user_id: user_id}, %{id: user_id}), do: true
diff --git a/lib/memex_web/live/pipeline_live/show.html.heex b/lib/memex_web/live/pipeline_live/show.html.heex
index 102e4b4..4072ba6 100644
--- a/lib/memex_web/live/pipeline_live/show.html.heex
+++ b/lib/memex_web/live/pipeline_live/show.html.heex
@@ -5,20 +5,22 @@
<%= if @pipeline.tags, do: @pipeline.tags |> Enum.join(", ") %>
-
+ <%= if @pipeline.description do %>
+
+ <% end %>
<%= gettext("Visibility: %{visibility}", visibility: @pipeline.visibility) %>
-
+
<.link class="btn btn-primary" navigate={Routes.pipeline_index_path(@socket, :index)}>
<%= dgettext("actions", "back") %>
@@ -42,18 +44,131 @@
<% end %>
+
+
+
+
+ <%= gettext("steps:") %>
+
+
+ <%= if @steps |> Enum.empty?() do %>
+
+ <%= gettext("no steps") %>
+
+ <% else %>
+ <%= for %{id: step_id, position: position, title: title} = step <- @steps do %>
+
+
+ <%= gettext("%{position}. %{title}", position: position + 1, title: title) %>
+
+
+ <%= if is_owner?(@pipeline, @current_user) do %>
+
+ <%= if position <= 0 do %>
+
+ <% else %>
+
+ <% end %>
+
+ <%= if position >= length(@steps) - 1 do %>
+
+ <% else %>
+
+ <% 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") %>
+
+
+
+
+ <% end %>
+
+
+ <.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") %>
+
+ <% end %>
-<%= if @live_action in [:edit] do %>
- <.modal return_to={Routes.pipeline_show_path(@socket, :show, @pipeline.slug)}>
- <.live_component
- module={MemexWeb.PipelineLive.FormComponent}
- id={@pipeline.id}
- current_user={@current_user}
- title={@page_title}
- action={@live_action}
- pipeline={@pipeline}
- return_to={Routes.pipeline_show_path(@socket, :show, @pipeline.slug)}
- />
-
+<%= case @live_action do %>
+ <% :edit -> %>
+ <.modal return_to={Routes.pipeline_show_path(@socket, :show, @pipeline.slug)}>
+ <.live_component
+ module={MemexWeb.PipelineLive.FormComponent}
+ id={@pipeline.id}
+ current_user={@current_user}
+ title={@page_title}
+ action={@live_action}
+ pipeline={@pipeline}
+ return_to={Routes.pipeline_show_path(@socket, :show, @pipeline.slug)}
+ />
+
+ <% :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)}
+ />
+
+ <% :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)}
+ />
+
+ <% _ -> %>
<% end %>
diff --git a/lib/memex_web/live/step_live/form_component.ex b/lib/memex_web/live/step_live/form_component.ex
new file mode 100644
index 0000000..32ab236
--- /dev/null
+++ b/lib/memex_web/live/step_live/form_component.ex
@@ -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
diff --git a/lib/memex_web/live/step_live/form_component.html.heex b/lib/memex_web/live/step_live/form_component.html.heex
new file mode 100644
index 0000000..19623df
--- /dev/null
+++ b/lib/memex_web/live/step_live/form_component.html.heex
@@ -0,0 +1,34 @@
+
+ <.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) %>
+
+
+ <%= submit(dgettext("actions", "save"),
+ phx_disable_with: gettext("saving..."),
+ class: "mx-auto btn btn-primary"
+ ) %>
+
+
+
diff --git a/lib/memex_web/router.ex b/lib/memex_web/router.ex
index e8e795c..f20f3c5 100644
--- a/lib/memex_web/router.ex
+++ b/lib/memex_web/router.ex
@@ -69,6 +69,8 @@ defmodule MemexWeb.Router do
live "/pipelines/new", PipelineLive.Index, :new
live "/pipelines/:slug/edit", PipelineLive.Index, :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
put "/users/settings", UserSettingsController, :update
diff --git a/priv/gettext/actions.pot b/priv/gettext/actions.pot
index 4500be4..01cee7f 100644
--- a/priv/gettext/actions.pot
+++ b/priv/gettext/actions.pot
@@ -72,7 +72,8 @@ msgstr ""
#: lib/memex_web/live/note_live/index.html.heex:49
#: 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/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
msgid "delete"
msgstr ""
@@ -87,7 +88,8 @@ msgstr ""
#: lib/memex_web/live/note_live/index.html.heex:38
#: 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/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
msgid "edit"
msgstr ""
@@ -137,13 +139,19 @@ msgstr ""
#: 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/pipeline_live/form_component.html.heex:42
+#: lib/memex_web/live/step_live/form_component.html.heex:28
#, elixir-autogen, elixir-format
msgid "save"
msgstr ""
#: lib/memex_web/live/context_live/show.html.heex:16
#: 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
msgid "back"
msgstr ""
+
+#: lib/memex_web/live/pipeline_live/show.html.heex:129
+#, elixir-autogen, elixir-format
+msgid "add step"
+msgstr ""
diff --git a/priv/gettext/default.pot b/priv/gettext/default.pot
index 219a105..3cb050d 100644
--- a/priv/gettext/default.pot
+++ b/priv/gettext/default.pot
@@ -82,7 +82,7 @@ msgstr ""
#: lib/memex_web/live/context_live/show.html.heex:11
#: 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
msgid "Visibility: %{visibility}"
msgstr ""
@@ -309,6 +309,7 @@ msgstr ""
#: 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/pipeline_live/form_component.html.heex:43
+#: lib/memex_web/live/step_live/form_component.html.heex:29
#, elixir-autogen, elixir-format
msgid "saving..."
msgstr ""
@@ -426,7 +427,7 @@ msgstr ""
#: lib/memex_web/live/note_live/index.ex:57
#: lib/memex_web/live/note_live/show.ex:41
#: 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
msgid "%{slug} deleted"
msgstr ""
@@ -443,7 +444,7 @@ msgstr ""
#: lib/memex_web/live/note_live/index.ex:23
#: lib/memex_web/live/note_live/show.ex:48
#: 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
msgid "edit %{slug}"
msgstr ""
@@ -460,7 +461,7 @@ msgstr ""
#: lib/memex_web/live/context_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
msgid "%{slug} could not be found"
msgstr ""
@@ -503,3 +504,48 @@ msgstr ""
#, elixir-autogen, elixir-format
msgid "what is this?"
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 ""
diff --git a/priv/gettext/prompts.pot b/priv/gettext/prompts.pot
index 3217299..355ae8f 100644
--- a/priv/gettext/prompts.pot
+++ b/priv/gettext/prompts.pot
@@ -146,7 +146,8 @@ msgstr ""
#: lib/memex_web/live/note_live/index.html.heex:46
#: 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/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
msgid "are you sure?"
msgstr ""
diff --git a/test/memex/steps_test.exs b/test/memex/steps_test.exs
index ab8467c..486cfef 100644
--- a/test/memex/steps_test.exs
+++ b/test/memex/steps_test.exs
@@ -1,7 +1,7 @@
defmodule Memex.StepsTest do
use Memex.DataCase
import Memex.{PipelinesFixtures, StepsFixtures}
- alias Memex.Pipelines.{Step, Steps}
+ alias Memex.Pipelines.{Steps, Steps.Step}
@moduletag :steps_test
@invalid_attrs %{content: nil, title: nil}
@@ -13,19 +13,19 @@ defmodule Memex.StepsTest do
[user: user, pipeline: pipeline]
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_b = step_fixture(1, pipeline, user)
step_c = step_fixture(2, pipeline, user)
assert Steps.list_steps(pipeline, user) == [step_a, step_b, step_c]
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)
assert Steps.get_step!(step.id, user) == step
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_pipeline = pipeline_fixture(another_user)
step = step_fixture(0, another_pipeline, another_user)
@@ -35,7 +35,7 @@ defmodule Memex.StepsTest do
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 = %{
"content" => "some content",
"title" => "some title"
@@ -46,12 +46,12 @@ defmodule Memex.StepsTest do
assert step.title == "some title"
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
assert {:error, %Ecto.Changeset{}} = Steps.create_step(@invalid_attrs, 0, pipeline, user)
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)
update_attrs = %{
@@ -59,36 +59,90 @@ defmodule Memex.StepsTest do
"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.title == "some updated title"
end
- test "update_step/2 with invalid data returns error changeset", %{
+ test "update_step/3 with invalid data returns error changeset", %{
pipeline: pipeline,
user: user
} do
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)
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)
assert {:ok, %Step{}} = Steps.delete_step(step, user)
assert_raise Ecto.NoResultsError, fn -> Steps.get_step!(step.id, user) 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()
step = step_fixture(0, pipeline, user)
assert {:ok, %Step{}} = Steps.delete_step(step, admin_user)
assert_raise Ecto.NoResultsError, fn -> Steps.get_step!(step.id, user) 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
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
diff --git a/test/memex_web/live/pipeline_live_test.exs b/test/memex_web/live/pipeline_live_test.exs
index afa41c3..08dd23b 100644
--- a/test/memex_web/live/pipeline_live_test.exs
+++ b/test/memex_web/live/pipeline_live_test.exs
@@ -1,8 +1,7 @@
defmodule MemexWeb.PipelineLiveTest do
use MemexWeb.ConnCase
-
import Phoenix.LiveViewTest
- import Memex.PipelinesFixtures
+ import Memex.{PipelinesFixtures, StepsFixtures}
@create_attrs %{
"description" => "some description",
@@ -22,6 +21,18 @@ defmodule MemexWeb.PipelineLiveTest do
"slug" => 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
[pipeline: pipeline_fixture(user)]
@@ -134,5 +145,110 @@ defmodule MemexWeb.PipelineLiveTest do
refute has_element?(index_live, "#pipeline-#{pipeline.id}")
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