);
savedTitle = title.value;
savedBody = body.value;
@@ -483,8 +494,19 @@ useEditorGuards(dirty, save);
+
+
+
+ Started
+ {{ new Date(startedAt).toLocaleString() }}
+
+
+ Completed
+ {{ new Date(completedAt).toLocaleString() }}
+
+
+
+
+
+
@@ -903,6 +929,28 @@ useEditorGuards(dirty, save);
align-items: center;
}
+/* Lifecycle timestamps */
+.sb-timestamps {
+ display: flex;
+ flex-direction: column;
+ gap: 0.2rem;
+ margin-top: 0.25rem;
+}
+.sb-timestamp {
+ display: flex;
+ justify-content: space-between;
+ gap: 0.4rem;
+ font-size: 0.75rem;
+}
+.sb-ts-label {
+ color: var(--color-text-muted);
+ flex-shrink: 0;
+}
+.sb-ts-value {
+ color: var(--color-text-secondary);
+ text-align: right;
+}
+
/* Narrow screen: sidebar collapses */
@media (max-width: 720px) {
.task-body { flex-direction: column; overflow-y: auto; overflow-x: hidden; }
diff --git a/frontend/src/views/TaskViewerView.vue b/frontend/src/views/TaskViewerView.vue
index 39adee4..7295be0 100644
--- a/frontend/src/views/TaskViewerView.vue
+++ b/frontend/src/views/TaskViewerView.vue
@@ -33,12 +33,14 @@ const statusCycle: Record
= {
todo: "in_progress",
in_progress: "done",
done: "todo",
+ cancelled: "todo",
};
const statusDotClass: Record = {
todo: "dot-todo",
in_progress: "dot-in-progress",
done: "dot-done",
+ cancelled: "dot-cancelled",
};
function cycleSubTaskStatus(subTask: Note) {
@@ -137,8 +139,25 @@ const forwardStatus: Record = {
todo: "in_progress",
in_progress: "done",
done: null,
+ cancelled: null,
};
+function recurrenceSummary(rule: Record | null): string | null {
+ if (!rule) return null;
+ if (rule.type === "interval") {
+ return `Every ${rule.every} ${rule.unit}(s)`;
+ }
+ if (rule.type === "calendar") {
+ if (rule.unit === "month") return `Monthly on day ${rule.day_of_month}`;
+ if (rule.unit === "year") {
+ const months = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];
+ const m = months[((rule.month as number) ?? 1) - 1];
+ return `Yearly on ${m} ${rule.day_of_month}`;
+ }
+ }
+ return null;
+}
+
const advanceLabel = computed(() => {
const s = store.currentTask?.status as TaskStatus | undefined;
if (!s) return null;
@@ -318,6 +337,17 @@ const subTaskProgress = computed(() => {
Due: {{ store.currentTask.due_date }}
+
+
+ Started: {{ new Date(store.currentTask.started_at).toLocaleString() }}
+
+
+ Completed: {{ new Date(store.currentTask.completed_at).toLocaleString() }}
+
+
+ ↻ {{ recurrenceSummary(store.currentTask.recurrence_rule as Record | null) }}
+
+