diff --git a/assets/js/app.js b/assets/js/app.js index e1ec7c9..5413b05 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -18,8 +18,135 @@ import "phoenix_html" import {Socket} from "phoenix" import LiveSocket from "phoenix_live_view" +let Hooks = {} + +Hooks.Dragger = { + toggleAddDelete() { + const deleter = document.getElementById('deleter') + if (deleter) { + const adder = document.getElementById('adder') + deleter.hidden = adder.hidden + adder.hidden = !adder.hidden + } + }, + + get dragImage() { + const canvas = document.createElement("canvas") + canvas.width = canvas.height = 60 + const ctx = canvas.getContext("2d") + + ctx.beginPath() + ctx.arc(30, 30, 30, 0, 2 * Math.PI) + ctx.fill() + + return canvas + }, + + mounted() { + this.el.addEventListener("dragstart", event => { + event.dataTransfer.setData("text/plain", `task-id:${this.el.dataset.taskId}`) + event.dataTransfer.setDragImage(this.dragImage, 25, 25) + this.toggleAddDelete() + }) + + this.el.addEventListener("dragend", event => { + this.toggleAddDelete() + }) + }, +} + +Hooks.AddDropper = { + get bgClass() { return "has-background-warning"}, + + addTaskToPlan(payload) { return this.pushEvent("add-task-to-plan", payload) }, + + getTaskPayload(event) { return event.dataTransfer.getData("text/plain") }, + + parseTaskPayload(payload) { return payload.startsWith("task-id:") ? payload.split(":")[1] : null }, + + addHoverClass(event) { event.target.classList.add(this.bgClass) }, + + removeHoverClass(event) { event.target.classList.remove(this.bgClass) }, + + mounted() { + this.el.addEventListener("drop", event => { + event.preventDefault() + const payload = this.getTaskPayload(event) + const taskID = this.parseTaskPayload(payload) + if (taskID !== null) { + const planID = this.el.dataset.planId + this.addTaskToPlan({ "task_id": taskID, "plan_id": planID, }) + this.removeHoverClass(event) + } + }) + + this.el.addEventListener("dragover", event => event.preventDefault()) + + this.el.addEventListener("dragenter", event => { + const payload = this.getTaskPayload(event) + const taskID = this.parseTaskPayload(payload) + if (taskID !== null) { this.addHoverClass(event) } + }) + + this.el.addEventListener("dragleave", event => { + const payload = this.getTaskPayload(event) + const taskID = this.parseTaskPayload(payload) + if (taskID !== null) { this.removeHoverClass(event) } + }) + } +} + +Hooks.DeleteDropper = { + get hoverBGClass() { return "has-background-warning"}, + + get baseBGClass() { return "has-background-danger"}, + + deleteTaskFromPlan(payload) { return this.pushEvent("delete-task-from-plan", payload) }, + + getTaskPayload(event) { return event.dataTransfer.getData("text/plain") }, + + parseTaskPayload(payload) { return payload.startsWith("task-id:") ? payload.split(":")[1] : null }, + + addHoverClass(event) { + event.target.classList.add(this.hoverBGClass) + event.target.classList.remove(this.baseBGClass) + }, + + removeHoverClass(event) { + event.target.classList.remove(this.hoverBGClass) + event.target.classList.add(this.baseBGClass) + }, + + mounted() { + this.el.addEventListener("drop", event => { + event.preventDefault() + const payload = this.getTaskPayload(event) + const taskID = this.parseTaskPayload(payload) + if (taskID !== null) { + const planID = this.el.dataset.drop + this.deleteTaskFromPlan({ "task_id": taskID, "plan_id": planID, }) + this.removeHoverClass(event) + } + }) + + this.el.addEventListener("dragover", event => event.preventDefault()) + + this.el.addEventListener("dragenter", event => { + const payload = this.getTaskPayload(event) + const taskID = this.parseTaskPayload(payload) + if (taskID !== null) { this.addHoverClass(event) } + }) + + this.el.addEventListener("dragleave", event => { + const payload = this.getTaskPayload(event) + const taskID = this.parseTaskPayload(payload) + if (taskID !== null) { this.removeHoverClass(event) } + }) + } +} + let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content") -let liveSocket = new LiveSocket("/live", Socket, {params: {_csrf_token: csrfToken}}) +let liveSocket = new LiveSocket("/live", Socket, {params: {_csrf_token: csrfToken}, hooks: Hooks}) // Connect if there are any LiveViews on the page liveSocket.connect() @@ -33,15 +160,15 @@ liveSocket.connect() window.liveSocket = liveSocket document.addEventListener('DOMContentLoaded', () => { - const $navbarBurgers = Array.prototype.slice.call(document.querySelectorAll('.navbar-burger'), 0); + const $navbarBurgers = Array.prototype.slice.call(document.querySelectorAll('.navbar-burger'), 0) if ($navbarBurgers.length > 0) { $navbarBurgers.forEach( el => { el.addEventListener('click', () => { - const target = el.dataset.target; - const $target = document.getElementById(target); - el.classList.toggle('is-active'); - $target.classList.toggle('is-active'); - }); - }); + const target = el.dataset.target + const $target = document.getElementById(target) + el.classList.toggle('is-active') + $target.classList.toggle('is-active') + }) + }) } -}); +}) diff --git a/lib/planner/tasks.ex b/lib/planner/tasks.ex index 7088335..fab47c3 100644 --- a/lib/planner/tasks.ex +++ b/lib/planner/tasks.ex @@ -1,8 +1,12 @@ defmodule Planner.Tasks do import Ecto.Query + + alias Ecto.Multi alias Ecto.UUID alias Planner.Repo alias Planner.Tasks.Task + alias Planner.Tasks.Plan + alias Planner.Tasks.PlanDetail def list_all_tasks, do: Repo.all(Task) @@ -15,6 +19,20 @@ defmodule Planner.Tasks do |> Repo.all() end + def list_unfinished_tasks_by_plan_id(plan_id) do + q = + Ecto.Query.from( + t in Task, + join: pd in PlanDetail, + on: t.id == pd.task_id, + where: pd.plan_id == ^plan_id and is_nil(t.finished_at), + order_by: [desc: t.updated_at] + ) + + Repo.all(q) + |> Repo.preload(:plans) + end + def list_finished_tasks do from( t in Task, @@ -32,6 +50,15 @@ defmodule Planner.Tasks do |> Repo.insert() end + def create_task_and_add_to_plan(task_attrs, plan) do + Multi.new() + |> Multi.insert(:task, Task.changeset(%Task{}, task_attrs)) + |> Multi.run(:plan_detail, fn _repo, %{task: task} -> + create_plan_detail(%{"task_id" => task.id, "plan_id" => plan.id}) + end) + |> Repo.transaction() + end + def update_task(%Task{} = task, attrs) do task |> Task.changeset(attrs) @@ -69,4 +96,89 @@ defmodule Planner.Tasks do _ -> task_exists?(task_id) end end + + def list_plans do + Repo.all(Plan) + end + + def list_unfinished_plans do + from( + p in Plan, + where: is_nil(p.finished_at), + order_by: [desc: p.updated_at] + ) + |> Repo.all() + end + + def get_plan!(id), do: Repo.get!(Plan, id) + + def create_plan(attrs \\ %{}) do + %Plan{} + |> Plan.changeset(attrs) + |> Repo.insert() + end + + def update_plan(%Plan{} = plan, attrs) do + plan + |> Plan.changeset(attrs) + |> Repo.update() + end + + def delete_plan(%Plan{} = plan) do + Repo.delete(plan) + end + + def change_plan(%Plan{} = plan, attrs \\ %{}) do + Plan.changeset(plan, attrs) + end + + def plan_exists?(id), do: Repo.exists?(from(p in Plan, where: p.id == ^id)) + + def finish_plan_by_id!(id) do + get_plan!(id) + |> Plan.finish_plan() + |> Repo.update() + end + + def verify_plan_id_from_url(plan_id) do + plan_id = + case UUID.dump(plan_id) do + # don't actually want the dumped UUID, so discard + {:ok, _} -> plan_id + :error -> :error + end + + case plan_id do + :error -> :error + _ -> plan_exists?(plan_id) + end + end + + def list_plan_details do + Repo.all(PlanDetail) + end + + def get_plan_detail!(id), do: Repo.get!(PlanDetail, id) + + def get_plan_detail_by!(clauses), do: Repo.get_by!(PlanDetail, clauses) + + def create_plan_detail(attrs \\ %{}, on_conflict \\ :nothing) do + %PlanDetail{} + |> PlanDetail.changeset(attrs) + |> Repo.insert(on_conflict: on_conflict) + end + + def update_plan_detail(%PlanDetail{} = plan_detail, attrs) do + plan_detail + |> PlanDetail.changeset(attrs) + |> Repo.update() + end + + def delete_plan_detail(%PlanDetail{} = plan_detail) do + Repo.delete(plan_detail) + end + + def change_plan_detail(%PlanDetail{} = plan_detail, attrs \\ %{}) do + PlanDetail.changeset(plan_detail, attrs) + end end diff --git a/lib/planner/tasks/plan.ex b/lib/planner/tasks/plan.ex new file mode 100644 index 0000000..77cd977 --- /dev/null +++ b/lib/planner/tasks/plan.ex @@ -0,0 +1,29 @@ +defmodule Planner.Tasks.Plan do + use Ecto.Schema + import Ecto.Changeset + + @primary_key {:id, :binary_id, autogenerate: true} + @foreign_key_type :binary_id + schema "plans" do + field :description, :string + field :finished_at, :naive_datetime + field :end, :naive_datetime + field :name, :string + field :start, :naive_datetime + + timestamps() + end + + def changeset(plan, attrs) do + plan + |> cast(attrs, [:description, :finished_at, :start, :end, :name]) + |> validate_required([:name]) + end + + def finish_plan(plan) do + # TODO, this should check if `finished_at` is not nil, first + change(plan, finished_at: now()) + end + + defp now(), do: NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second) +end diff --git a/lib/planner/tasks/plan_detail.ex b/lib/planner/tasks/plan_detail.ex new file mode 100644 index 0000000..87fe14f --- /dev/null +++ b/lib/planner/tasks/plan_detail.ex @@ -0,0 +1,19 @@ +defmodule Planner.Tasks.PlanDetail do + use Ecto.Schema + import Ecto.Changeset + + @primary_key {:id, :binary_id, autogenerate: true} + @foreign_key_type :binary_id + schema "plan_details" do + field :sort, :integer + field :task_id, :binary_id + field :plan_id, :binary_id + + timestamps() + end + + def changeset(plan_detail, attrs) do + plan_detail + |> cast(attrs, [:sort, :task_id, :plan_id]) + end +end diff --git a/lib/planner/tasks/task.ex b/lib/planner/tasks/task.ex index 1235370..7b73d4b 100644 --- a/lib/planner/tasks/task.ex +++ b/lib/planner/tasks/task.ex @@ -10,6 +10,8 @@ defmodule Planner.Tasks.Task do field(:finished_at, :naive_datetime) field(:due_at, :naive_datetime) + many_to_many(:plans, Planner.Tasks.Plan, join_through: "plan_details", on_delete: :delete_all) + timestamps() end diff --git a/lib/planner_web/live/tasks_components.ex b/lib/planner_web/live/tasks_components.ex index a689a39..66ab8d7 100644 --- a/lib/planner_web/live/tasks_components.ex +++ b/lib/planner_web/live/tasks_components.ex @@ -21,7 +21,7 @@ defmodule TasksComponent do ~L"""
<%= f = form_for(@changeset, "#", [phx_submit: "new-task"]) %> -
+
<%= text_input(f, :value, @@ -33,6 +33,17 @@ defmodule TasksComponent do
+ <%= if(!is_nil(@active_plan)) do %> + + <% end %> +
    <%= for task <- @tasks do %> <%= live_component(@socket, @@ -73,7 +84,7 @@ defmodule TaskComponent do
    <%= if(@is_active) do %> <%= case @live_action do %> - <% :show -> %> + <% :show_task -> %> <%= live_component(@socket, TaskDetailsComponent, id: "task_details:#{@task.id}", @@ -81,7 +92,7 @@ defmodule TaskComponent do route_index_tasks: @route_index_tasks, route_edit_task: @route_edit_task )%> - <% :edit -> %> + <% :edit_task -> %> <%= live_component(@socket, TaskEditComponent, id: "task_edit:#{@task.id}", @@ -92,7 +103,7 @@ defmodule TaskComponent do <%= live_patch(to: @route_show_task.(@socket, @task.id), style: "display: block;" ) do %> -
    +
    <%= md_to_html(@task.value) %>
    <% end %> @@ -116,7 +127,7 @@ defmodule TaskDetailsComponent do def render(assigns) do ~L""" -
    +
    <%= live_patch("", to: @route_index_tasks.(@socket), class: "delete is-pulled-right" diff --git a/lib/planner_web/live/tasks_live.ex b/lib/planner_web/live/tasks_live.ex index 552eee2..82112d5 100644 --- a/lib/planner_web/live/tasks_live.ex +++ b/lib/planner_web/live/tasks_live.ex @@ -2,48 +2,165 @@ defmodule PlannerWeb.TasksLive do use PlannerWeb, :live_view alias Planner.Tasks + alias Planner.Tasks.Plan def mount(_params, _session, socket) do socket = - socket - |> assign(:tasks, Tasks.list_unfinished_tasks()) - |> assign(:active_task, nil) - - {:ok, socket} + socket + |> assign(:plans, Tasks.list_unfinished_plans()) + |> assign(:plan_changeset, Tasks.change_plan(%Plan{})) + {:ok, socket} end - def handle_params(%{"task_id" => task_id}, _, socket) do - case Tasks.verify_task_id_from_url(task_id) do - true -> {:noreply, assign(socket, :active_task, task_id)} - _ -> {:noreply, push_patch(socket, to: Routes.tasks_path(socket, :index))} + # plan: yes, task: yes + def handle_params(%{"plan_id" => plan_id, "task_id" => task_id}, _, socket) do + case Tasks.verify_plan_id_from_url(plan_id) and Tasks.verify_task_id_from_url(task_id) do + true -> + socket = + socket + |> assign(:active_task, task_id) + |> assign(:active_plan, Tasks.get_plan!(plan_id)) + |> assign(:tasks, Tasks.list_unfinished_tasks_by_plan_id(plan_id)) + |> add_plan_routes(plan_id) + + {:noreply, socket} + + _ -> + {:noreply, push_patch(socket, to: Routes.tasks_path(socket, :index))} end end + # plan: no, task: yes + def handle_params(%{"task_id" => task_id}, _, socket) do + case Tasks.verify_task_id_from_url(task_id) do + true -> + socket = + socket + |> assign(:active_task, task_id) + |> assign(:active_plan, nil) + |> assign(:tasks, Tasks.list_unfinished_tasks()) + |> add_task_routes() + + {:noreply, assign(socket, :active_task, task_id)} + + _ -> + {:noreply, push_patch(socket, to: Routes.tasks_path(socket, :index))} + end + end + + # plan: yes, task: no + def handle_params(%{"plan_id" => plan_id}, _, socket) do + case Tasks.verify_plan_id_from_url(plan_id) do + true -> + socket = + socket + |> assign(:active_task, nil) + |> assign(:active_plan, Tasks.get_plan!(plan_id)) + |> assign(:tasks, Tasks.list_unfinished_tasks_by_plan_id(plan_id)) + |> add_plan_routes(plan_id) + + {:noreply, socket} + + _ -> + {:noreply, push_patch(socket, to: Routes.tasks_path(socket, :index))} + end + end + + # plan: no, task: no def handle_params(_, _, socket) do - {:noreply, assign(socket, :active_task, nil)} + socket = + socket + |> assign(:active_task, nil) + |> assign(:active_plan, nil) + |> assign(:tasks, Tasks.list_unfinished_tasks()) + |> add_task_routes() + + {:noreply, socket} end def render(assigns) do ~L""" -
    - <%= live_component(@socket, - TasksComponent, - id: :all_unfinished_tasks, - live_action: @live_action, - tasks: @tasks, - active_task: @active_task, - route_show_task: &(Routes.tasks_path(&1, :show, &2)), - route_edit_task: &(Routes.tasks_path(&1, :edit, &2)), - route_index_tasks: &(Routes.tasks_path(&1, :index)) - )%> +
    +
    +

    plans

    + +
    +
    + <%= case @active_plan do %> + <%= nil -> %> +

    all unfinished tasks

    + <% _ -> %> +

    + +   + <%= @active_plan.name %> +

    + <% end %> + <%= live_component(@socket, + TasksComponent, + id: :tasks, + live_action: @live_action, + tasks: @tasks, + active_plan: @active_plan, + active_task: @active_task, + route_show_task: @route_show_task, + route_edit_task: @route_edit_task, + route_index_tasks: @route_index_tasks + )%> +
    """ end + def handle_event("new-plan", %{"plan" => plan_params}, socket) do + case Tasks.create_plan(plan_params) do + {:ok, _plan} -> + {:noreply, assign(socket, plans: Tasks.list_unfinished_plans())} + {:error, %Ecto.Changeset{} = changeset} -> + {:noreply, assign(socket, plan_changeset: changeset)} + end + end + + def handle_event("finish-plan", %{"plan-id" => plan_id}, socket) do + {_, plan} = Tasks.finish_plan_by_id!(plan_id) + socket = put_flash(socket, :info, "finished plan \"#{plan.name}\"") + socket = assign(socket, plans: Tasks.list_unfinished_plans()) + {:noreply, push_patch(socket, to: Routes.tasks_path(socket, :index))} + end + def handle_event("keydown", _params, socket) do + route = get_index_route(socket) + case socket.assigns.live_action do :index -> {:noreply, socket} - _ -> {:noreply, push_patch(socket, to: Routes.tasks_path(socket, :index))} + _ -> {:noreply, push_patch(socket, to: route)} end end @@ -57,7 +174,9 @@ defmodule PlannerWeb.TasksLive do socket |> refresh_tasks_and_flash_msg("task \"#{task.value}\" updated") - {:noreply, push_patch(socket, to: Routes.tasks_path(socket, :show, task.id))} + route = get_index_route(socket) + + {:noreply, push_patch(socket, to: route)} {:error, changeset} -> send_update(TaskEditComponent, id: "task_edit:#{task.id}", changeset: changeset) @@ -72,27 +191,83 @@ defmodule PlannerWeb.TasksLive do def handle_event("delete-task", %{"task-id" => task_id}, socket) do {_, task} = Tasks.delete_task_by_id!(task_id) - {:noreply, refresh_tasks_and_flash_msg(socket, "task \"#{task.value}\" deleted")} + socket = refresh_tasks_and_flash_msg(socket, "task \"#{task.value}\" deleted") + route = get_index_route(socket) + + {:noreply, push_patch(socket, to: route)} end def handle_event("new-task", %{"task" => task_params}, socket) do + add_new_task(task_params, socket.assigns.active_plan, socket) + end + + def handle_event("add-task-to-plan", plan_detail_params, socket) do + {_, pd} = Tasks.create_plan_detail(plan_detail_params) + + {:noreply, + refresh_tasks_and_flash_msg(socket, "task #{pd.task_id} added to plan #{pd.plan_id}")} + end + + def handle_event("delete-task-from-plan", %{"task_id" => task_id, "plan_id" => plan_id}, socket) do + plan_detail = Tasks.get_plan_detail_by!(task_id: task_id, plan_id: plan_id) + {_, pd} = Tasks.delete_plan_detail(plan_detail) + + {:noreply, + refresh_tasks_and_flash_msg(socket, "task #{pd.task_id} removed from plan #{pd.plan_id}")} + end + + defp refresh_tasks_and_flash_msg(socket, msg) do + tasks = + case socket.assigns.active_plan do + nil -> Tasks.list_unfinished_tasks() + plan -> Tasks.list_unfinished_tasks_by_plan_id(plan.id) + end + + socket + |> assign(:tasks, tasks) + |> put_flash(:info, msg) + end + + defp add_new_task(task_params, active_plan = nil, socket) do case Tasks.create_task(task_params) do {:ok, task} -> - socket = - socket - |> refresh_tasks_and_flash_msg("task \"#{task.value}\" created") - - {:noreply, push_patch(socket, to: Routes.tasks_path(socket, :show, task.id))} + {:noreply, refresh_tasks_and_flash_msg(socket, "task \"#{task.value}\" created")} {:error, %Ecto.Changeset{} = changeset} -> - send_update(TasksComponent, id: :all_unfinished_tasks, changeset: changeset) + send_update(TasksComponent, id: :tasks, changeset: changeset) {:noreply, socket} end end - defp refresh_tasks_and_flash_msg(socket, msg) do + defp add_new_task(task_params, active_plan, socket) do + case Tasks.create_task_and_add_to_plan(task_params, active_plan) do + {:ok, %{plan_detail: _, task: task}} -> + {:noreply, refresh_tasks_and_flash_msg(socket, "task \"#{task.value}\" created")} + + {:error, :task, %Ecto.Changeset{} = changeset, _} -> + send_update(TasksComponent, id: :tasks, changeset: changeset) + {:noreply, socket} + end + end + + defp add_plan_routes(socket, plan_id) do socket - |> assign(:tasks, Tasks.list_unfinished_tasks()) - |> put_flash(:info, msg) + |> assign(:route_show_task, &Routes.tasks_path(&1, :show_task, plan_id, &2)) + |> assign(:route_edit_task, &Routes.tasks_path(&1, :edit_task, plan_id, &2)) + |> assign(:route_index_tasks, &Routes.tasks_path(&1, :show_plan, plan_id)) + end + + defp add_task_routes(socket) do + socket + |> assign(:route_show_task, &Routes.tasks_path(&1, :show_task, &2)) + |> assign(:route_edit_task, &Routes.tasks_path(&1, :edit_task, &2)) + |> assign(:route_index_tasks, &Routes.tasks_path(&1, :index)) + end + + defp get_index_route(socket) do + case socket.assigns.active_plan do + nil -> Routes.tasks_path(socket, :index) + plan -> Routes.tasks_path(socket, :show_plan, plan.id) + end end end diff --git a/lib/planner_web/router.ex b/lib/planner_web/router.ex index 1d79577..df11c3a 100644 --- a/lib/planner_web/router.ex +++ b/lib/planner_web/router.ex @@ -53,8 +53,12 @@ defmodule PlannerWeb.Router do pipe_through([:browser, :require_authenticated_user]) live("/", TasksLive, :index) - live("/:task_id", TasksLive, :show) - live("/:task_id/edit", TasksLive, :edit) + live("/tasks", TasksLive, :index) + live("/tasks/:task_id", TasksLive, :show_task) + live("/tasks/:task_id/edit", TasksLive, :edit_task) + live("/plans/:plan_id/tasks", TasksLive, :show_plan) + live("/plans/:plan_id/tasks/:task_id", TasksLive, :show_task) + live("/plans/:plan_id/tasks/:task_id/edit", TasksLive, :edit_task) get("/users/settings", UserSettingsController, :edit) put("/users/settings/update_password", UserSettingsController, :update_password) diff --git a/priv/repo/migrations/20200809220758_create_plans.exs b/priv/repo/migrations/20200809220758_create_plans.exs new file mode 100644 index 0000000..449b381 --- /dev/null +++ b/priv/repo/migrations/20200809220758_create_plans.exs @@ -0,0 +1,16 @@ +defmodule Planner.Repo.Migrations.CreatePlans do + use Ecto.Migration + + def change do + create table(:plans, primary_key: false) do + add :id, :binary_id, primary_key: true + add :description, :string + add :finished_at, :naive_datetime + add :start, :naive_datetime + add :end, :naive_datetime + add :name, :string + + timestamps() + end + end +end diff --git a/priv/repo/migrations/20200809221238_create_plan_details.exs b/priv/repo/migrations/20200809221238_create_plan_details.exs new file mode 100644 index 0000000..40f84dd --- /dev/null +++ b/priv/repo/migrations/20200809221238_create_plan_details.exs @@ -0,0 +1,18 @@ +defmodule Planner.Repo.Migrations.CreatePlanDetails do + use Ecto.Migration + + def change do + create table(:plan_details, primary_key: false) do + add :id, :binary_id, primary_key: true + add :sort, :integer + add :task_id, references(:tasks, on_delete: :nothing, type: :binary_id) + add :plan_id, references(:plans, on_delete: :nothing, type: :binary_id) + + timestamps() + end + + create index(:plan_details, [:task_id]) + create index(:plan_details, [:plan_id]) + create unique_index(:plan_details, [:task_id, :plan_id]) + end +end diff --git a/priv/repo/seeds.exs b/priv/repo/seeds.exs index 0bad2ee..2baece3 100644 --- a/priv/repo/seeds.exs +++ b/priv/repo/seeds.exs @@ -1,11 +1,46 @@ -# Script for populating the database. You can run it as: -# -# mix run priv/repo/seeds.exs -# -# Inside the script, you can read and write to any of your -# repositories directly: -# -# Planner.Repo.insert!(%Planner.SomeSchema{}) -# -# We recommend using the bang functions (`insert!`, `update!` -# and so on) as they will fail if something goes wrong. +# mix run priv/repo/seeds.exs + +alias Planner.Tasks + +tasks_records = [ + %{"value" => "task1"}, + %{"value" => "task2"}, + %{"value" => "task3"}, + %{"value" => "task4"}, + %{"value" => "task5"}, + %{"value" => "task6"} +] + +tasks = + Enum.map(tasks_records, fn record -> + {:ok, task} = Tasks.create_task(record) + task + end) + +plans_records = [ + %{name: "plan1"}, + %{name: "plan2"}, + %{name: "plan3"} +] + +plans = + Enum.map(plans_records, fn record -> + {:ok, plan} = Tasks.create_plan(record) + plan + end) + +[t1, t2, t3, t4, t5, _] = tasks +[p1, p2, _] = plans + +plan_details_records = [ + %{plan_id: p1.id, task_id: t1.id, sort: 0}, + %{plan_id: p1.id, task_id: t2.id, sort: 0}, + %{plan_id: p1.id, task_id: t3.id, sort: 0}, + %{plan_id: p2.id, task_id: t4.id, sort: 0}, + %{plan_id: p2.id, task_id: t5.id, sort: 0} + # deliberately leave off the last task +] + +Enum.each(plan_details_records, fn record -> + Tasks.create_plan_detail(record) +end)