From 93d2f4ebd660e0139ddb3467847eca4bedc3e5bd Mon Sep 17 00:00:00 2001 From: Yisroel Baum Date: Tue, 18 Aug 2026 00:16:57 +0300 Subject: [PATCH] show overdue assignments today --- .../Http/Controllers/ScheduleController.php | 2 ++ .../Schedule/EloquentScheduleRepository.php | 11 +++--- backend/app/Schedule/ScheduleRepository.php | 2 +- .../ListAssignmentsForDate.php | 9 ++--- .../tests/Fakes/FakeScheduleRepository.php | 36 +++++++++++++++++-- frontend/website/src/stores/schedules.ts | 1 + frontend/website/src/views/DashboardView.vue | 29 ++++++++++++++- 7 files changed, 77 insertions(+), 13 deletions(-) diff --git a/backend/app/Http/Controllers/ScheduleController.php b/backend/app/Http/Controllers/ScheduleController.php index f80461a..33a6316 100644 --- a/backend/app/Http/Controllers/ScheduleController.php +++ b/backend/app/Http/Controllers/ScheduleController.php @@ -249,6 +249,8 @@ class ScheduleController extends Controller return [ 'id' => $assignment->getId(), + 'scheduledDate' => $assignment->getScheduledDate() + ->format('Y-m-d'), 'schedule' => [ 'id' => $assignmentForDate->getScheduleId(), 'set' => [ diff --git a/backend/app/Schedule/EloquentScheduleRepository.php b/backend/app/Schedule/EloquentScheduleRepository.php index 214e8a5..8576591 100644 --- a/backend/app/Schedule/EloquentScheduleRepository.php +++ b/backend/app/Schedule/EloquentScheduleRepository.php @@ -63,7 +63,7 @@ class EloquentScheduleRepository implements ScheduleRepository return $schedules; } - public function findAssignmentsForUserOnDate( + public function findIncompleteAssignmentsForUserDueOnOrBefore( User $user, DateTimeImmutable $date, ): array { @@ -76,11 +76,14 @@ class EloquentScheduleRepository implements ScheduleRepository 'schedule_assignments.schedule_id', ) ->where('schedules.user_id', $user->getId()) - ->where('schedule_assignments.scheduled_date', $date->format( - 'Y-m-d', - )) + ->where( + 'schedule_assignments.scheduled_date', + '<=', + $date->format('Y-m-d'), + ) ->whereNull('schedule_assignments.completed_at') ->with('schedule') + ->orderBy('schedule_assignments.scheduled_date') ->orderByDesc('schedules.id') ->orderBy('schedule_assignments.position') ->orderBy('schedule_assignments.id') diff --git a/backend/app/Schedule/ScheduleRepository.php b/backend/app/Schedule/ScheduleRepository.php index 3222311..20b5f9f 100644 --- a/backend/app/Schedule/ScheduleRepository.php +++ b/backend/app/Schedule/ScheduleRepository.php @@ -19,7 +19,7 @@ interface ScheduleRepository /** * @return list */ - public function findAssignmentsForUserOnDate( + public function findIncompleteAssignmentsForUserDueOnOrBefore( User $user, DateTimeImmutable $date, ): array; diff --git a/backend/app/Schedule/UseCases/ListAssignmentsForDate/ListAssignmentsForDate.php b/backend/app/Schedule/UseCases/ListAssignmentsForDate/ListAssignmentsForDate.php index 9e3017d..23f388d 100644 --- a/backend/app/Schedule/UseCases/ListAssignmentsForDate/ListAssignmentsForDate.php +++ b/backend/app/Schedule/UseCases/ListAssignmentsForDate/ListAssignmentsForDate.php @@ -22,10 +22,11 @@ class ListAssignmentsForDate { $date = $this->parseDate($request->date); - return $this->scheduleRepository->findAssignmentsForUserOnDate( - $request->user, - $date, - ); + return $this->scheduleRepository + ->findIncompleteAssignmentsForUserDueOnOrBefore( + $request->user, + $date, + ); } /** diff --git a/backend/tests/Fakes/FakeScheduleRepository.php b/backend/tests/Fakes/FakeScheduleRepository.php index e2f35bc..e12d126 100644 --- a/backend/tests/Fakes/FakeScheduleRepository.php +++ b/backend/tests/Fakes/FakeScheduleRepository.php @@ -78,7 +78,7 @@ class FakeScheduleRepository implements ScheduleRepository }, array_values($schedules)); } - public function findAssignmentsForUserOnDate( + public function findIncompleteAssignmentsForUserDueOnOrBefore( User $user, DateTimeImmutable $date, ): array { @@ -86,8 +86,7 @@ class FakeScheduleRepository implements ScheduleRepository foreach ($this->findAllForUser($user) as $schedule) { foreach ($schedule->getAssignments() as $assignment) { - if ($assignment->getScheduledDate()->format('Y-m-d') - !== $date->format('Y-m-d') + if ($assignment->getScheduledDate() > $date || $assignment->getCompletedAt() !== null ) { continue; @@ -101,6 +100,37 @@ class FakeScheduleRepository implements ScheduleRepository } } + usort( + $assignments, + function ( + AssignmentForDate $first, + AssignmentForDate $second, + ): int { + $firstAssignment = $first->getAssignment(); + $secondAssignment = $second->getAssignment(); + $dateComparison = $firstAssignment->getScheduledDate() + <=> $secondAssignment->getScheduledDate(); + if ($dateComparison !== 0) { + return $dateComparison; + } + + $scheduleComparison = $second->getScheduleId() + <=> $first->getScheduleId(); + if ($scheduleComparison !== 0) { + return $scheduleComparison; + } + + $positionComparison = $firstAssignment->getPosition() + <=> $secondAssignment->getPosition(); + if ($positionComparison !== 0) { + return $positionComparison; + } + + return $firstAssignment->getId() + <=> $secondAssignment->getId(); + }, + ); + return $assignments; } diff --git a/frontend/website/src/stores/schedules.ts b/frontend/website/src/stores/schedules.ts index 2471d66..825bbfb 100644 --- a/frontend/website/src/stores/schedules.ts +++ b/frontend/website/src/stores/schedules.ts @@ -31,6 +31,7 @@ const scheduleAssignmentSchema = assignmentIdentitySchema.extend({ }) export const assignmentForDateSchema = assignmentIdentitySchema.extend({ + scheduledDate: isoDateSchema, schedule: z.object({ id: z.number().int().positive(), set: z.object({ diff --git a/frontend/website/src/views/DashboardView.vue b/frontend/website/src/views/DashboardView.vue index 822da86..7d1eeaf 100644 --- a/frontend/website/src/views/DashboardView.vue +++ b/frontend/website/src/views/DashboardView.vue @@ -36,6 +36,14 @@ function formatDate(value: string): string { }).format(new Date(`${value}T00:00:00Z`)) } +function isOverdue(scheduledDate: string): boolean { + return scheduledDate < todayDate.value +} + +function overdueLabel(scheduledDate: string): string { + return `Overdue ยท Due ${formatDate(scheduledDate)}` +} + async function completeAssignment(assignmentId: number): Promise { await schedulesStore.setAssignmentCompleted(assignmentId, true) } @@ -76,7 +84,7 @@ async function completeAssignment(assignmentId: number): Promise {

- Nothing is assigned for today. + Nothing is due today.

    @@ -97,6 +105,12 @@ async function completeAssignment(assignmentId: number): Promise {

    {{ assignment.schedule.set.name }}

    {{ assignment.element.kind }} +

    {{ assignment.element.path.join(' / ') }}

    @@ -390,6 +404,19 @@ async function completeAssignment(assignmentId: number): Promise { text-transform: none; } +.today-assignment__overdue { + display: inline-flex; + width: fit-content; + margin-top: 0.85rem; + padding: 0.3rem 0.55rem; + border-radius: 999px; + color: #923f31; + background: #f7e5dc; + font-size: 0.68rem; + font-weight: 800; + letter-spacing: 0.025em; +} + .today-assignment__path { margin: 1.75rem 0 0; color: #183029;