Repeats, estimates, weekly review: completing a repeating task spawns the next occurrence; estimate chips + quick-add tokens (~30m, monthly); review screen with one-tap triage; server gains repeat column and applies its idempotent schema at boot
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,8 +1,12 @@
|
||||
// Natural-language quick add, Todoist style:
|
||||
// "Pay rent tomorrow p1 @home #Sydney ~30m"
|
||||
// "Pay rent tomorrow p1 @home #Sydney ~30m monthly"
|
||||
// → title "Pay rent", due tomorrow, priority 1, context @home,
|
||||
// project "Sydney" (matched by name prefix), estimate 30 min.
|
||||
import type { Project, Task } from "./types";
|
||||
// project "Sydney" (matched by name prefix), estimate 30 min, repeats monthly.
|
||||
//
|
||||
// The add form parses the title field live and mirrors every recognized token
|
||||
// into a visible form control, so a false positive (e.g. "fri" in a name) is
|
||||
// seen and fixable, never silent.
|
||||
import type { Project, Repeat, Task } from "./types";
|
||||
|
||||
export interface ParsedQuickAdd {
|
||||
title: string;
|
||||
@@ -11,9 +15,17 @@ export interface ParsedQuickAdd {
|
||||
context?: string;
|
||||
project_id?: string;
|
||||
estimate_min?: number;
|
||||
repeat?: Repeat;
|
||||
}
|
||||
|
||||
const WEEKDAYS = ["sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"];
|
||||
const REPEAT_WORDS: Record<string, Repeat> = {
|
||||
daily: "daily",
|
||||
weekdays: "weekdays",
|
||||
weekly: "weekly",
|
||||
monthly: "monthly",
|
||||
yearly: "yearly",
|
||||
};
|
||||
|
||||
function iso(d: Date): string {
|
||||
const y = d.getFullYear();
|
||||
@@ -26,7 +38,8 @@ function parseDateWord(word: string): string | undefined {
|
||||
const w = word.toLowerCase();
|
||||
const today = new Date();
|
||||
if (w === "today" || w === "tod") return iso(today);
|
||||
if (w === "tomorrow" || w === "tmr" || w === "tom") {
|
||||
// No "tom" alias — it's a name far more often than a date.
|
||||
if (w === "tomorrow" || w === "tmr") {
|
||||
const d = new Date(today);
|
||||
d.setDate(d.getDate() + 1);
|
||||
return iso(d);
|
||||
@@ -77,6 +90,11 @@ export function parseQuickAdd(input: string, projects: Project[]): ParsedQuickAd
|
||||
out.estimate_min = est;
|
||||
continue;
|
||||
}
|
||||
const rep = REPEAT_WORDS[token.toLowerCase()];
|
||||
if (rep && out.repeat === undefined) {
|
||||
out.repeat = rep;
|
||||
continue;
|
||||
}
|
||||
if (out.due_date === undefined) {
|
||||
const due = parseDateWord(token);
|
||||
if (due) {
|
||||
|
||||
Reference in New Issue
Block a user