Skip to content

Commit 7591445

Browse files
refactor: align dashboard and tests with Eisenhower metrics
1 parent fdc13bc commit 7591445

11 files changed

Lines changed: 326 additions & 146 deletions

File tree

app/Http/Controllers/DashboardController.php

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace App\Http\Controllers;
44

5+
use App\Models\Todo;
56
use Illuminate\Http\Request;
67
use Inertia\Inertia;
78

@@ -46,23 +47,23 @@ public function __invoke(Request $request)
4647
$stats = [
4748
'important_urgent' => (clone $notArchivedTodos)
4849
->where('is_completed', false)
49-
->where('importance', 'important')
50-
->where('priority', 'urgent')
50+
->where('importance', Todo::IMPORTANCE_IMPORTANT)
51+
->where('priority', Todo::PRIORITY_URGENT)
5152
->count(),
5253
'important_not_urgent' => (clone $notArchivedTodos)
5354
->where('is_completed', false)
54-
->where('importance', 'important')
55-
->where('priority', 'not_urgent')
55+
->where('importance', Todo::IMPORTANCE_IMPORTANT)
56+
->where('priority', Todo::PRIORITY_NOT_URGENT)
5657
->count(),
5758
'not_important_urgent' => (clone $notArchivedTodos)
5859
->where('is_completed', false)
59-
->where('importance', 'not_important')
60-
->where('priority', 'urgent')
60+
->where('importance', Todo::IMPORTANCE_NOT_IMPORTANT)
61+
->where('priority', Todo::PRIORITY_URGENT)
6162
->count(),
6263
'not_important_not_urgent' => (clone $notArchivedTodos)
6364
->where('is_completed', false)
64-
->where('importance', 'not_important')
65-
->where('priority', 'not_urgent')
65+
->where('importance', Todo::IMPORTANCE_NOT_IMPORTANT)
66+
->where('priority', Todo::PRIORITY_NOT_URGENT)
6667
->count(),
6768
'completed' => (clone $notArchivedTodos)->where('is_completed', true)->count(),
6869
'archived' => $user->todos()->archived()->count(),

app/Http/Controllers/TodoController.php

Lines changed: 50 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,10 @@ public function index(Request $request)
4545
$query->pending();
4646
break;
4747
case 'urgent':
48-
$query->highPriority();
48+
$query->byPriority(Todo::PRIORITY_URGENT);
4949
break;
5050
case 'not_urgent':
51-
$query->lowPriority();
51+
$query->byPriority(Todo::PRIORITY_NOT_URGENT);
5252
break;
5353
case 'important':
5454
$query->byImportance(Todo::IMPORTANCE_IMPORTANT);
@@ -476,16 +476,23 @@ public function toggle(Request $request, Todo $todo)
476476

477477
if ($isCompleted) {
478478
$updateData['priority'] = null;
479+
$updateData['importance'] = null;
480+
} else {
481+
$updateData['priority'] = $todo->priority ?? Todo::PRIORITY_NOT_URGENT;
482+
$updateData['importance'] = $todo->importance ?? Todo::IMPORTANCE_NOT_IMPORTANT;
479483
}
480484

481485
$todo->update($updateData);
486+
$todo->refresh();
482487

483488
// JSON for API, redirect for web/Inertia
484489
if ($request->wantsJson() || $request->is('api/*')) {
485490
return response()->json([
486491
'message' => 'Todo status updated successfully',
487-
'is_completed' => ! $todo->is_completed,
488-
'completed_at' => ! $todo->is_completed ? now() : null,
492+
'is_completed' => $todo->is_completed,
493+
'completed_at' => $todo->completed_at,
494+
'priority' => $todo->priority,
495+
'importance' => $todo->importance,
489496
]);
490497
}
491498

@@ -529,36 +536,51 @@ public function updatePriority(Request $request, Todo $todo)
529536

530537
$validated = $request->validate([
531538
'priority' => 'nullable|in:not_urgent,urgent',
539+
'importance' => 'nullable|in:not_important,important',
532540
'is_completed' => 'boolean',
533541
]);
534542

535-
$updateData = [];
543+
$isCompleted = array_key_exists('is_completed', $validated)
544+
? (bool) $validated['is_completed']
545+
: $todo->is_completed;
546+
547+
$priority = array_key_exists('priority', $validated)
548+
? $validated['priority']
549+
: $todo->priority;
550+
551+
$importance = array_key_exists('importance', $validated)
552+
? $validated['importance']
553+
: $todo->importance;
536554

537-
// Handle priority updates
538-
if (array_key_exists('priority', $validated)) {
539-
$updateData['priority'] = $validated['priority'];
555+
if ($isCompleted) {
556+
$priority = null;
557+
$importance = null;
558+
} else {
559+
$priority = $priority ?? Todo::PRIORITY_NOT_URGENT;
560+
$importance = $importance ?? Todo::IMPORTANCE_NOT_IMPORTANT;
540561
}
541562

542-
if (isset($validated['is_completed'])) {
543-
$updateData['is_completed'] = $validated['is_completed'];
544-
$updateData['completed_at'] = $validated['is_completed'] ? now() : null;
563+
$updateData = [
564+
'priority' => $priority,
565+
'importance' => $importance,
566+
];
545567

546-
// When marking as completed, remove priority (set to null)
547-
if ($validated['is_completed']) {
548-
$updateData['priority'] = null;
549-
$updateData['importance'] = null;
550-
}
568+
if (array_key_exists('is_completed', $validated)) {
569+
$updateData['is_completed'] = $isCompleted;
570+
$updateData['completed_at'] = $isCompleted ? now() : null;
551571
}
552572

553573
$todo->update($updateData);
574+
$todo->refresh();
554575

555576
// JSON for API, redirect for web/Inertia
556577
if ($request->wantsJson() || $request->is('api/*')) {
557578
return response()->json([
558579
'message' => 'Todo priority updated successfully',
559-
'priority' => $updateData['priority'] ?? $todo->priority,
560-
'is_completed' => isset($validated['is_completed']) ? $validated['is_completed'] : $todo->is_completed,
561-
'completed_at' => isset($validated['is_completed']) ? ($validated['is_completed'] ? now() : null) : $todo->completed_at,
580+
'priority' => $todo->priority,
581+
'importance' => $todo->importance,
582+
'is_completed' => $todo->is_completed,
583+
'completed_at' => $todo->completed_at,
562584
]);
563585
}
564586

@@ -701,23 +723,27 @@ public function archiveCompleted(Request $request)
701723
return redirect()->back()->with('success', "Successfully archived {$completedCount} completed todos");
702724
}
703725

704-
/**
705-
* Display archived todos.
706-
*/
707726
public function archived(Request $request)
708727
{
709-
$archivedTodos = Auth::user()->todos()
728+
$user = Auth::user();
729+
730+
$archivedTodos = $user->todos()
710731
->archived()
711732
->with(['tags', 'relatedTodos', 'linkedByTodos'])
712733
->orderBy('archived_at', 'desc')
713-
->paginate(12)
734+
->paginate(15)
714735
->withQueryString();
715736

716737
return Inertia::render('Todos/Archived', [
717738
'todos' => $archivedTodos,
718739
]);
719740
}
720741

742+
/**
743+
'todos' => $archivedTodos,
744+
]);
745+
}
746+
721747
/**
722748
* Upload attachment for a todo.
723749
*/

resources/js/Components/EisenhowerMatrix.jsx

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -210,9 +210,41 @@ export default function EisenhowerMatrix({ todos = [], onTaskSelect = NO_OP, sel
210210
}
211211
};
212212

213+
const resolveImportance = (todo) => {
214+
const raw = todo.importance ?? todo.importance_level ?? null;
215+
216+
if (typeof raw !== 'string') {
217+
return 'not_important';
218+
}
219+
220+
const normalized = raw.toLowerCase();
221+
222+
if (normalized === 'high' || normalized === 'important') {
223+
return 'important';
224+
}
225+
226+
return 'not_important';
227+
};
228+
229+
const resolvePriority = (todo) => {
230+
const raw = todo.priority ?? todo.priority_level ?? null;
231+
232+
if (typeof raw !== 'string') {
233+
return 'not_urgent';
234+
}
235+
236+
const normalized = raw.toLowerCase();
237+
238+
if (normalized === 'urgent' || normalized === 'high') {
239+
return 'urgent';
240+
}
241+
242+
return 'not_urgent';
243+
};
244+
213245
const getQuadrant = (todo) => {
214-
const priority = todo.priority ?? 'not_urgent';
215-
const importance = todo.importance ?? 'not_important';
246+
const priority = resolvePriority(todo);
247+
const importance = resolveImportance(todo);
216248

217249
if (importance === 'important' && priority === 'urgent') return 'q1';
218250
if (importance === 'important' && priority === 'not_urgent') return 'q2';

resources/js/Pages/Todos/Index.jsx

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -77,32 +77,29 @@ export default function Index({ todos, tags, filter, selectedTag, selectedType }
7777
setTodoToDelete(null);
7878
};
7979

80+
const PRIORITY_LABELS = {
81+
urgent: 'Urgent',
82+
not_urgent: 'Not Urgent',
83+
};
84+
8085
const formatPriorityLabel = (priority) => {
8186
if (!priority) {
8287
return '';
8388
}
8489

85-
return priority.charAt(0).toUpperCase() + priority.slice(1);
90+
return PRIORITY_LABELS[priority] ?? priority.replace(/_/g, ' ').replace(/\b\w/g, (char) => char.toUpperCase());
8691
};
8792

8893
const getPriorityStyle = (priority) => {
8994
const palette = {
90-
low: {
91-
badgeBg: '#10B981',
92-
badgeText: '#FFFFFF',
93-
},
94-
medium: {
95-
badgeBg: '#F59E0B',
96-
badgeText: '#FFFFFF',
97-
},
98-
high: {
99-
badgeBg: '#EF4444',
100-
badgeText: '#FFFFFF',
101-
},
10295
urgent: {
10396
badgeBg: '#DC2626',
10497
badgeText: '#FFFFFF',
10598
},
99+
not_urgent: {
100+
badgeBg: '#2563EB',
101+
badgeText: '#FFFFFF',
102+
},
106103
default: {
107104
badgeBg: '#111827',
108105
badgeText: '#FFFFFF',
@@ -259,8 +256,10 @@ export default function Index({ todos, tags, filter, selectedTag, selectedType }
259256
{ key: null, label: 'All' },
260257
{ key: 'pending', label: 'Pending' },
261258
{ key: 'completed', label: 'Completed' },
262-
{ key: 'high_priority', label: 'High Priority' },
263-
{ key: 'low_priority', label: 'Low Priority' },
259+
{ key: 'urgent', label: 'Urgent' },
260+
{ key: 'not_urgent', label: 'Not Urgent' },
261+
{ key: 'important', label: 'Important' },
262+
{ key: 'not_important', label: 'Not Important' },
264263
].map(({ key, label }) => (
265264
<Link
266265
key={key || 'all'}

resources/js/Pages/Todos/__tests__/filterTodos.test.js

Lines changed: 40 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,43 +9,67 @@ test('returns empty array for non-array input', () => {
99
assert.deepEqual(result, []);
1010
});
1111

12-
test('sorts tasks by completion status, priority, and created_at', () => {
12+
test('sorts tasks by completion status, priority, importance, and created_at', () => {
1313
const todos = [
14-
{ id: 1, is_completed: false, priority: 'medium', created_at: '2025-10-20T10:00:00Z' },
15-
{ id: 2, is_completed: true, priority: 'high', created_at: '2025-10-21T09:00:00Z' },
16-
{ id: 3, is_completed: false, priority: 'urgent', created_at: '2025-10-19T12:00:00Z' },
17-
{ id: 4, is_completed: false, priority: 'low', created_at: '2025-10-22T08:00:00Z' },
14+
{ id: 1, is_completed: false, priority: 'not_urgent', importance: 'important', created_at: '2025-10-20T10:00:00Z' },
15+
{ id: 2, is_completed: true, priority: 'urgent', importance: 'important', created_at: '2025-10-21T09:00:00Z' },
16+
{ id: 3, is_completed: false, priority: 'urgent', importance: 'not_important', created_at: '2025-10-19T12:00:00Z' },
17+
{ id: 4, is_completed: false, priority: 'not_urgent', importance: 'not_important', created_at: '2025-10-22T08:00:00Z' },
1818
];
1919

2020
const result = filterAndSortTodos(todos, null, false);
2121

2222
assert.deepEqual(result.map((todo) => todo.id), [3, 1, 4, 2]);
2323
});
2424

25-
test('filters by high priority', () => {
25+
test('filters by urgent priority', () => {
2626
const todos = [
27-
{ id: 1, is_completed: false, priority: 'high', created_at: '2025-10-20T10:00:00Z' },
28-
{ id: 2, is_completed: false, priority: 'medium', created_at: '2025-10-20T11:00:00Z' },
29-
{ id: 3, is_completed: false, priority: 'urgent', created_at: '2025-10-20T12:00:00Z' },
27+
{ id: 1, is_completed: false, priority: 'urgent', importance: 'important', created_at: '2025-10-20T10:00:00Z' },
28+
{ id: 2, is_completed: false, priority: 'not_urgent', importance: 'important', created_at: '2025-10-20T11:00:00Z' },
29+
{ id: 3, is_completed: false, priority: 'urgent', importance: 'not_important', created_at: '2025-10-20T12:00:00Z' },
3030
];
3131

32-
const result = filterAndSortTodos(todos, 'high_priority', false);
32+
const result = filterAndSortTodos(todos, 'urgent', false);
3333

34-
assert.deepEqual(result.map((todo) => todo.id), [3, 1]);
34+
assert.deepEqual(result.map((todo) => todo.id), [1, 3]);
3535
});
3636

37-
test('filters by low priority', () => {
37+
test('filters by not urgent priority', () => {
3838
const todos = [
39-
{ id: 1, is_completed: false, priority: 'high', created_at: '2025-10-20T10:00:00Z' },
40-
{ id: 2, is_completed: false, priority: 'medium', created_at: '2025-10-20T11:00:00Z' },
41-
{ id: 3, is_completed: false, priority: 'low', created_at: '2025-10-20T12:00:00Z' },
39+
{ id: 1, is_completed: false, priority: 'urgent', importance: 'important', created_at: '2025-10-20T10:00:00Z' },
40+
{ id: 2, is_completed: false, priority: 'not_urgent', importance: 'important', created_at: '2025-10-20T11:00:00Z' },
41+
{ id: 3, is_completed: false, priority: 'not_urgent', importance: 'not_important', created_at: '2025-10-20T12:00:00Z' },
4242
];
4343

44-
const result = filterAndSortTodos(todos, 'low_priority', false);
44+
const result = filterAndSortTodos(todos, 'not_urgent', false);
4545

4646
assert.deepEqual(result.map((todo) => todo.id), [2, 3]);
4747
});
4848

49+
test('filters by important importance', () => {
50+
const todos = [
51+
{ id: 1, is_completed: false, priority: 'urgent', importance: 'important', created_at: '2025-10-20T10:00:00Z' },
52+
{ id: 2, is_completed: false, priority: 'not_urgent', importance: 'not_important', created_at: '2025-10-20T11:00:00Z' },
53+
{ id: 3, is_completed: false, priority: 'not_urgent', importance: 'important', created_at: '2025-10-20T12:00:00Z' },
54+
];
55+
56+
const result = filterAndSortTodos(todos, 'important', false);
57+
58+
assert.deepEqual(result.map((todo) => todo.id), [1, 3]);
59+
});
60+
61+
test('filters by not important importance', () => {
62+
const todos = [
63+
{ id: 1, is_completed: false, priority: 'urgent', importance: 'not_important', created_at: '2025-10-20T10:00:00Z' },
64+
{ id: 2, is_completed: false, priority: 'not_urgent', importance: 'not_important', created_at: '2025-10-20T11:00:00Z' },
65+
{ id: 3, is_completed: false, priority: 'urgent', importance: 'important', created_at: '2025-10-20T12:00:00Z' },
66+
];
67+
68+
const result = filterAndSortTodos(todos, 'not_important', false);
69+
70+
assert.deepEqual(result.map((todo) => todo.id), [1, 2]);
71+
});
72+
4973
test('sorts notes by created_at descending', () => {
5074
const notes = [
5175
{ id: 1, created_at: '2025-10-20T10:00:00Z' },

0 commit comments

Comments
 (0)