fix: use has_time flag for due date time tracking
Replace the hours==0 && minutes==0 heuristic with an explicit has_time bool field on Task. Existing files without the field deserialize as false (date-only), preserving current behavior. Frontend components pass and receive has_time through DateTimePicker's onchange callback.
This commit is contained in:
parent
970ed9aa1d
commit
72475a552a
|
|
@ -1,7 +1,8 @@
|
|||
<script lang="ts">
|
||||
let { value = null, onchange, onclose }: {
|
||||
let { value = null, has_time = false, onchange, onclose }: {
|
||||
value: string | null;
|
||||
onchange: (iso: string | null) => void;
|
||||
has_time: boolean;
|
||||
onchange: (iso: string | null, has_time: boolean) => void;
|
||||
onclose: () => void;
|
||||
} = $props();
|
||||
|
||||
|
|
@ -12,7 +13,7 @@
|
|||
let viewYear = $state(existing ? existing.getFullYear() : now.getFullYear());
|
||||
let viewMonth = $state(existing ? existing.getMonth() : now.getMonth());
|
||||
let selectedDay = $state(existing ? existing.getDate() : now.getDate());
|
||||
let includeTime = $state(existing ? (existing.getHours() !== 0 || existing.getMinutes() !== 0) : false);
|
||||
let includeTime = $state(has_time);
|
||||
let selectedHour = $state(existing ? existing.getHours() : now.getHours());
|
||||
let selectedMinute = $state(existing ? existing.getMinutes() : 0);
|
||||
let visible = $state(false);
|
||||
|
|
@ -66,12 +67,12 @@
|
|||
const h = includeTime ? selectedHour : 0;
|
||||
const m = includeTime ? selectedMinute : 0;
|
||||
const iso = new Date(viewYear, viewMonth, selectedDay, h, m).toISOString();
|
||||
onchange(iso);
|
||||
onchange(iso, includeTime);
|
||||
dismiss();
|
||||
}
|
||||
|
||||
function clear() {
|
||||
onchange(null);
|
||||
onchange(null, false);
|
||||
dismiss();
|
||||
}
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
let title = $state("");
|
||||
let description = $state("");
|
||||
let dueDate = $state<string | null>(null);
|
||||
let dueDateHasTime = $state(false);
|
||||
let inputEl = $state<HTMLInputElement | null>(null);
|
||||
let showDatePicker = $state(false);
|
||||
|
||||
|
|
@ -17,11 +18,12 @@
|
|||
if (!title.trim()) return;
|
||||
const created = await app.createTask(title.trim(), description.trim() || undefined);
|
||||
if (dueDate && created) {
|
||||
await app.updateTask({ ...created, due_date: dueDate, updated_at: new Date().toISOString() });
|
||||
await app.updateTask({ ...created, due_date: dueDate, has_time: dueDateHasTime, updated_at: new Date().toISOString() });
|
||||
}
|
||||
title = "";
|
||||
description = "";
|
||||
dueDate = null;
|
||||
dueDateHasTime = false;
|
||||
newTaskState.open = false;
|
||||
}
|
||||
|
||||
|
|
@ -30,11 +32,13 @@
|
|||
title = "";
|
||||
description = "";
|
||||
dueDate = null;
|
||||
dueDateHasTime = false;
|
||||
showDatePicker = false;
|
||||
}
|
||||
|
||||
function handleDateChange(iso: string | null) {
|
||||
function handleDateChange(iso: string | null, hasTime: boolean = false) {
|
||||
dueDate = iso;
|
||||
dueDateHasTime = hasTime;
|
||||
}
|
||||
|
||||
function formatDateChip(iso: string): string {
|
||||
|
|
@ -43,8 +47,7 @@
|
|||
const dayNames = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
||||
const day = dayNames[d.getDay()];
|
||||
const pad = (n: number) => String(n).padStart(2, "0");
|
||||
const hasTime = d.getHours() !== 0 || d.getMinutes() !== 0;
|
||||
const timePart = hasTime ? `, ${pad(d.getHours())}:${pad(d.getMinutes())}` : "";
|
||||
const timePart = dueDateHasTime ? `, ${pad(d.getHours())}:${pad(d.getMinutes())}` : "";
|
||||
if (d.toDateString() === today.toDateString()) return `Today${timePart}`;
|
||||
return `${day}, ${pad(d.getDate())}/${pad(d.getMonth() + 1)}${timePart}`;
|
||||
}
|
||||
|
|
@ -137,6 +140,7 @@
|
|||
{#if showDatePicker}
|
||||
<DateTimePicker
|
||||
value={dueDate}
|
||||
has_time={dueDateHasTime}
|
||||
onchange={handleDateChange}
|
||||
onclose={() => (showDatePicker = false)}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -42,8 +42,8 @@
|
|||
debouncedSave({ description });
|
||||
}
|
||||
|
||||
function handleDateChange(iso: string | null) {
|
||||
app.updateTask({ ...task, due_date: iso, updated_at: new Date().toISOString() });
|
||||
function handleDateChange(iso: string | null, hasTime: boolean = false) {
|
||||
app.updateTask({ ...task, due_date: iso, has_time: hasTime, updated_at: new Date().toISOString() });
|
||||
}
|
||||
|
||||
async function handleToggle() {
|
||||
|
|
@ -79,7 +79,7 @@
|
|||
const dayNames = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
||||
const day = dayNames[d.getDay()];
|
||||
const pad = (n: number) => String(n).padStart(2, "0");
|
||||
const hasTime = d.getHours() !== 0 || d.getMinutes() !== 0;
|
||||
const hasTime = task.has_time;
|
||||
const timePart = hasTime ? `, ${pad(d.getHours())}:${pad(d.getMinutes())}` : "";
|
||||
if (d.toDateString() === today.toDateString()) return `Today${timePart}`;
|
||||
return `${day}, ${pad(d.getDate())}/${pad(d.getMonth() + 1)}${timePart}`;
|
||||
|
|
@ -223,6 +223,7 @@
|
|||
{#if showDatePicker}
|
||||
<DateTimePicker
|
||||
value={task.due_date}
|
||||
has_time={task.has_time}
|
||||
onchange={handleDateChange}
|
||||
onclose={() => (showDatePicker = false)}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ export interface Task {
|
|||
description: string;
|
||||
status: "backlog" | "completed";
|
||||
due_date: string | null;
|
||||
has_time: boolean;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
parent_id: string | null;
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ pub struct Task {
|
|||
pub status: TaskStatus,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub due_date: Option<DateTime<Utc>>,
|
||||
#[serde(default)]
|
||||
pub has_time: bool,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
|
|
@ -32,6 +34,7 @@ impl Task {
|
|||
description: String::new(),
|
||||
status: TaskStatus::Backlog,
|
||||
due_date: None,
|
||||
has_time: false,
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
parent_id: None,
|
||||
|
|
|
|||
|
|
@ -56,6 +56,8 @@ pub struct TaskFrontmatter {
|
|||
pub status: TaskStatus,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub due: Option<DateTime<Utc>>,
|
||||
#[serde(default)]
|
||||
pub has_time: bool,
|
||||
pub created: DateTime<Utc>,
|
||||
pub updated: DateTime<Utc>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
|
|
@ -68,6 +70,7 @@ impl From<&Task> for TaskFrontmatter {
|
|||
id: task.id,
|
||||
status: task.status,
|
||||
due: task.due_date,
|
||||
has_time: task.has_time,
|
||||
created: task.created_at,
|
||||
updated: task.updated_at,
|
||||
parent: task.parent_id,
|
||||
|
|
@ -256,6 +259,7 @@ impl Storage for FileSystemStorage {
|
|||
description,
|
||||
status: frontmatter.status,
|
||||
due_date: frontmatter.due,
|
||||
has_time: frontmatter.has_time,
|
||||
created_at: frontmatter.created,
|
||||
updated_at: frontmatter.updated,
|
||||
parent_id: frontmatter.parent,
|
||||
|
|
@ -344,6 +348,7 @@ impl Storage for FileSystemStorage {
|
|||
description,
|
||||
status: frontmatter.status,
|
||||
due_date: frontmatter.due,
|
||||
has_time: frontmatter.has_time,
|
||||
created_at: frontmatter.created,
|
||||
updated_at: frontmatter.updated,
|
||||
parent_id: frontmatter.parent,
|
||||
|
|
|
|||
Loading…
Reference in a new issue