diff --git a/assets/css/app.scss b/assets/css/app.scss
index ca39025..6b6e2a3 100644
--- a/assets/css/app.scss
+++ b/assets/css/app.scss
@@ -1,2 +1 @@
 @import "bulma/bulma.sass";
-
diff --git a/assets/js/app.js b/assets/js/app.js
index 36a0a18..e1ec7c9 100644
--- a/assets/js/app.js
+++ b/assets/js/app.js
@@ -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".
diff --git a/assets/package-lock.json b/assets/package-lock.json
index 5dd70cb..77c4d59 100644
--- a/assets/package-lock.json
+++ b/assets/package-lock.json
@@ -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",
diff --git a/assets/package.json b/assets/package.json
index 5ede3a0..8a13c23 100644
--- a/assets/package.json
+++ b/assets/package.json
@@ -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",
diff --git a/lib/planner/tasks.ex b/lib/planner/tasks.ex
index be99db1..54e0ff3 100644
--- a/lib/planner/tasks.ex
+++ b/lib/planner/tasks.ex
@@ -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
diff --git a/lib/planner/tasks/task.ex b/lib/planner/tasks/task.ex
index 33a4e44..375afe9 100644
--- a/lib/planner/tasks/task.ex
+++ b/lib/planner/tasks/task.ex
@@ -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
diff --git a/lib/planner_web/controllers/task_controller.ex b/lib/planner_web/controllers/task_controller.ex
new file mode 100644
index 0000000..8ededf3
--- /dev/null
+++ b/lib/planner_web/controllers/task_controller.ex
@@ -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
diff --git a/lib/planner_web/live/landing_live.ex b/lib/planner_web/live/landing_live.ex
index 6684198..e25b484 100644
--- a/lib/planner_web/live/landing_live.ex
+++ b/lib/planner_web/live/landing_live.ex
@@ -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
diff --git a/lib/planner_web/router.ex b/lib/planner_web/router.ex
index 80eecf1..4b9e2f1 100644
--- a/lib/planner_web/router.ex
+++ b/lib/planner_web/router.ex
@@ -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)
diff --git a/lib/planner_web/templates/layout/live.html.leex b/lib/planner_web/templates/layout/live.html.leex
index 49376da..3d96e4d 100644
--- a/lib/planner_web/templates/layout/live.html.leex
+++ b/lib/planner_web/templates/layout/live.html.leex
@@ -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>
diff --git a/lib/planner_web/templates/layout/root.html.leex b/lib/planner_web/templates/layout/root.html.leex
index 11caa9b..79caea9 100644
--- a/lib/planner_web/templates/layout/root.html.leex
+++ b/lib/planner_web/templates/layout/root.html.leex
@@ -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">
diff --git a/lib/planner_web/templates/task/edit.html.eex b/lib/planner_web/templates/task/edit.html.eex
new file mode 100644
index 0000000..49a892a
--- /dev/null
+++ b/lib/planner_web/templates/task/edit.html.eex
@@ -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>
diff --git a/lib/planner_web/templates/task/form.html.eex b/lib/planner_web/templates/task/form.html.eex
new file mode 100644
index 0000000..4f1fa46
--- /dev/null
+++ b/lib/planner_web/templates/task/form.html.eex
@@ -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>
diff --git a/lib/planner_web/templates/task/index.html.eex b/lib/planner_web/templates/task/index.html.eex
new file mode 100644
index 0000000..06f68e5
--- /dev/null
+++ b/lib/planner_web/templates/task/index.html.eex
@@ -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>
diff --git a/lib/planner_web/templates/task/new.html.eex b/lib/planner_web/templates/task/new.html.eex
new file mode 100644
index 0000000..fd05e9e
--- /dev/null
+++ b/lib/planner_web/templates/task/new.html.eex
@@ -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>
diff --git a/lib/planner_web/templates/task/show.html.eex b/lib/planner_web/templates/task/show.html.eex
new file mode 100644
index 0000000..708ced0
--- /dev/null
+++ b/lib/planner_web/templates/task/show.html.eex
@@ -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>
diff --git a/lib/planner_web/templates/user_session/new.html.eex b/lib/planner_web/templates/user_session/new.html.eex
index 602f86a..dcad0b1 100644
--- a/lib/planner_web/templates/user_session/new.html.eex
+++ b/lib/planner_web/templates/user_session/new.html.eex
@@ -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>
diff --git a/lib/planner_web/views/task_view.ex b/lib/planner_web/views/task_view.ex
new file mode 100644
index 0000000..52cdc23
--- /dev/null
+++ b/lib/planner_web/views/task_view.ex
@@ -0,0 +1,3 @@
+defmodule PlannerWeb.TaskView do
+  use PlannerWeb, :view
+end