Show completed task history in sidebar detail view
change nmolxxklzvuumlpxryqtswoulrsorxsq
commit a932f1e6e1d70423ddf624ade675689db109cea0
author Alpha Chen <alpha@kejadlen.dev>
date
parent tqzvqvkt
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() {