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
+1
View File
@@ -27,6 +27,7 @@
* @property {number|null} recurrence_duration
* @property {string|null} parent_uuid
* @property {string[]} tags
* @property {number} urgency
*/
/**
@@ -73,6 +73,16 @@
{/each}
</div>
{/if}
{#if task.urgency > 0}
<span class="meta-item urgency"
class:urgency-critical={task.urgency >= 10}
class:urgency-high={task.urgency >= 5 && task.urgency < 10}
class:urgency-normal={task.urgency > 0 && task.urgency < 5}
>
{task.urgency.toFixed(1)}
</span>
{/if}
</div>
</div>
</div>
@@ -196,4 +206,21 @@
color: var(--color-tag-text);
border-radius: 0.25rem;
}
.urgency {
margin-left: auto;
font-variant-numeric: tabular-nums;
}
.urgency-critical {
color: var(--color-priority-high-text);
}
.urgency-high {
color: var(--color-priority-medium-text);
}
.urgency-normal {
color: var(--text-secondary);
}
</style>
+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
}
];