Files
F-r-ldraledigplannerare/PLANNED_DAYS_405_TODO.md
T

3.1 KiB
Raw Blame History

TODO — Fix GET /api/planned-days 405 (planned-days router is incomplete)

Status: open · Area: backend · Owner: unassigned · Filed: 2026-06-14

Symptom

Loading the Dashboard and adding a day in the Calendar both log:

"GET /api/planned-days?child_id=3b9b6dad-... HTTP/1.1" 405 Method Not Allowed

Adding a day works (POST /api/planned-days → 201), but the follow-up refresh (GET /api/planned-days?child_id=…) is rejected, so the calendar/dashboard never show planned days.

Root cause

backend/app/routers/planned_days.py defines POST "" but no GET "". FastAPI returns 405 (not 404) because the path exists for another verb. The list-by-child endpoint was never finished — the GET /range handler (lines ~65–78) is a dead stub (pass body, = None defaults on its service/_current_user deps) and /range also collides with GET /{planned_leave_day_id}.

Same router has a second gap: api.updatePlannedDay issues PUT /api/planned-days/{id} (used by Calendar edit, frontend/src/pages/Calendar.tsx:99), but there is no PUT handler and no PlanningService.update. Editing an existing day will 405 once adding works.

Fix

In backend/app/services/planning_service.py:

  1. Add list_by_child:
    async def list_by_child(self, child_id: UUID) -> list[PlannedLeaveDay]:
        return await self._repo.list(child_id=child_id)
    
    (The SQLite list() already filters on child_id alone.)
  2. Add update(self, planned_leave_day_id: UUID, day_type: str | None, is_dubbeldag: bool | None) -> PlannedLeaveDay — load via get_by_id (raise NotFoundError if missing), apply provided fields, call repo.update.

In backend/app/routers/planned_days.py:

  1. Add the list route before GET /{planned_leave_day_id}:
    @router.get("", response_model=list[PlannedLeaveDayResponse])
    async def list_planned_days(
        service: PlanningServiceDep,
        _current_user: CurrentUserDep,
        child_id: UUID = Query(...),
    ) -> list[PlannedLeaveDayResponse]:
        return [_to_response(d) for d in await service.list_by_child(child_id)]
    
  2. Delete the dead GET /range stub (lines ~65–78).
  3. Add the PUT /{planned_leave_day_id} handler using the existing PlannedLeaveDayUpdate model → service.update(...) → _to_response.

Route ordering matters: declare "", /workspace/..., then /{planned_leave_day_id} last, so the UUID catch-all doesn't swallow the others.

Verification

From backend/:

uv run ruff check . && uv run ruff format --check . && uv run pytest

Add tests:

  • GET /api/planned-days?child_id=… → 200 with the seeded rows for that child.
  • PUT /api/planned-days/{id} → 200, fields updated; unknown id → 404.

Manual: add a vacation day in the Calendar → POST 201 then GET 200 and the list refreshes; edit an existing day → PUT succeeds.

After code changes: graphify update .

The 405 only blocks the planned-days list. The dashboard's empty metrics / parental-leave breakdown is a separate snake_case↔camelCase issue — see DASHBOARD_EMPTY_DATA_TODO.md.