NEW: Initial task mgmt (#4)
This commit is contained in:
parent
9990771c18
commit
61de82e24e
17 changed files with 261 additions and 104 deletions
lib
39
lib/planner/tasks.ex
Normal file
39
lib/planner/tasks.ex
Normal file
|
@ -0,0 +1,39 @@
|
|||
defmodule Planner.Tasks do
|
||||
import Ecto.Query
|
||||
alias Planner.Repo
|
||||
alias Planner.Tasks.Task
|
||||
|
||||
def add_task(attrs) do
|
||||
%Task{}
|
||||
|> Task.changeset(attrs)
|
||||
|> Repo.insert()
|
||||
end
|
||||
|
||||
def list_all_tasks, do: Repo.all(Task)
|
||||
|
||||
def list_unfinished_tasks do
|
||||
from(
|
||||
t in Task,
|
||||
where: is_nil(t.finished_at)
|
||||
)
|
||||
|> Repo.all()
|
||||
end
|
||||
|
||||
def change_task(%Task{} = task) do
|
||||
task
|
||||
|> Task.changeset(%{})
|
||||
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
|
30
lib/planner/tasks/task.ex
Normal file
30
lib/planner/tasks/task.ex
Normal file
|
@ -0,0 +1,30 @@
|
|||
defmodule Planner.Tasks.Task do
|
||||
use Ecto.Schema
|
||||
import Ecto.Changeset
|
||||
|
||||
@primary_key {:id, :binary_id, autogenerate: true}
|
||||
@foreign_key_type :binary_id
|
||||
schema "tasks" do
|
||||
field(:value, :string)
|
||||
field(:filed_at, :naive_datetime)
|
||||
field(:finished_at, :naive_datetime)
|
||||
field(:due_at, :naive_datetime)
|
||||
|
||||
timestamps()
|
||||
end
|
||||
|
||||
@doc false
|
||||
def changeset(task, attrs) do
|
||||
task
|
||||
|> cast(attrs, [:value, :filed_at, :finished_at, :due_at])
|
||||
|> 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
|
|
@ -23,6 +23,7 @@ defmodule PlannerWeb do
|
|||
|
||||
import Plug.Conn
|
||||
import PlannerWeb.Gettext
|
||||
import Phoenix.LiveView.Controller
|
||||
alias PlannerWeb.Router.Helpers, as: Routes
|
||||
end
|
||||
end
|
||||
|
@ -35,18 +36,37 @@ defmodule PlannerWeb do
|
|||
|
||||
# Import convenience functions from controllers
|
||||
import Phoenix.Controller, only: [get_flash: 1, get_flash: 2, view_module: 1]
|
||||
import Phoenix.LiveView.Helpers
|
||||
|
||||
# Include shared imports and aliases for views
|
||||
unquote(view_helpers())
|
||||
end
|
||||
end
|
||||
|
||||
def live_view do
|
||||
quote do
|
||||
use Phoenix.LiveView,
|
||||
layout: {PlannerWeb.LayoutView, "live.html"}
|
||||
|
||||
unquote(view_helpers())
|
||||
end
|
||||
end
|
||||
|
||||
def live_component do
|
||||
quote do
|
||||
use Phoenix.LiveComponent
|
||||
|
||||
unquote(view_helpers())
|
||||
end
|
||||
end
|
||||
|
||||
def router do
|
||||
quote do
|
||||
use Phoenix.Router
|
||||
|
||||
import Plug.Conn
|
||||
import Phoenix.Controller
|
||||
import Phoenix.LiveView.Router
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
defmodule PlannerWeb.PageController do
|
||||
use PlannerWeb, :controller
|
||||
|
||||
def index(conn, _params) do
|
||||
render(conn, "index.html")
|
||||
end
|
||||
end
|
83
lib/planner_web/live/landing_live.ex
Normal file
83
lib/planner_web/live/landing_live.ex
Normal file
|
@ -0,0 +1,83 @@
|
|||
defmodule PlannerWeb.LandingLive do
|
||||
use Phoenix.LiveView, layout: {PlannerWeb.LayoutView, "live.html"}
|
||||
use Phoenix.HTML
|
||||
|
||||
import PlannerWeb.ErrorHelpers
|
||||
|
||||
alias Planner.Tasks
|
||||
alias Planner.Tasks.Task
|
||||
|
||||
def mount(_params, _session, socket) do
|
||||
socket =
|
||||
socket
|
||||
# |> put_flash(:info, "hello world")
|
||||
|> assign(:new_task_changeset, Tasks.change_task(%Task{}))
|
||||
|> assign(:tasks, Tasks.list_unfinished_tasks())
|
||||
|
||||
{:ok, socket}
|
||||
end
|
||||
|
||||
def render(assigns) do
|
||||
~L"""
|
||||
<%= f = form_for(@new_task_changeset, "#", [phx_submit: :save_new_task]) %>
|
||||
<%= label f, :value, "New Task" %>
|
||||
<%= text_input f, :value %>
|
||||
<%= error_tag f, :value %>
|
||||
|
||||
<%= submit "Create" %>
|
||||
</form>
|
||||
|
||||
<hr>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="3">tasks</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<%= for task <- @tasks do %>
|
||||
<tr>
|
||||
<td><%= task.value %></td>
|
||||
<td><button phx-click="delete_task" phx-value-task_id="<%= task.id %>">delete</button></td>
|
||||
<td><button phx-click="finish_task" phx-value-task_id="<%= task.id %>">done</button></td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
"""
|
||||
end
|
||||
|
||||
def handle_event("save_new_task", %{"task" => task_params}, socket) do
|
||||
case Tasks.add_task(task_params) do
|
||||
{:ok, task} ->
|
||||
{:noreply,
|
||||
socket
|
||||
|> put_flash(:info, "task created")
|
||||
|> assign(:tasks, Tasks.list_unfinished_tasks())}
|
||||
|
||||
{:error, %Ecto.Changeset{} = changeset} ->
|
||||
{:noreply,
|
||||
socket
|
||||
|> assign(new_task_changeset: changeset)}
|
||||
end
|
||||
end
|
||||
|
||||
def handle_event("delete_task", %{"task_id" => task_id}, socket) do
|
||||
Tasks.delete_task_by_id!(task_id)
|
||||
|
||||
{:noreply,
|
||||
socket
|
||||
|> put_flash(:info, "task deleted")
|
||||
|> assign(:tasks, Tasks.list_unfinished_tasks())}
|
||||
end
|
||||
|
||||
def handle_event("finish_task", %{"task_id" => task_id}, socket) do
|
||||
Tasks.finish_task_by_id!(task_id)
|
||||
|
||||
{:noreply,
|
||||
socket
|
||||
|> put_flash(:info, "task completed")
|
||||
|> assign(:tasks, Tasks.list_unfinished_tasks())}
|
||||
end
|
||||
end
|
|
@ -6,7 +6,8 @@ defmodule PlannerWeb.Router do
|
|||
pipeline :browser do
|
||||
plug(:accepts, ["html"])
|
||||
plug(:fetch_session)
|
||||
plug(:fetch_flash)
|
||||
plug(:fetch_live_flash)
|
||||
plug(:put_root_layout, {PlannerWeb.LayoutView, :root})
|
||||
plug(:protect_from_forgery)
|
||||
plug(:put_secure_browser_headers)
|
||||
plug(:fetch_current_user)
|
||||
|
@ -51,7 +52,7 @@ defmodule PlannerWeb.Router do
|
|||
scope "/", PlannerWeb do
|
||||
pipe_through([:browser, :require_authenticated_user])
|
||||
|
||||
get("/", PageController, :index)
|
||||
live("/", LandingLive, :index)
|
||||
|
||||
get("/users/settings", UserSettingsController, :edit)
|
||||
put("/users/settings/update_password", UserSettingsController, :update_password)
|
||||
|
|
|
@ -1,28 +1,5 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<title>Planner · Phoenix Framework</title>
|
||||
<link rel="stylesheet" href="<%= Routes.static_path(@conn, "/css/app.css") %>"/>
|
||||
<script defer type="text/javascript" src="<%= Routes.static_path(@conn, "/js/app.js") %>"></script>
|
||||
</head>
|
||||
<body>
|
||||
<ul>
|
||||
<%= if @current_user do %>
|
||||
<li><%= @current_user.email %></li>
|
||||
<li><%= link "Settings", to: Routes.user_settings_path(@conn, :edit) %></li>
|
||||
<li><%= link "Logout", to: Routes.user_session_path(@conn, :delete), method: :delete %></li>
|
||||
<% else %>
|
||||
<li><%= link "Login", to: Routes.user_session_path(@conn, :new) %></li>
|
||||
<% end %>
|
||||
</ul>
|
||||
|
||||
<main role="main" class="container">
|
||||
<p class="alert alert-info" role="alert"><%= get_flash(@conn, :info) %></p>
|
||||
<p class="alert alert-danger" role="alert"><%= get_flash(@conn, :error) %></p>
|
||||
<%= @inner_content %>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
<main role="main" class="container">
|
||||
<p class="alert alert-info" role="alert"><%= get_flash(@conn, :info) %></p>
|
||||
<p class="alert alert-danger" role="alert"><%= get_flash(@conn, :error) %></p>
|
||||
<%= @inner_content %>
|
||||
</main>
|
||||
|
|
11
lib/planner_web/templates/layout/live.html.leex
Normal file
11
lib/planner_web/templates/layout/live.html.leex
Normal file
|
@ -0,0 +1,11 @@
|
|||
<main role="main" class="container">
|
||||
<p class="alert alert-info" role="alert"
|
||||
phx-click="lv:clear-flash"
|
||||
phx-value-key="info"><%= live_flash(@flash, :info) %></p>
|
||||
|
||||
<p class="alert alert-danger" role="alert"
|
||||
phx-click="lv:clear-flash"
|
||||
phx-value-key="error"><%= live_flash(@flash, :error) %></p>
|
||||
|
||||
<%= @inner_content %>
|
||||
</main>
|
28
lib/planner_web/templates/layout/root.html.leex
Normal file
28
lib/planner_web/templates/layout/root.html.leex
Normal file
|
@ -0,0 +1,28 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<title>Planner</title>
|
||||
<link rel="stylesheet" href="<%= Routes.static_path(@conn, "/css/app.css") %>"/>
|
||||
<%= csrf_meta_tag() %>
|
||||
<script defer type="text/javascript" src="<%= Routes.static_path(@conn, "/js/app.js") %>"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Planner</h1>
|
||||
<ul>
|
||||
<%= if @current_user do %>
|
||||
<li><%= @current_user.email %></li>
|
||||
<li><%= link "Settings", to: Routes.user_settings_path(@conn, :edit) %></li>
|
||||
<li><%= link "Logout", to: Routes.user_session_path(@conn, :delete), method: :delete %></li>
|
||||
<% else %>
|
||||
<li><%= link "Login", to: Routes.user_session_path(@conn, :new) %></li>
|
||||
<% end %>
|
||||
</ul>
|
||||
|
||||
<hr>
|
||||
|
||||
<%= @inner_content %>
|
||||
</body>
|
||||
</html>
|
|
@ -1 +0,0 @@
|
|||
<h1>Planner</h1>
|
Loading…
Add table
Add a link
Reference in a new issue