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:
+3
-91
@@ -4,10 +4,8 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.jnss.me/joakim/opal/internal/engine"
|
||||
"github.com/google/uuid"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -101,99 +99,13 @@ func parseAddArgs(args []string) (string, []string, error) {
|
||||
}
|
||||
|
||||
func addRecurringTask(description string, mod *engine.Modifier) error {
|
||||
// Extract recurrence pattern
|
||||
recurPattern := mod.SetAttributes["recur"]
|
||||
if recurPattern == nil {
|
||||
return fmt.Errorf("no recurrence pattern specified")
|
||||
}
|
||||
|
||||
// Validate: recurring tasks must have due date
|
||||
if mod.SetAttributes["due"] == nil {
|
||||
return fmt.Errorf("recurring tasks require a due date (use due:YYYY-MM-DD or due:monday)")
|
||||
}
|
||||
|
||||
duration, err := engine.ParseRecurrencePattern(*recurPattern)
|
||||
instance, err := engine.CreateRecurringTask(description, mod)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid recurrence pattern: %w", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Create template task (without saving yet)
|
||||
now := time.Now()
|
||||
template := &engine.Task{
|
||||
UUID: uuid.New(),
|
||||
Status: engine.StatusRecurring,
|
||||
Description: description,
|
||||
Priority: engine.PriorityDefault,
|
||||
Created: now,
|
||||
Modified: now,
|
||||
RecurrenceDuration: &duration,
|
||||
Tags: []string{},
|
||||
}
|
||||
|
||||
// Create modifier without the recur attribute
|
||||
tempMod := &engine.Modifier{
|
||||
SetAttributes: make(map[string]*string),
|
||||
AttributeOrder: []string{},
|
||||
AddTags: mod.AddTags,
|
||||
RemoveTags: mod.RemoveTags,
|
||||
}
|
||||
|
||||
// Copy all attributes except recur
|
||||
for _, key := range mod.AttributeOrder {
|
||||
if key != "recur" {
|
||||
val := mod.SetAttributes[key]
|
||||
tempMod.SetAttributes[key] = val
|
||||
tempMod.AttributeOrder = append(tempMod.AttributeOrder, key)
|
||||
}
|
||||
}
|
||||
|
||||
// Apply modifiers to template before first save
|
||||
if err := tempMod.ApplyToNew(template); err != nil {
|
||||
return fmt.Errorf("failed to apply modifiers to template: %w", err)
|
||||
}
|
||||
|
||||
// Save template
|
||||
if err := template.Save(); err != nil {
|
||||
return fmt.Errorf("failed to save template: %w", err)
|
||||
}
|
||||
|
||||
// Add tags to template (requires task.ID from save)
|
||||
for _, tag := range mod.AddTags {
|
||||
if err := template.AddTag(tag); err != nil {
|
||||
return fmt.Errorf("failed to add tag to template: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Create first instance
|
||||
instance := &engine.Task{
|
||||
UUID: uuid.New(),
|
||||
Status: engine.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 fmt.Errorf("failed to save first instance: %w", err)
|
||||
}
|
||||
|
||||
// Copy tags to instance
|
||||
for _, tag := range template.Tags {
|
||||
if err := instance.AddTag(tag); err != nil {
|
||||
return fmt.Errorf("failed to add tag to instance: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf("Created recurring task %s\n", template.UUID)
|
||||
fmt.Printf("Created recurring task %s\n", *instance.ParentUUID)
|
||||
fmt.Printf("First instance: %s\n", instance.UUID)
|
||||
fmt.Printf("Recurrence: %s\n", engine.FormatRecurrenceDuration(duration))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user