IMP: task crud (#6)

This commit is contained in:
Matthew Ryan Dillon 2020-06-21 16:09:24 -07:00 committed by GitHub
parent 2b8a6a1927
commit 673d096f53
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 260 additions and 33 deletions

View file

@ -1,2 +1 @@
@import "bulma/bulma.sass";

View file

@ -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".

View file

@ -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",

View file

@ -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",

View file

@ -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

View file

@ -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

View 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

View file

@ -18,14 +18,14 @@ defmodule PlannerWeb.LandingLive do
def render(assigns) do
~L"""
<div class="box">
<%= f = form_for(@new_task_changeset, "#", [phx_submit: :save_new_task]) %>
<div class="field">
<div class="control">
<%= text_input f, :value, placeholder: "add new task", class: "input" %>
<%= f = form_for(@new_task_changeset, "#", [phx_submit: :save_new_task]) %>
<div class="field">
<div class="control">
<%= text_input f, :value, placeholder: "add new task", class: "input" %>
</div>
<%= error_tag f, :value %>
</div>
<%= error_tag f, :value %>
</div>
</form>
</form>
</div>
"""
end

View file

@ -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)

View file

@ -1,17 +1,15 @@
<section class="section">
<div class="container">
<%= if live_flash(@flash, :info) do %>
<main role="main" class="container">
<%= if live_flash(@flash, :info) do %>
<p class="alert alert-info" role="alert"
phx-click="lv:clear-flash"
phx-value-key="info"><%= live_flash(@flash, :info) %></p>
<% end %>
<% end %>
<%= if live_flash(@flash, :error) do %>
<%= if live_flash(@flash, :error) do %>
<p class="alert alert-danger" role="alert"
phx-click="lv:clear-flash"
phx-value-key="error"><%= live_flash(@flash, :error) %></p>
<% end %>
<% end %>
<%= @inner_content %>
</div>
<%= @inner_content %>
</main>

View file

@ -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">

View 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>

View 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>

View 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>

View 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>

View 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>

View file

@ -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>

View file

@ -0,0 +1,3 @@
defmodule PlannerWeb.TaskView do
use PlannerWeb, :view
end