Rename due_date to date across codebase
The kebab menu and docs referred to a task "due date" but the field was just a date; this change renames due_date/group_by_due_date and related identifiers to date/group_by_date across Rust, TypeScript, Svelte, CLI, docs and tests to keep terminology consistent. Updates include API/command names, storage/models, repository methods, UI text, JS variables and builder/parse functions so code, tests and documentation all use "date" semantics. Preserve old "group_by_due_date" field name Add serde alias to ListMetadata.group_by_date so older .listdata.json files that used the previous field name (group_by_due_date) can still be deserialized correctly. This fixes serialization/deserialization issues encountered at /home/trztn/Documents/Onyx and /var/home/trztn/Nextcloud/Onyx. the frontmatter due should be date... I don't want due anywhere - Renamed `TaskFrontmatter.due` → `TaskFrontmatter.date`; YAML key on disk is now `date:` instead of `due:` - Added `#[serde(alias = "due")]` so existing task files with `due:` frontmatter still deserialize correctly - Updated google_tasks.rs to write `date:` instead of `due:` in generated YAML - Renamed CLI `--due` flag to `--date`; updated function signature and display string "Due:" → "Date:" Remove serde aliases and rename due→date Drop backwards-compat aliases and update frontmatter/metadata to use the canonical "date"/"group_by_date" fields. The aliases for group_by_due_date and due were removed to avoid maintaining backward-compatibility and to reflect the current schema. Updated storage types to rename the fields and adjusted serialization attributes so YAML/JSON frontmatter and .listdata.json files now use group_by_date and date consistently.
This commit is contained in:
parent
a0c183df82
commit
9ed84690ac
|
|
@ -56,7 +56,7 @@ The GUI uses Svelte 5 runes mode (`$state`, `$derived`, `$effect`, `$props()`).
|
|||
- **Task animations**: Grid-rows `0fr`/`1fr` trick for smooth collapse/expand. Module-level `animateInIds` Set coordinates expand-in after toggle.
|
||||
- **Inline editing**: Click task to edit, auto-save on blur. `debouncedSave` snapshots task before timer to prevent stale-reference errors on component destroy.
|
||||
- **Kebab menus**: Tasks and lists use kebab menus with custom `ConfirmDialog` component (not native `confirm()`). "Move to..." is inline in the menu (not a submenu) to avoid overflow.
|
||||
- **Main panel header**: Hamburger + window controls in top bar; list name (large, bold) + kebab below divider (matching task detail layout). Kebab has Rename, Group by due date, Delete completed, Delete list.
|
||||
- **Main panel header**: Hamburger + window controls in top bar; list name (large, bold) + kebab below divider (matching task detail layout). Kebab has Rename, Group by date, Delete completed, Delete list.
|
||||
- **New task**: FAB button opens bottom toast sheet (outside sliding container for fixed positioning).
|
||||
|
||||
### Development phase
|
||||
|
|
@ -81,10 +81,10 @@ Pre-alpha. No users, no released builds, no data to migrate. Breaking changes to
|
|||
- Workspace switcher drop-up with add/remove
|
||||
- Per-workspace theme system (System default, Light, Dark, Nord, Dracula, Solarized Dark) via CSS `data-theme` attribute
|
||||
- Completed tasks section with animated show/hide
|
||||
- Due date picker/editor (DateTimePicker in new task + task detail); `has_time: bool` field tracks whether time is set
|
||||
- Date picker/editor (DateTimePicker in new task + task detail); `has_time: bool` field tracks whether time is set
|
||||
- Move task between lists (inline list in kebab menu, no submenu)
|
||||
- List rename (inline input in main panel header via kebab)
|
||||
- Group-by-due-date toggle per list (main panel kebab; persists flag but display sorting not yet implemented)
|
||||
- Group-by-date toggle per list (main panel kebab; persists flag but display sorting not yet implemented)
|
||||
- Delete completed tasks (main panel kebab + subtask kebab, with confirmation dialogs)
|
||||
- Keyboard shortcuts (Escape priority chain: settings → detail → list menu → drawer → menus)
|
||||
- Setup screen with 2-step mode selection (Local Folder vs WebDAV Server), window dragging, "Open Existing Folder" option, remote folder browsing
|
||||
|
|
|
|||
|
|
@ -488,7 +488,7 @@ fn rename_list(
|
|||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn set_group_by_due_date(
|
||||
fn set_group_by_date(
|
||||
list_id: String,
|
||||
enabled: bool,
|
||||
state: State<'_, Mutex<AppState>>,
|
||||
|
|
@ -498,12 +498,12 @@ fn set_group_by_due_date(
|
|||
mute_watcher(&mut s);
|
||||
let id = Uuid::parse_str(&list_id).map_err(|e| e.to_string())?;
|
||||
repo_mut(&mut s)?
|
||||
.set_group_by_due_date(id, enabled)
|
||||
.set_group_by_date(id, enabled)
|
||||
.map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn get_group_by_due_date(
|
||||
fn get_group_by_date(
|
||||
list_id: String,
|
||||
state: State<'_, Mutex<AppState>>,
|
||||
) -> Result<bool, String> {
|
||||
|
|
@ -511,7 +511,7 @@ fn get_group_by_due_date(
|
|||
ensure_repo(&mut s)?;
|
||||
let id = Uuid::parse_str(&list_id).map_err(|e| e.to_string())?;
|
||||
repo_ref(&s)?
|
||||
.get_group_by_due_date(id)
|
||||
.get_group_by_date(id)
|
||||
.map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
|
|
@ -924,8 +924,8 @@ pub fn run() {
|
|||
reorder_task,
|
||||
move_task,
|
||||
rename_list,
|
||||
set_group_by_due_date,
|
||||
get_group_by_due_date,
|
||||
set_group_by_date,
|
||||
get_group_by_date,
|
||||
set_webdav_config,
|
||||
set_workspace_theme,
|
||||
set_sync_interval,
|
||||
|
|
|
|||
|
|
@ -9,21 +9,21 @@
|
|||
|
||||
let title = $state("");
|
||||
let description = $state("");
|
||||
let dueDate = $state<string | null>(null);
|
||||
let dueDateHasTime = $state(false);
|
||||
let date = $state<string | null>(null);
|
||||
let dateHasTime = $state(false);
|
||||
let inputEl = $state<HTMLInputElement | null>(null);
|
||||
let showDatePicker = $state(false);
|
||||
|
||||
async function handleSubmit() {
|
||||
if (!title.trim()) return;
|
||||
const created = await app.createTask(title.trim(), description.trim() || undefined);
|
||||
if (dueDate && created) {
|
||||
await app.updateTask({ ...created, due_date: dueDate, has_time: dueDateHasTime });
|
||||
if (date && created) {
|
||||
await app.updateTask({ ...created, date: date, has_time: dateHasTime });
|
||||
}
|
||||
title = "";
|
||||
description = "";
|
||||
dueDate = null;
|
||||
dueDateHasTime = false;
|
||||
date = null;
|
||||
dateHasTime = false;
|
||||
newTaskState.open = false;
|
||||
}
|
||||
|
||||
|
|
@ -31,14 +31,14 @@
|
|||
newTaskState.open = false;
|
||||
title = "";
|
||||
description = "";
|
||||
dueDate = null;
|
||||
dueDateHasTime = false;
|
||||
date = null;
|
||||
dateHasTime = false;
|
||||
showDatePicker = false;
|
||||
}
|
||||
|
||||
function handleDateChange(iso: string | null, hasTime: boolean = false) {
|
||||
dueDate = iso;
|
||||
dueDateHasTime = hasTime;
|
||||
date = iso;
|
||||
dateHasTime = hasTime;
|
||||
}
|
||||
|
||||
function formatDateChip(iso: string): string {
|
||||
|
|
@ -47,7 +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 timePart = dueDateHasTime ? `, ${pad(d.getHours())}:${pad(d.getMinutes())}` : "";
|
||||
const timePart = dateHasTime ? `, ${pad(d.getHours())}:${pad(d.getMinutes())}` : "";
|
||||
if (d.toDateString() === today.toDateString()) return `Today${timePart}`;
|
||||
return `${day}, ${pad(d.getDate())}/${pad(d.getMonth() + 1)}${timePart}`;
|
||||
}
|
||||
|
|
@ -102,12 +102,12 @@
|
|||
<svg class="h-5 w-5 shrink-0 opacity-40" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-12a1 1 0 10-2 0v4a1 1 0 00.293.707l2.828 2.829a1 1 0 101.415-1.415L11 9.586V6z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
{#if dueDate}
|
||||
{#if date}
|
||||
<div class="flex items-center gap-1.5 rounded-full border border-border-light bg-black/5 px-3 py-1 text-sm dark:border-border-dark dark:bg-white/10">
|
||||
<button type="button" onclick={() => (showDatePicker = true)} class="hover:opacity-70">
|
||||
{formatDateChip(dueDate)}
|
||||
{formatDateChip(date)}
|
||||
</button>
|
||||
<button type="button" onclick={() => (dueDate = null)} class="opacity-40 hover:opacity-80">
|
||||
<button type="button" onclick={() => (date = null)} class="opacity-40 hover:opacity-80">
|
||||
<svg class="h-3.5 w-3.5" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z" />
|
||||
</svg>
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@
|
|||
}
|
||||
|
||||
function handleDateChange(iso: string | null, hasTime: boolean = false) {
|
||||
app.updateTask({ ...task, due_date: iso, has_time: hasTime });
|
||||
app.updateTask({ ...task, date: iso, has_time: hasTime });
|
||||
}
|
||||
|
||||
async function handleToggle() {
|
||||
|
|
@ -237,10 +237,10 @@
|
|||
<svg class="h-5 w-5 shrink-0 opacity-40" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-12a1 1 0 10-2 0v4a1 1 0 00.293.707l2.828 2.829a1 1 0 101.415-1.415L11 9.586V6z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
{#if task.due_date}
|
||||
{#if task.date}
|
||||
<div class="flex items-center gap-1.5 rounded-full border border-border-light bg-black/5 px-3 py-1 text-sm dark:border-border-dark dark:bg-white/10">
|
||||
<button onclick={() => (showDatePicker = true)} class="hover:opacity-70">
|
||||
{formatDateChip(task.due_date)}
|
||||
{formatDateChip(task.date)}
|
||||
</button>
|
||||
<button onclick={() => handleDateChange(null)} class="opacity-40 hover:opacity-80">
|
||||
<svg class="h-3.5 w-3.5" viewBox="0 0 20 20" fill="currentColor">
|
||||
|
|
@ -388,7 +388,7 @@
|
|||
<!-- Date picker overlay -->
|
||||
{#if showDatePicker}
|
||||
<DateTimePicker
|
||||
value={task.due_date}
|
||||
value={task.date}
|
||||
has_time={task.has_time}
|
||||
onchange={handleDateChange}
|
||||
onclose={() => (showDatePicker = false)}
|
||||
|
|
|
|||
|
|
@ -150,14 +150,14 @@
|
|||
{#if task.description}
|
||||
<p class="mt-0.5 text-xs opacity-40 line-clamp-1">{task.description}</p>
|
||||
{/if}
|
||||
{#if task.due_date && dateChipStyle !== "hidden"}
|
||||
{#if task.date && dateChipStyle !== "hidden"}
|
||||
{#if dateChipStyle === "overdue"}
|
||||
<span class="mt-1 inline-block rounded-full border border-danger px-2 py-0.5 text-xs text-danger opacity-80">
|
||||
{formatDate(task.due_date)}
|
||||
{formatDate(task.date)}
|
||||
</span>
|
||||
{:else}
|
||||
<span class="mt-1 inline-block rounded-full border border-border-light px-2 py-0.5 text-xs opacity-50 dark:border-border-dark">
|
||||
{formatDate(task.due_date)}
|
||||
{formatDate(task.date)}
|
||||
</span>
|
||||
{/if}
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ let completedTasks = $derived(tasks.filter((t) => t.status === "completed" && !t
|
|||
type TaskGroup = { label: string; tasks: Task[]; date: Date | null };
|
||||
|
||||
let groupedPendingTasks = $derived.by((): TaskGroup[] | null => {
|
||||
if (!activeList?.group_by_due_date) return null;
|
||||
if (!activeList?.group_by_date) return null;
|
||||
const now = new Date();
|
||||
const todayStart = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
||||
const tomorrowStart = new Date(todayStart);
|
||||
|
|
@ -68,10 +68,10 @@ let groupedPendingTasks = $derived.by((): TaskGroup[] | null => {
|
|||
const noDate: Task[] = [];
|
||||
|
||||
for (const task of pendingTasks) {
|
||||
if (!task.due_date) {
|
||||
if (!task.date) {
|
||||
noDate.push(task);
|
||||
} else {
|
||||
const d = new Date(task.due_date);
|
||||
const d = new Date(task.date);
|
||||
const dayStart = new Date(d.getFullYear(), d.getMonth(), d.getDate());
|
||||
if (dayStart < todayStart) overdue.push(task);
|
||||
else if (dayStart.getTime() === todayStart.getTime()) today.push(task);
|
||||
|
|
@ -86,7 +86,7 @@ let groupedPendingTasks = $derived.by((): TaskGroup[] | null => {
|
|||
|
||||
const taskOrderIndex = new Map(pendingTasks.map((t, i) => [t.id, i]));
|
||||
const sortByDue = (a: Task, b: Task) => {
|
||||
const dateDiff = new Date(a.due_date!).getTime() - new Date(b.due_date!).getTime();
|
||||
const dateDiff = new Date(a.date!).getTime() - new Date(b.date!).getTime();
|
||||
if (dateDiff !== 0) return dateDiff;
|
||||
return (taskOrderIndex.get(a.id) ?? 0) - (taskOrderIndex.get(b.id) ?? 0);
|
||||
};
|
||||
|
|
@ -395,11 +395,11 @@ async function renameList(listId: string, newName: string) {
|
|||
}
|
||||
}
|
||||
|
||||
async function setGroupByDueDate(listId: string, enabled: boolean) {
|
||||
async function setGroupByDate(listId: string, enabled: boolean) {
|
||||
try {
|
||||
await invoke("set_group_by_due_date", { listId, enabled });
|
||||
await invoke("set_group_by_date", { listId, enabled });
|
||||
lists = lists.map((l) =>
|
||||
l.id === listId ? { ...l, group_by_due_date: enabled } : l,
|
||||
l.id === listId ? { ...l, group_by_date: enabled } : l,
|
||||
);
|
||||
if (listId === activeListId) await loadTasks();
|
||||
} catch (e) {
|
||||
|
|
@ -656,7 +656,7 @@ export const app = {
|
|||
deleteTask,
|
||||
moveTask,
|
||||
renameList,
|
||||
setGroupByDueDate,
|
||||
setGroupByDate,
|
||||
triggerSync,
|
||||
startAutoSync,
|
||||
stopAutoSync,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ export interface Task {
|
|||
title: string;
|
||||
description: string;
|
||||
status: "backlog" | "completed";
|
||||
due_date: string | null;
|
||||
date: string | null;
|
||||
has_time: boolean;
|
||||
version: number;
|
||||
parent_id: string | null;
|
||||
|
|
@ -15,7 +15,7 @@ export interface TaskList {
|
|||
tasks: Task[];
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
group_by_due_date: boolean;
|
||||
group_by_date: boolean;
|
||||
}
|
||||
|
||||
export type WorkspaceMode = "local" | "webdav";
|
||||
|
|
|
|||
|
|
@ -12,10 +12,10 @@ pub fn enable(list_name: String, workspace: Option<String>) -> Result<()> {
|
|||
.find(|l| l.title == list_name)
|
||||
.ok_or_else(|| anyhow::anyhow!("List '{}' not found", list_name))?;
|
||||
|
||||
repo.set_group_by_due_date(list.id, true)
|
||||
repo.set_group_by_date(list.id, true)
|
||||
.context("Failed to enable grouping")?;
|
||||
|
||||
output::success(&format!("Enabled group-by-due-date for list \"{}\"", list_name));
|
||||
output::success(&format!("Enabled group-by-date for list \"{}\"", list_name));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -30,10 +30,10 @@ pub fn disable(list_name: String, workspace: Option<String>) -> Result<()> {
|
|||
.find(|l| l.title == list_name)
|
||||
.ok_or_else(|| anyhow::anyhow!("List '{}' not found", list_name))?;
|
||||
|
||||
repo.set_group_by_due_date(list.id, false)
|
||||
repo.set_group_by_date(list.id, false)
|
||||
.context("Failed to disable grouping")?;
|
||||
|
||||
output::success(&format!("Disabled group-by-due-date for list \"{}\"", list_name));
|
||||
output::success(&format!("Disabled group-by-date for list \"{}\"", list_name));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ fn print_tasks(tasks: &[Task]) {
|
|||
}
|
||||
for task in tasks {
|
||||
let checkbox = if task.status == TaskStatus::Completed { "[✓]".green() } else { "[ ]".normal() };
|
||||
let due_str = task.due_date.map(|d| format!(" (due: {})", d.format("%Y-%m-%d")).yellow().to_string()).unwrap_or_default();
|
||||
let due_str = task.date.map(|d| format!(" ({})", d.format("%Y-%m-%d")).yellow().to_string()).unwrap_or_default();
|
||||
output::item(&format!("{} {}{} {}", checkbox, task.title, due_str, task.id.to_string().dimmed()));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use uuid::Uuid;
|
|||
use crate::output;
|
||||
use crate::commands::get_repository;
|
||||
|
||||
pub fn add(title: String, list_name: Option<String>, due_str: Option<String>, workspace: Option<String>) -> Result<()> {
|
||||
pub fn add(title: String, list_name: Option<String>, date_str: Option<String>, workspace: Option<String>) -> Result<()> {
|
||||
let (mut repo, _workspace_name) = get_repository(workspace)?;
|
||||
|
||||
// Get lists
|
||||
|
|
@ -29,18 +29,18 @@ pub fn add(title: String, list_name: Option<String>, due_str: Option<String>, wo
|
|||
// Create task
|
||||
let mut task = Task::new(title.clone());
|
||||
|
||||
// Parse due date if provided
|
||||
if let Some(due_str) = due_str {
|
||||
let due_date = parse_due_date(&due_str)?;
|
||||
task.due_date = Some(due_date);
|
||||
// Parse date if provided
|
||||
if let Some(due_str) = date_str {
|
||||
let date = parse_date(&due_str)?;
|
||||
task.date = Some(date);
|
||||
}
|
||||
|
||||
// Save task
|
||||
repo.create_task(list.id, task.clone())
|
||||
.context("Failed to create task")?;
|
||||
|
||||
let due_info = if let Some(due) = task.due_date {
|
||||
format!("\n Due: {}", due.format("%Y-%m-%d"))
|
||||
let due_info = if let Some(due) = task.date {
|
||||
format!("\n Date: {}", due.format("%Y-%m-%d"))
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
|
|
@ -204,7 +204,7 @@ pub fn edit(task_id_str: String, workspace: Option<String>) -> Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_due_date(s: &str) -> Result<DateTime<Utc>> {
|
||||
fn parse_date(s: &str) -> Result<DateTime<Utc>> {
|
||||
// Try parsing as date only (YYYY-MM-DD)
|
||||
if let Ok(naive_date) = chrono::NaiveDate::parse_from_str(s, "%Y-%m-%d") {
|
||||
let naive_datetime = naive_date.and_hms_opt(0, 0, 0)
|
||||
|
|
|
|||
|
|
@ -39,9 +39,9 @@ enum Commands {
|
|||
/// List to add task to
|
||||
#[arg(short, long)]
|
||||
list: Option<String>,
|
||||
/// Due date (ISO 8601 format: YYYY-MM-DD or YYYY-MM-DDTHH:MM:SS)
|
||||
/// Date (ISO 8601 format: YYYY-MM-DD or YYYY-MM-DDTHH:MM:SS)
|
||||
#[arg(short, long)]
|
||||
due: Option<String>,
|
||||
date: Option<String>,
|
||||
/// Workspace to use
|
||||
#[arg(short, long)]
|
||||
workspace: Option<String>,
|
||||
|
|
@ -74,7 +74,7 @@ enum Commands {
|
|||
workspace: Option<String>,
|
||||
},
|
||||
|
||||
/// Toggle group-by-due-date for a list
|
||||
/// Toggle group-by-date for a list
|
||||
#[command(subcommand)]
|
||||
Group(GroupCommands),
|
||||
|
||||
|
|
@ -176,7 +176,7 @@ enum ListCommands {
|
|||
|
||||
#[derive(Subcommand)]
|
||||
enum GroupCommands {
|
||||
/// Enable group-by-due-date for a list
|
||||
/// Enable group-by-date for a list
|
||||
Enable {
|
||||
/// Name of the list
|
||||
#[arg(short, long)]
|
||||
|
|
@ -186,7 +186,7 @@ enum GroupCommands {
|
|||
workspace: Option<String>,
|
||||
},
|
||||
|
||||
/// Disable group-by-due-date for a list
|
||||
/// Disable group-by-date for a list
|
||||
Disable {
|
||||
/// Name of the list
|
||||
#[arg(short, long)]
|
||||
|
|
@ -235,8 +235,8 @@ fn main() -> Result<()> {
|
|||
list::delete(name, workspace)?;
|
||||
}
|
||||
},
|
||||
Commands::Add { title, list, due, workspace } => {
|
||||
task::add(title, list, due, workspace)?;
|
||||
Commands::Add { title, list, date, workspace } => {
|
||||
task::add(title, list, date, workspace)?;
|
||||
}
|
||||
Commands::Complete { task_id, workspace } => {
|
||||
task::complete(task_id, workspace)?;
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ pub struct Task {
|
|||
pub description: String,
|
||||
pub status: TaskStatus,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub due_date: Option<DateTime<Utc>>,
|
||||
pub date: Option<DateTime<Utc>>,
|
||||
#[serde(default)]
|
||||
pub has_time: bool,
|
||||
pub version: u64,
|
||||
|
|
@ -31,7 +31,7 @@ impl Task {
|
|||
title,
|
||||
description: String::new(),
|
||||
status: TaskStatus::Backlog,
|
||||
due_date: None,
|
||||
date: None,
|
||||
has_time: false,
|
||||
version: 0,
|
||||
parent_id: None,
|
||||
|
|
@ -43,8 +43,8 @@ impl Task {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn with_due_date(mut self, due_date: DateTime<Utc>) -> Self {
|
||||
self.due_date = Some(due_date);
|
||||
pub fn with_date(mut self, date: DateTime<Utc>) -> Self {
|
||||
self.date = Some(date);
|
||||
self
|
||||
}
|
||||
|
||||
|
|
@ -69,7 +69,7 @@ pub struct TaskList {
|
|||
pub tasks: Vec<Task>,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
pub group_by_due_date: bool,
|
||||
pub group_by_date: bool,
|
||||
}
|
||||
|
||||
impl TaskList {
|
||||
|
|
@ -81,7 +81,7 @@ impl TaskList {
|
|||
tasks: Vec::new(),
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
group_by_due_date: false,
|
||||
group_by_date: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -152,7 +152,7 @@ mod tests {
|
|||
assert_eq!(task.title, "My Task");
|
||||
assert_eq!(task.description, "");
|
||||
assert_eq!(task.status, TaskStatus::Backlog);
|
||||
assert!(task.due_date.is_none());
|
||||
assert!(task.date.is_none());
|
||||
assert!(!task.has_time);
|
||||
assert_eq!(task.version, 0);
|
||||
assert!(task.parent_id.is_none());
|
||||
|
|
@ -197,12 +197,12 @@ mod tests {
|
|||
let dt = Utc::now();
|
||||
let task = Task::new("Chained".to_string())
|
||||
.with_description("Desc".to_string())
|
||||
.with_due_date(dt)
|
||||
.with_date(dt)
|
||||
.with_parent(parent_id);
|
||||
|
||||
assert_eq!(task.title, "Chained");
|
||||
assert_eq!(task.description, "Desc");
|
||||
assert_eq!(task.due_date, Some(dt));
|
||||
assert_eq!(task.date, Some(dt));
|
||||
assert_eq!(task.parent_id, Some(parent_id));
|
||||
}
|
||||
|
||||
|
|
@ -231,7 +231,7 @@ mod tests {
|
|||
fn test_task_serde_skips_none_fields() {
|
||||
let task = Task::new("Minimal".to_string());
|
||||
let json = serde_json::to_string(&task).unwrap();
|
||||
assert!(!json.contains("due_date"));
|
||||
assert!(!json.contains("\"date\""));
|
||||
assert!(!json.contains("parent_id"));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -118,17 +118,17 @@ impl TaskRepository {
|
|||
}
|
||||
|
||||
// Grouping preference
|
||||
pub fn set_group_by_due_date(&mut self, list_id: Uuid, enabled: bool) -> Result<()> {
|
||||
pub fn set_group_by_date(&mut self, list_id: Uuid, enabled: bool) -> Result<()> {
|
||||
let mut metadata = self.storage.read_list_metadata(list_id)?;
|
||||
metadata.group_by_due_date = enabled;
|
||||
metadata.group_by_date = enabled;
|
||||
metadata.updated_at = chrono::Utc::now();
|
||||
self.storage.write_list_metadata(&metadata)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_group_by_due_date(&self, list_id: Uuid) -> Result<bool> {
|
||||
pub fn get_group_by_date(&self, list_id: Uuid) -> Result<bool> {
|
||||
let metadata = self.storage.read_list_metadata(list_id)?;
|
||||
Ok(metadata.group_by_due_date)
|
||||
Ok(metadata.group_by_date)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -214,19 +214,19 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn test_group_by_due_date() {
|
||||
fn test_group_by_date() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let mut repo = TaskRepository::init(temp_dir.path().to_path_buf()).unwrap();
|
||||
|
||||
let list = repo.create_list("Test List".to_string()).unwrap();
|
||||
|
||||
assert!(!repo.get_group_by_due_date(list.id).unwrap());
|
||||
assert!(!repo.get_group_by_date(list.id).unwrap());
|
||||
|
||||
repo.set_group_by_due_date(list.id, true).unwrap();
|
||||
assert!(repo.get_group_by_due_date(list.id).unwrap());
|
||||
repo.set_group_by_date(list.id, true).unwrap();
|
||||
assert!(repo.get_group_by_date(list.id).unwrap());
|
||||
|
||||
repo.set_group_by_due_date(list.id, false).unwrap();
|
||||
assert!(!repo.get_group_by_due_date(list.id).unwrap());
|
||||
repo.set_group_by_date(list.id, false).unwrap();
|
||||
assert!(!repo.get_group_by_date(list.id).unwrap());
|
||||
}
|
||||
|
||||
// --- Error path tests ---
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ pub struct ListMetadata {
|
|||
pub id: Uuid,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
pub group_by_due_date: bool,
|
||||
pub group_by_date: bool,
|
||||
pub task_order: Vec<Uuid>,
|
||||
}
|
||||
|
||||
|
|
@ -73,7 +73,7 @@ impl ListMetadata {
|
|||
id,
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
group_by_due_date: false,
|
||||
group_by_date: false,
|
||||
task_order: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
|
@ -88,7 +88,7 @@ pub struct TaskFrontmatter {
|
|||
pub id: Uuid,
|
||||
pub status: TaskStatus,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub due: Option<DateTime<Utc>>,
|
||||
pub date: Option<DateTime<Utc>>,
|
||||
#[serde(default, skip_serializing_if = "is_false")]
|
||||
pub has_time: bool,
|
||||
#[serde(default = "default_version")]
|
||||
|
|
@ -360,7 +360,7 @@ impl Storage for FileSystemStorage {
|
|||
title,
|
||||
description,
|
||||
status: frontmatter.status,
|
||||
due_date: frontmatter.due,
|
||||
date: frontmatter.date,
|
||||
has_time: frontmatter.has_time,
|
||||
version: frontmatter.version,
|
||||
parent_id: frontmatter.parent,
|
||||
|
|
@ -456,7 +456,7 @@ impl Storage for FileSystemStorage {
|
|||
title,
|
||||
description,
|
||||
status: frontmatter.status,
|
||||
due_date: frontmatter.due,
|
||||
date: frontmatter.date,
|
||||
has_time: frontmatter.has_time,
|
||||
version: frontmatter.version,
|
||||
parent_id: frontmatter.parent,
|
||||
|
|
@ -547,7 +547,7 @@ impl Storage for FileSystemStorage {
|
|||
tasks: Vec::new(),
|
||||
created_at: list_metadata.created_at,
|
||||
updated_at: list_metadata.updated_at,
|
||||
group_by_due_date: list_metadata.group_by_due_date,
|
||||
group_by_date: list_metadata.group_by_date,
|
||||
};
|
||||
|
||||
Ok(task_list)
|
||||
|
|
@ -582,7 +582,7 @@ impl Storage for FileSystemStorage {
|
|||
tasks,
|
||||
created_at: list_metadata.created_at,
|
||||
updated_at: list_metadata.updated_at,
|
||||
group_by_due_date: list_metadata.group_by_due_date,
|
||||
group_by_date: list_metadata.group_by_date,
|
||||
};
|
||||
|
||||
lists.push(task_list);
|
||||
|
|
@ -761,7 +761,7 @@ mod tests {
|
|||
|
||||
let content = "---\nid: 550e8400-e29b-41d4-a716-446655440000\nstatus: backlog\ndue: 2026-06-15T12:00:00Z\nversion: 2\nparent: 660e8400-e29b-41d4-a716-446655440001\n---\n\nNotes";
|
||||
let (fm, _) = storage.parse_markdown_with_frontmatter(content).unwrap();
|
||||
assert!(fm.due.is_some());
|
||||
assert!(fm.date.is_some());
|
||||
assert!(fm.parent.is_some());
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue