IMP: task crud (#6)
This commit is contained in:
parent
2b8a6a1927
commit
673d096f53
18 changed files with 260 additions and 33 deletions
|
@ -1,2 +1 @@
|
|||
@import "bulma/bulma.sass";
|
||||
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
// its own CSS file.
|
||||
import "../css/app.scss"
|
||||
|
||||
import '@fortawesome/fontawesome-free/js/all'
|
||||
|
||||
// webpack automatically bundles all modules in your
|
||||
// entry points. Those entry points can be configured
|
||||
// in "webpack.config.js".
|
||||
|
|
5
assets/package-lock.json
generated
5
assets/package-lock.json
generated
|
@ -962,6 +962,11 @@
|
|||
"to-fast-properties": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"@fortawesome/fontawesome-free": {
|
||||
"version": "5.13.1",
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-5.13.1.tgz",
|
||||
"integrity": "sha512-D819f34FLHeBN/4xvw0HR0u7U2G7RqjPSggXqf7LktsxWQ48VAfGwvMrhcVuaZV2fF069c/619RdgCCms0DHhw=="
|
||||
},
|
||||
"@types/json-schema": {
|
||||
"version": "7.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.5.tgz",
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
"watch": "webpack --mode development --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-free": "^5.13.1",
|
||||
"bulma": "^0.9.0",
|
||||
"phoenix": "file:../deps/phoenix",
|
||||
"phoenix_html": "file:../deps/phoenix_html",
|
||||
|
|
|
@ -4,6 +4,10 @@ defmodule Planner.Tasks do
|
|||
alias Planner.Tasks.Task
|
||||
|
||||
def add_task(attrs) do
|
||||
attrs =
|
||||
attrs
|
||||
|> cast_finished_at()
|
||||
|
||||
%Task{}
|
||||
|> Task.changeset(attrs)
|
||||
|> Repo.insert()
|
||||
|
@ -14,7 +18,17 @@ defmodule Planner.Tasks do
|
|||
def list_unfinished_tasks do
|
||||
from(
|
||||
t in Task,
|
||||
where: is_nil(t.finished_at)
|
||||
where: is_nil(t.finished_at),
|
||||
order_by: [desc: t.updated_at]
|
||||
)
|
||||
|> Repo.all()
|
||||
end
|
||||
|
||||
def list_finished_tasks do
|
||||
from(
|
||||
t in Task,
|
||||
where: not is_nil(t.finished_at),
|
||||
order_by: [desc: t.updated_at]
|
||||
)
|
||||
|> Repo.all()
|
||||
end
|
||||
|
@ -24,16 +38,29 @@ defmodule Planner.Tasks do
|
|||
|> Task.changeset(%{})
|
||||
end
|
||||
|
||||
def cast_finished_at(attrs) do
|
||||
attrs
|
||||
|> Map.update("finished_at", nil, fn
|
||||
"true" -> NaiveDateTime.utc_now()
|
||||
"false" -> nil
|
||||
val -> val
|
||||
end)
|
||||
end
|
||||
|
||||
def update_task(%Task{} = task, attrs) do
|
||||
attrs =
|
||||
attrs
|
||||
|> cast_finished_at()
|
||||
|
||||
task
|
||||
|> Task.changeset(attrs)
|
||||
|> Repo.update()
|
||||
end
|
||||
|
||||
def get_task!(id), do: Repo.get!(Task, id)
|
||||
|
||||
def delete_task_by_id!(id) do
|
||||
get_task!(id)
|
||||
|> Repo.delete()
|
||||
end
|
||||
|
||||
def finish_task_by_id!(id) do
|
||||
get_task!(id)
|
||||
|> Task.finish_task()
|
||||
|> Repo.update()
|
||||
end
|
||||
end
|
||||
|
|
|
@ -20,11 +20,4 @@ defmodule Planner.Tasks.Task do
|
|||
|> validate_required([:value])
|
||||
|> validate_length(:value, min: 3)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def finish_task(task) do
|
||||
now = NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second)
|
||||
# TODO, this should check if `finished_at` is not nil, first
|
||||
change(task, finished_at: now)
|
||||
end
|
||||
end
|
||||
|
|
82
lib/planner_web/controllers/task_controller.ex
Normal file
82
lib/planner_web/controllers/task_controller.ex
Normal file
|
@ -0,0 +1,82 @@
|
|||
defmodule PlannerWeb.TaskController do
|
||||
use PlannerWeb, :controller
|
||||
|
||||
alias Planner.Tasks
|
||||
alias Planner.Tasks.Task
|
||||
|
||||
def index(conn, _params) do
|
||||
tasks = Tasks.list_unfinished_tasks()
|
||||
|
||||
conn
|
||||
|> assign(:tasks, tasks)
|
||||
|> render("index.html")
|
||||
end
|
||||
|
||||
def new(conn, _params) do
|
||||
task = %Task{}
|
||||
changeset = Tasks.change_task(%Task{})
|
||||
|
||||
conn
|
||||
|> assign(:task, task)
|
||||
|> assign(:changeset, changeset)
|
||||
|> render("new.html")
|
||||
end
|
||||
|
||||
def create(conn, %{"task" => task_params}) do
|
||||
case Tasks.add_task(task_params) do
|
||||
{:ok, _task} ->
|
||||
conn
|
||||
|> put_flash(:info, "task created")
|
||||
|> redirect(to: Routes.task_path(conn, :index))
|
||||
|
||||
{:error, %Ecto.Changeset{} = changeset} ->
|
||||
conn
|
||||
|> assign(:task, %Task{})
|
||||
|> assign(:changeset, changeset)
|
||||
|> render("new.html")
|
||||
end
|
||||
end
|
||||
|
||||
def show(conn, %{"id" => id}) do
|
||||
task = Tasks.get_task!(id)
|
||||
|
||||
conn
|
||||
|> assign(:task, task)
|
||||
|> render("show.html")
|
||||
end
|
||||
|
||||
def edit(conn, %{"id" => id}) do
|
||||
task = Tasks.get_task!(id)
|
||||
changeset = Tasks.change_task(task)
|
||||
|
||||
conn
|
||||
|> assign(:task, task)
|
||||
|> assign(:changeset, changeset)
|
||||
|> render("edit.html")
|
||||
end
|
||||
|
||||
def update(conn, %{"id" => id, "task" => task_params}) do
|
||||
task = Tasks.get_task!(id)
|
||||
|
||||
case Tasks.update_task(task, task_params) do
|
||||
{:ok, _task} ->
|
||||
conn
|
||||
|> put_flash(:info, "task updated")
|
||||
|> redirect(to: Routes.task_path(conn, :index))
|
||||
|
||||
{:error, %Ecto.Changeset{} = changeset} ->
|
||||
conn
|
||||
|> assign(:task, task)
|
||||
|> assign(:changeset, changeset)
|
||||
|> render("edit.html")
|
||||
end
|
||||
end
|
||||
|
||||
def delete(conn, %{"id" => id}) do
|
||||
{:ok, _task} = Tasks.delete_task_by_id!(id)
|
||||
|
||||
conn
|
||||
|> put_flash(:info, "task deleted")
|
||||
|> redirect(to: Routes.task_path(conn, :index))
|
||||
end
|
||||
end
|
|
@ -54,6 +54,8 @@ defmodule PlannerWeb.Router do
|
|||
|
||||
live("/", LandingLive, :index)
|
||||
|
||||
resources("/tasks", TaskController)
|
||||
|
||||
get("/users/settings", UserSettingsController, :edit)
|
||||
put("/users/settings/update_password", UserSettingsController, :update_password)
|
||||
put("/users/settings/update_email", UserSettingsController, :update_email)
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<section class="section">
|
||||
<div class="container">
|
||||
<main role="main" class="container">
|
||||
<%= if live_flash(@flash, :info) do %>
|
||||
<p class="alert alert-info" role="alert"
|
||||
phx-click="lv:clear-flash"
|
||||
|
@ -13,5 +12,4 @@
|
|||
<% end %>
|
||||
|
||||
<%= @inner_content %>
|
||||
</div>
|
||||
</main>
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<script defer type="text/javascript" src="<%= Routes.static_path(@conn, "/js/app.js") %>"></script>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar is-dark" role="navigation">
|
||||
<nav class="navbar is-dark mb-5" role="navigation">
|
||||
<div class="navbar-brand">
|
||||
<%= link "planner", to: Routes.landing_path(@conn, :index), class: "navbar-item has-text-weight-bold hast-text-info-light" %>
|
||||
|
||||
|
@ -22,7 +22,7 @@
|
|||
|
||||
<div id="nvbr" class="navbar-menu">
|
||||
<div class="navbar-start">
|
||||
<%= link "tasks", to: Routes.landing_path(@conn, :index), class: "navbar-item" %>
|
||||
<%= link "tasks", to: Routes.task_path(@conn, :index), class: "navbar-item" %>
|
||||
</div>
|
||||
|
||||
<div class="navbar-end">
|
||||
|
|
5
lib/planner_web/templates/task/edit.html.eex
Normal file
5
lib/planner_web/templates/task/edit.html.eex
Normal file
|
@ -0,0 +1,5 @@
|
|||
<div class="columns">
|
||||
<div class="column is-three-fifths is-offset-one-fifth">
|
||||
<%= render "form.html", Map.put(assigns, :action, Routes.task_path(@conn, :update, @task)) %>
|
||||
</div>
|
||||
</div>
|
42
lib/planner_web/templates/task/form.html.eex
Normal file
42
lib/planner_web/templates/task/form.html.eex
Normal file
|
@ -0,0 +1,42 @@
|
|||
<div class="box">
|
||||
<%= form_for @changeset, @action, fn f -> %>
|
||||
<%= if @changeset.action do %>
|
||||
<div class="help is-danger">
|
||||
<p>something went wrong (see below)</p>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
<%= textarea f, :value, required: true, class: "textarea", placeholder: "task" %>
|
||||
</div>
|
||||
<%= error_tag f, :value %>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<%= label f, :due_at, class: "label" do %>
|
||||
due (YYYY-MM-DD HH:MM:SS)
|
||||
<% end %>
|
||||
<div class="control">
|
||||
<%= text_input f, :due_at, class: "input", placeholder: "YYYY-MM-DD HH:MM:SS" %>
|
||||
</div>
|
||||
<%= error_tag f, :due_at %>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<%= label f, :finished_at, class: "label" do %>
|
||||
<%= if is_nil(@task.finished_at) do %>
|
||||
<%= checkbox f, :finished_at %>
|
||||
<% else %>
|
||||
<%= checkbox f, :finished_at, checked_value: @task.finished_at %>
|
||||
<% end %>
|
||||
finished
|
||||
<% end %>
|
||||
<%= error_tag f, :finished_at %>
|
||||
</div>
|
||||
|
||||
<div class="control">
|
||||
<%= submit "save", class: "button is-dark" %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
33
lib/planner_web/templates/task/index.html.eex
Normal file
33
lib/planner_web/templates/task/index.html.eex
Normal file
|
@ -0,0 +1,33 @@
|
|||
<div class="columns">
|
||||
<div class="column table-container">
|
||||
<%= link "new", to: Routes.task_path(@conn, :new), class: "button is-dark" %>
|
||||
|
||||
<div class="table-container">
|
||||
<table class="table is-striped is-hoverable is-fullwidth" style="table-layout: fixed;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>task</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<%= for task <- @tasks do %>
|
||||
<tr>
|
||||
<td>
|
||||
<%= if not is_nil(task.due_at) do %>
|
||||
<div class="tags mb-0">
|
||||
<span class="tag is-warning">
|
||||
due: <%= task.due_at %>
|
||||
</span>
|
||||
</div>
|
||||
<% end %>
|
||||
<%= link to: Routes.task_path(@conn, :show, task.id), class: "has-text-black" do %>
|
||||
<%= task.value %>
|
||||
<% end %>
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
5
lib/planner_web/templates/task/new.html.eex
Normal file
5
lib/planner_web/templates/task/new.html.eex
Normal file
|
@ -0,0 +1,5 @@
|
|||
<div class="columns">
|
||||
<div class="column is-three-fifths is-offset-one-fifth">
|
||||
<%= render "form.html", Map.put(assigns, :action, Routes.task_path(@conn, :create)) %>
|
||||
</div>
|
||||
</div>
|
31
lib/planner_web/templates/task/show.html.eex
Normal file
31
lib/planner_web/templates/task/show.html.eex
Normal file
|
@ -0,0 +1,31 @@
|
|||
<div class="columns">
|
||||
<div class="column is-three-fifths is-offset-one-fifth">
|
||||
<div class="box">
|
||||
|
||||
<%= if not is_nil(@task.due_at) or is_nil(@task.filed_at) do %>
|
||||
<div class="tags">
|
||||
<%= if not is_nil(@task.due_at) do %><span class="tag is-warning">due: <%= @task.due_at %></span><% end %>
|
||||
<%= if is_nil(@task.filed_at) do %><span class="tag is-danger">unfiled</span><% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<p class="mb-5">
|
||||
<%= @task.value %>
|
||||
</p>
|
||||
|
||||
<div class="tags">
|
||||
<span class="tag is-light">updated: <%= @task.updated_at %></span>
|
||||
<span class="tag is-light">created: <%= @task.inserted_at %></span>
|
||||
</div>
|
||||
|
||||
<div class="field is-grouped">
|
||||
<p class="control">
|
||||
<%= link "edit", to: Routes.task_path(@conn, :edit, @task.id), class: "button is-dark" %>
|
||||
</p>
|
||||
<p class="control">
|
||||
<%= link "delete", to: Routes.task_path(@conn, :delete, @task.id), class: "button is-dark", method: :delete, data: [confirm: "are you sure?"] %>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
<%= form_for @conn, Routes.user_session_path(@conn, :create), [as: :user], fn f -> %>
|
||||
<%= if @error_message do %>
|
||||
|
||||
<div class="help is-danger">
|
||||
<p><%= @error_message %></p>
|
||||
</div>
|
||||
|
|
3
lib/planner_web/views/task_view.ex
Normal file
3
lib/planner_web/views/task_view.ex
Normal file
|
@ -0,0 +1,3 @@
|
|||
defmodule PlannerWeb.TaskView do
|
||||
use PlannerWeb, :view
|
||||
end
|
Loading…
Add table
Reference in a new issue