Add actionable push notifications and fix recurring task completion

Features:
- Add task action buttons to push notifications (complete, view, cancel, etc.)
- Add button types logic for different task states (overdue, in_progress, etc.)
- Implement Chain of Responsibility pattern for task categorization
- Add comprehensive kanban categorization documentation

Fixes:
- Reset recurring task status to Pending after completion so tasks appear
  in correct kanban column (was staying in "In Progress")
- Fix PostgreSQL EXTRACT function error in overdue notifications query
- Update seed data to properly set next_due_date for recurring tasks

Admin:
- Add tasks list to residence detail page
- Fix task edit page to properly handle all fields

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Trey t
2025-12-05 14:23:14 -06:00
parent bbf3999c79
commit 1b06c0639c
22 changed files with 2715 additions and 142 deletions

View File

@@ -324,6 +324,67 @@ func TestTaskService_CreateCompletion(t *testing.T) {
assert.Equal(t, "Completed successfully", resp.Notes)
}
func TestTaskService_CreateCompletion_RecurringTask_ResetsStatusToPending(t *testing.T) {
db := testutil.SetupTestDB(t)
testutil.SeedLookupData(t, db)
taskRepo := repositories.NewTaskRepository(db)
residenceRepo := repositories.NewResidenceRepository(db)
service := NewTaskService(taskRepo, residenceRepo)
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "password")
residence := testutil.CreateTestResidence(t, db, user.ID, "Test House")
// Get the "In Progress" status (ID=2) and a recurring frequency
var inProgressStatus models.TaskStatus
db.Where("name = ?", "In Progress").First(&inProgressStatus)
var monthlyFrequency models.TaskFrequency
db.Where("name = ?", "Monthly").First(&monthlyFrequency)
// Create a recurring task with "In Progress" status
dueDate := time.Now().AddDate(0, 0, 7) // Due in 7 days
task := &models.Task{
ResidenceID: residence.ID,
CreatedByID: user.ID,
Title: "Recurring Task",
StatusID: &inProgressStatus.ID,
FrequencyID: &monthlyFrequency.ID,
DueDate: &dueDate,
NextDueDate: &dueDate,
IsCancelled: false,
IsArchived: false,
}
err := db.Create(task).Error
require.NoError(t, err)
// Complete the task
req := &requests.CreateTaskCompletionRequest{
TaskID: task.ID,
Notes: "Monthly maintenance done",
}
resp, err := service.CreateCompletion(req, user.ID)
require.NoError(t, err)
assert.NotZero(t, resp.ID)
// Verify the task in the response has status reset to "Pending" (ID=1)
require.NotNil(t, resp.Task, "Response should include the updated task")
require.NotNil(t, resp.Task.StatusID, "Task should have a status ID")
assert.Equal(t, uint(1), *resp.Task.StatusID, "Recurring task status should be reset to Pending (ID=1) after completion")
// Verify NextDueDate was updated (should be ~30 days from now for monthly)
require.NotNil(t, resp.Task.NextDueDate, "Recurring task should have NextDueDate set")
expectedNextDue := time.Now().AddDate(0, 0, 30) // Monthly = 30 days
assert.WithinDuration(t, expectedNextDue, *resp.Task.NextDueDate, 24*time.Hour, "NextDueDate should be approximately 30 days from now")
// Also verify by reloading from database directly
var reloadedTask models.Task
db.Preload("Status").First(&reloadedTask, task.ID)
require.NotNil(t, reloadedTask.StatusID)
assert.Equal(t, uint(1), *reloadedTask.StatusID, "Database should show Pending status")
assert.Equal(t, "Pending", reloadedTask.Status.Name)
}
func TestTaskService_GetCompletion(t *testing.T) {
db := testutil.SetupTestDB(t)
testutil.SeedLookupData(t, db)