Show completed task history in sidebar detail view
diff --git a/lib/ketchup/views/home.rb b/lib/ketchup/views/home.rb
index 58806cd..554822e 100644
--- a/lib/ketchup/views/home.rb
+++ b/lib/ketchup/views/home.rb
@@ -72,6 +72,20 @@ module Views
dt("x-show": "$store.sidebar.taskUrgency !== ''") { "Urgency" }
dd("x-show": "$store.sidebar.taskUrgency !== ''", "x-text": "$store.sidebar.taskUrgency")
end
+
+ template("x-if": "$store.sidebar.completedTasks.length > 0") do
+ div(class: "task-history") do
+ h3 { "History" }
+ ul do
+ template("x-for": "ct in $store.sidebar.completedTasks") do
+ li do
+ span(class: "task-history-check") { "✓" }
+ span(class: "task-history-date", "x-text": "ct.completed_at")
+ end
+ end
+ end
+ end
+ end
end
form(method: "post", action: "/series", "x-show": "$store.sidebar.mode === 'form'") do
@@ -196,6 +210,7 @@ module Views
"x-on:click": "$store.sidebar.showTask($el)",
"x-bind:class": "$store.sidebar.taskId == '#{task[:id]}' && 'task-selected'",
"data-task-id": task[:id].to_s,
+ "data-series-id": task[:series_id].to_s,
"data-task-name": name,
"data-task-note": task[:note],
"data-task-interval": interval_text(task[:interval_count], task[:interval_unit]),
diff --git a/lib/ketchup/web.rb b/lib/ketchup/web.rb
index e634a27..04e19d4 100644
--- a/lib/ketchup/web.rb
+++ b/lib/ketchup/web.rb
@@ -1,5 +1,6 @@
# frozen_string_literal: true
+require "json"
require "roda"
require_relative "models"
@@ -39,6 +40,23 @@ class Web < Roda
end
end
+ r.on "series", Integer do |series_id|
+ series = Series.where(id: series_id, user_id: current_user.id).first
+ r.halt 404 unless series
+
+ r.get "completed" do
+ completed = series.tasks_dataset
+ .exclude(completed_at: nil)
+ .order(Sequel.desc(:completed_at))
+ .select(:due_date, :completed_at)
+ .all
+ .map { |t| { due_date: t[:due_date].to_s, completed_at: t[:completed_at].strftime("%Y-%m-%d") } }
+
+ response["content-type"] = "application/json"
+ completed.to_json
+ end
+ end
+
r.on "series" do
r.post do
note = r.params["note"].to_s.strip
diff --git a/public/css/app.css b/public/css/app.css
index 04f92c2..2fe75d3 100644
--- a/public/css/app.css
+++ b/public/css/app.css
@@ -230,6 +230,35 @@ h2 { font-size: var(--step-0); font-weight: 600; }
color: #444;
}
+.task-history {
+ margin-block-start: var(--space-s);
+}
+
+.task-history h3 {
+ font-size: var(--step--2);
+ font-weight: 600;
+ color: #999;
+ margin-block-end: var(--space-3xs);
+}
+
+.task-history ul {
+ list-style: none;
+ padding: 0;
+}
+
+.task-history li {
+ display: flex;
+ align-items: center;
+ gap: var(--space-3xs);
+ font-size: var(--step--2);
+ color: #444;
+ padding-block: 2px;
+}
+
+.task-history-check {
+ color: #999;
+}
+
.calendar-horizon {
padding-block: var(--space-s) var(--space-2xs);
border-block-end: 1px solid #ccc;
diff --git a/public/js/app.js b/public/js/app.js
index a2e713c..24d35c5 100644
--- a/public/js/app.js
+++ b/public/js/app.js
@@ -35,6 +35,7 @@ document.addEventListener("alpine:init", () => {
taskDueDate: "",
taskUrgency: "",
taskOverdue: false,
+ completedTasks: [],
showTask(el) {
this.taskId = el.dataset.taskId
@@ -44,7 +45,12 @@ document.addEventListener("alpine:init", () => {
this.taskDueDate = el.dataset.taskDueDate
this.taskUrgency = el.dataset.taskUrgency
this.taskOverdue = el.dataset.taskOverdue === "true"
+ this.completedTasks = []
this.mode = "task"
+
+ fetch(`/series/${el.dataset.seriesId}/completed`)
+ .then((r) => r.json())
+ .then((data) => (this.completedTasks = data))
},
showForm() {