From 5a81c27665c1d871542dd91075321358d9719f8d Mon Sep 17 00:00:00 2001 From: Leon Morival Date: Mon, 18 May 2026 16:32:28 +0200 Subject: [PATCH] feat: calendar --- app/Http/Controllers/MealPostController.php | 31 +++++++++++ app/Http/Requests/MealPostCalendarRequest.php | 27 ++++++++++ routes/api.php | 1 + tests/Feature/MealPostCalendarTest.php | 53 +++++++++++++++++++ 4 files changed, 112 insertions(+) create mode 100644 app/Http/Requests/MealPostCalendarRequest.php create mode 100644 tests/Feature/MealPostCalendarTest.php diff --git a/app/Http/Controllers/MealPostController.php b/app/Http/Controllers/MealPostController.php index 26355a5..24874a4 100644 --- a/app/Http/Controllers/MealPostController.php +++ b/app/Http/Controllers/MealPostController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers; +use App\Http\Requests\MealPostCalendarRequest; use App\Http\Requests\MealPostsRequest; use App\Http\Requests\MealPostStatsRequest; use App\Http\Resources\MealPostsResource; @@ -65,6 +66,36 @@ class MealPostController extends Controller ]); } + public function calendar(MealPostCalendarRequest $request): JsonResponse + { + $month = $request->validated('month') ?? now()->format('Y-m'); + $referenceDate = CarbonImmutable::createFromFormat('!Y-m', $month); + $startDate = $referenceDate->startOfMonth(); + $endDate = $referenceDate->endOfMonth(); + + $days = MealPosts::query() + ->where('user_id', $request->user()->getKey()) + ->whereBetween('eaten_at', [$startDate->startOfDay(), $endDate->endOfDay()]) + ->selectRaw('DATE(eaten_at) as date, COUNT(*) as meals_count') + ->groupByRaw('DATE(eaten_at)') + ->orderBy('date') + ->get() + ->map(fn (MealPosts $mealPost): array => [ + 'date' => CarbonImmutable::parse((string) $mealPost->date)->toDateString(), + 'mealsCount' => (int) $mealPost->meals_count, + ]) + ->values(); + + return response()->json([ + 'data' => [ + 'month' => $referenceDate->format('Y-m'), + 'startDate' => $startDate->toDateString(), + 'endDate' => $endDate->toDateString(), + 'days' => $days, + ], + ]); + } + public function store(MealPostsRequest $request): JsonResponse { $data = $this->getValidatedDataWithImageUrl($request); diff --git a/app/Http/Requests/MealPostCalendarRequest.php b/app/Http/Requests/MealPostCalendarRequest.php new file mode 100644 index 0000000..9b4165d --- /dev/null +++ b/app/Http/Requests/MealPostCalendarRequest.php @@ -0,0 +1,27 @@ + ['sometimes', 'date_format:Y-m'], + ]; + } + + public function authorize(): bool + { + return true; + } + + public function messages(): array + { + return [ + 'month.date_format' => 'Choisis un mois valide.', + ]; + } +} diff --git a/routes/api.php b/routes/api.php index 8458d45..39453f4 100644 --- a/routes/api.php +++ b/routes/api.php @@ -26,6 +26,7 @@ Route::middleware('auth:sanctum')->group(function (): void { Route::middleware('auth:sanctum')->group(function (): void { Route::get('meal-posts/stats', [MealPostController::class, 'stats']); + Route::get('meal-posts/calendar', [MealPostController::class, 'calendar']); Route::post('meal-posts/analyze-image', [MealImageAnalysisController::class, 'store']); Route::apiResource('meal-posts', MealPostController::class)->except(['update']); Route::put('meal-posts/{mealPost}', [MealPostController::class, 'update'])->name('meal-posts.update'); diff --git a/tests/Feature/MealPostCalendarTest.php b/tests/Feature/MealPostCalendarTest.php new file mode 100644 index 0000000..981561c --- /dev/null +++ b/tests/Feature/MealPostCalendarTest.php @@ -0,0 +1,53 @@ +create(); + $otherUser = User::factory()->create(); + + MealPosts::factory()->for($user, 'user')->create([ + 'eaten_at' => '2026-02-04 08:00:00', + ]); + MealPosts::factory()->for($user, 'user')->create([ + 'eaten_at' => '2026-02-04 20:00:00', + ]); + MealPosts::factory()->for($user, 'user')->create([ + 'eaten_at' => '2026-02-12 12:00:00', + ]); + MealPosts::factory()->for($user, 'user')->create([ + 'eaten_at' => '2026-03-01 12:00:00', + ]); + MealPosts::factory()->for($otherUser, 'user')->create([ + 'eaten_at' => '2026-02-12 12:00:00', + ]); + + Sanctum::actingAs($user); + + $response = $this->getJson('/api/meal-posts/calendar?month=2026-02'); + + $response + ->assertOk() + ->assertJsonPath('data.month', '2026-02') + ->assertJsonPath('data.startDate', '2026-02-01') + ->assertJsonPath('data.endDate', '2026-02-28') + ->assertJsonCount(2, 'data.days') + ->assertJsonPath('data.days.0.date', '2026-02-04') + ->assertJsonPath('data.days.0.mealsCount', 2) + ->assertJsonPath('data.days.1.date', '2026-02-12') + ->assertJsonPath('data.days.1.mealsCount', 1); +}); + +it('validates the requested calendar month', function () { + Sanctum::actingAs(User::factory()->create()); + + $this + ->getJson('/api/meal-posts/calendar?month=02-2026') + ->assertUnprocessable() + ->assertJsonValidationErrors(['month']); +});