From f8b22da9de78a1afd6be1eca4c990735a544bf34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christopher=20M=C3=BChl?= Date: Thu, 9 Apr 2026 21:44:19 +0200 Subject: [PATCH] feat(08-02): add mode dispatch, filter params, and cascade endpoints --- Controller/HeatmapController.php | 53 ++++++++++++++++++++++++++------ Service/HeatmapService.php | 8 ++++- 2 files changed, 51 insertions(+), 10 deletions(-) diff --git a/Controller/HeatmapController.php b/Controller/HeatmapController.php index 23de4b3..94f8aea 100644 --- a/Controller/HeatmapController.php +++ b/Controller/HeatmapController.php @@ -21,16 +21,51 @@ class HeatmapController extends AbstractController $end = new \DateTimeImmutable('today'); $begin = $end->modify('-1 year'); + $mode = $request->query->getString('mode', 'daily'); + $projectId = $request->query->getInt('project') ?: null; + $customerId = $request->query->getInt('customer') ?: null; + $activityId = $request->query->getInt('activity') ?: null; + + $range = ['begin' => $begin->format('Y-m-d'), 'end' => $end->format('Y-m-d')]; + + return match ($mode) { + 'hourly' => new JsonResponse([ + 'hours' => $service->getHourlyAggregation($user, $begin, $end, $projectId, $customerId, $activityId), + 'range' => $range, + ]), + 'dayhour' => new JsonResponse([ + 'matrix' => $service->getDayHourAggregation($user, $begin, $end, $projectId, $customerId, $activityId, $user->getFirstDayOfWeek()), + 'range' => $range, + ]), + default => new JsonResponse([ + 'days' => $service->getDailyAggregation($user, $begin, $end, $projectId, $customerId, $activityId), + 'range' => $range, + ]), + }; + } + + #[Route(path: '/customers', name: 'heatmap_customers', methods: ['GET'])] + #[IsGranted('view_own_timesheet')] + public function customers(HeatmapService $service): JsonResponse + { + return new JsonResponse($service->getUserCustomers($this->getUser())); + } + + #[Route(path: '/projects', name: 'heatmap_projects', methods: ['GET'])] + #[IsGranted('view_own_timesheet')] + public function projects(Request $request, HeatmapService $service): JsonResponse + { + $customerId = $request->query->getInt('customer') ?: null; + + return new JsonResponse($service->getUserProjects($this->getUser(), $customerId)); + } + + #[Route(path: '/activities', name: 'heatmap_activities', methods: ['GET'])] + #[IsGranted('view_own_timesheet')] + public function activities(Request $request, HeatmapService $service): JsonResponse + { $projectId = $request->query->getInt('project') ?: null; - $days = $service->getDailyAggregation($user, $begin, $end, $projectId); - - return new JsonResponse([ - 'days' => $days, - 'range' => [ - 'begin' => $begin->format('Y-m-d'), - 'end' => $end->format('Y-m-d'), - ], - ]); + return new JsonResponse($service->getUserActivities($this->getUser(), $projectId)); } } diff --git a/Service/HeatmapService.php b/Service/HeatmapService.php index f2d505f..9d553e2 100644 --- a/Service/HeatmapService.php +++ b/Service/HeatmapService.php @@ -167,7 +167,7 @@ class HeatmapService /** * @return array */ - public function getUserProjects(User $user): array + public function getUserProjects(User $user, ?int $customerId = null): array { $qb = $this->repository->createQueryBuilder('t'); $qb->select('DISTINCT IDENTITY(t.project) as projectId, p.name') @@ -177,6 +177,12 @@ class HeatmapService ->setParameter('user', $user) ->orderBy('p.name', 'ASC'); + if ($customerId !== null) { + $qb->join('p.customer', 'c') + ->andWhere($qb->expr()->eq('c.id', ':customer')) + ->setParameter('customer', $customerId); + } + return array_map(fn(array $row) => [ 'id' => (int) $row['projectId'], 'name' => $row['name'],