feat: add JSON serialization, urgency field, and snake_case API contract

Fix latent API bug where multi-word fields (RecurrenceDuration, ParentUUID,
CreatedAt) serialized as PascalCase, breaking the frontend. Add explicit
snake_case json tags and custom MarshalJSON/UnmarshalJSON on Task, Status,
and APIKey to emit unix timestamps and string status codes.

Add Urgency float64 as a derived field on Task, populated via
PopulateUrgency helper in all handlers before serialization. The report
engine's sortByUrgency now also retains the computed score.

Frontend updated with urgency type, color-coded badge in TaskItem, and
mock data values.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-15 14:58:34 +01:00
parent 924b66bc64
commit 3bb2ef2759
9 changed files with 581 additions and 51 deletions
+30 -15
View File
@@ -27,7 +27,8 @@ export const mockTasks = [
until: null,
recurrence_duration: null,
parent_uuid: null,
tags: ['devops', 'selfhosted']
tags: ['devops', 'selfhosted'],
urgency: 14.2
},
{
uuid: '11111111-1111-4111-a111-111111111102',
@@ -46,7 +47,8 @@ export const mockTasks = [
until: null,
recurrence_duration: null,
parent_uuid: null,
tags: ['testing', 'backend']
tags: ['testing', 'backend'],
urgency: 7.3
},
{
uuid: '11111111-1111-4111-a111-111111111103',
@@ -65,7 +67,8 @@ export const mockTasks = [
until: null,
recurrence_duration: null,
parent_uuid: null,
tags: ['bug']
tags: ['bug'],
urgency: 4.1
},
{
uuid: '11111111-1111-4111-a111-111111111104',
@@ -84,7 +87,8 @@ export const mockTasks = [
until: null,
recurrence_duration: null,
parent_uuid: null,
tags: ['errand']
tags: ['errand'],
urgency: 3.5
},
{
uuid: '11111111-1111-4111-a111-111111111105',
@@ -103,7 +107,8 @@ export const mockTasks = [
until: null,
recurrence_duration: null,
parent_uuid: null,
tags: ['frontend', 'design']
tags: ['frontend', 'design'],
urgency: 15.8
},
{
uuid: '11111111-1111-4111-a111-111111111106',
@@ -122,7 +127,8 @@ export const mockTasks = [
until: null,
recurrence_duration: null,
parent_uuid: null,
tags: ['admin']
tags: ['admin'],
urgency: 2.4
},
{
uuid: '11111111-1111-4111-a111-111111111107',
@@ -141,7 +147,8 @@ export const mockTasks = [
until: null,
recurrence_duration: null,
parent_uuid: null,
tags: ['frontend']
tags: ['frontend'],
urgency: 2.9
},
{
uuid: '11111111-1111-4111-a111-111111111108',
@@ -160,7 +167,8 @@ export const mockTasks = [
until: null,
recurrence_duration: null,
parent_uuid: null,
tags: ['selfhosted', 'maintenance']
tags: ['selfhosted', 'maintenance'],
urgency: 1.6
},
{
uuid: '11111111-1111-4111-a111-111111111109',
@@ -179,7 +187,8 @@ export const mockTasks = [
until: null,
recurrence_duration: null,
parent_uuid: null,
tags: ['reading', 'learning']
tags: ['reading', 'learning'],
urgency: 1.2
},
{
uuid: '11111111-1111-4111-a111-111111111110',
@@ -198,7 +207,8 @@ export const mockTasks = [
until: null,
recurrence_duration: null,
parent_uuid: null,
tags: ['review', 'backend']
tags: ['review', 'backend'],
urgency: 10.5
},
// ── Completed tasks ──────────────────────────────────────────
@@ -219,7 +229,8 @@ export const mockTasks = [
until: null,
recurrence_duration: null,
parent_uuid: null,
tags: ['backend', 'refactor']
tags: ['backend', 'refactor'],
urgency: 0
},
{
uuid: '22222222-2222-4222-a222-222222222202',
@@ -238,7 +249,8 @@ export const mockTasks = [
until: null,
recurrence_duration: null,
parent_uuid: null,
tags: ['auth', 'selfhosted']
tags: ['auth', 'selfhosted'],
urgency: 0
},
{
uuid: '22222222-2222-4222-a222-222222222203',
@@ -257,7 +269,8 @@ export const mockTasks = [
until: null,
recurrence_duration: null,
parent_uuid: null,
tags: ['ux', 'backend']
tags: ['ux', 'backend'],
urgency: 0
},
{
uuid: '22222222-2222-4222-a222-222222222204',
@@ -276,7 +289,8 @@ export const mockTasks = [
until: null,
recurrence_duration: null,
parent_uuid: null,
tags: ['bug', 'backend']
tags: ['bug', 'backend'],
urgency: 0
},
{
uuid: '22222222-2222-4222-a222-222222222205',
@@ -295,6 +309,7 @@ export const mockTasks = [
until: null,
recurrence_duration: null,
parent_uuid: null,
tags: ['docs']
tags: ['docs'],
urgency: 0
}
];