feat: add parse endpoint, refactor recurring tasks, and improve web task completion
Extract CreateRecurringTask into engine package for reuse by both CLI and API. Add POST /tasks/parse endpoint for CLI-style input parsing. Remove FK constraint on change_log to preserve history after task deletion. Update web frontend to filter completed tasks from view and add mock mode support for development. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -100,6 +100,94 @@ func CalculateNextDue(currentDue time.Time, recurrence time.Duration) time.Time
|
||||
return currentDue.Add(recurrence)
|
||||
}
|
||||
|
||||
// CreateRecurringTask creates a recurring task template and its first instance.
|
||||
// It validates the recurrence pattern and due date, creates the template with
|
||||
// StatusRecurring, then creates the first pending instance linked to it.
|
||||
// Returns the first instance task.
|
||||
func CreateRecurringTask(description string, mod *Modifier) (*Task, error) {
|
||||
recurPattern := mod.SetAttributes["recur"]
|
||||
if recurPattern == nil {
|
||||
return nil, fmt.Errorf("no recurrence pattern specified")
|
||||
}
|
||||
|
||||
if mod.SetAttributes["due"] == nil {
|
||||
return nil, fmt.Errorf("recurring tasks require a due date (use due:YYYY-MM-DD or due:monday)")
|
||||
}
|
||||
|
||||
duration, err := ParseRecurrencePattern(*recurPattern)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid recurrence pattern: %w", err)
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
template := &Task{
|
||||
UUID: uuid.New(),
|
||||
Status: StatusRecurring,
|
||||
Description: description,
|
||||
Priority: PriorityDefault,
|
||||
Created: now,
|
||||
Modified: now,
|
||||
RecurrenceDuration: &duration,
|
||||
Tags: []string{},
|
||||
}
|
||||
|
||||
// Build modifier without the recur attribute
|
||||
tempMod := &Modifier{
|
||||
SetAttributes: make(map[string]*string),
|
||||
AttributeOrder: []string{},
|
||||
AddTags: mod.AddTags,
|
||||
RemoveTags: mod.RemoveTags,
|
||||
}
|
||||
for _, key := range mod.AttributeOrder {
|
||||
if key != "recur" {
|
||||
tempMod.SetAttributes[key] = mod.SetAttributes[key]
|
||||
tempMod.AttributeOrder = append(tempMod.AttributeOrder, key)
|
||||
}
|
||||
}
|
||||
|
||||
if err := tempMod.ApplyToNew(template); err != nil {
|
||||
return nil, fmt.Errorf("failed to apply modifiers to template: %w", err)
|
||||
}
|
||||
|
||||
if err := template.Save(); err != nil {
|
||||
return nil, fmt.Errorf("failed to save template: %w", err)
|
||||
}
|
||||
|
||||
for _, tag := range mod.AddTags {
|
||||
if err := template.AddTag(tag); err != nil {
|
||||
return nil, fmt.Errorf("failed to add tag to template: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Create first instance
|
||||
instance := &Task{
|
||||
UUID: uuid.New(),
|
||||
Status: StatusPending,
|
||||
Description: description,
|
||||
Priority: template.Priority,
|
||||
Created: now,
|
||||
Modified: now,
|
||||
ParentUUID: &template.UUID,
|
||||
Due: template.Due,
|
||||
Wait: template.Wait,
|
||||
Scheduled: template.Scheduled,
|
||||
Project: template.Project,
|
||||
Tags: []string{},
|
||||
}
|
||||
|
||||
if err := instance.Save(); err != nil {
|
||||
return nil, fmt.Errorf("failed to save first instance: %w", err)
|
||||
}
|
||||
|
||||
for _, tag := range template.Tags {
|
||||
if err := instance.AddTag(tag); err != nil {
|
||||
return nil, fmt.Errorf("failed to add tag to instance: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return instance, nil
|
||||
}
|
||||
|
||||
// SpawnNextInstance creates a new task instance from completed recurring task
|
||||
func SpawnNextInstance(completedInstance *Task) error {
|
||||
if completedInstance.ParentUUID == nil {
|
||||
|
||||
Reference in New Issue
Block a user