fix(photos): accept bare-date filter bounds, send T00:00:00 from UI

GET /api/v1/photos rejected ?date_from=2026-04-10 with 422 because
pydantic v2's datetime parser doesn't accept date-only strings. The
frontend has been padding date_to with T23:59:59 forever to make the
upper bound inclusive, but date_from went out as a bare YYYY-MM-DD,
so every date-range filter request 422'd and the grid showed nothing.

Frontend: pad date_from with T00:00:00 the same way date_to gets
T23:59:59 — symmetry, and pydantic v2 accepts the full form.

Backend: change date_from/date_to to Optional[str] and parse with
datetime.fromisoformat in the handler. fromisoformat accepts both
bare dates ('2026-04-10' -> midnight) and full ISO strings, so any
older client that still sends a date-only value continues to work.
Tz-aware values get coerced to naive UTC before binding (matches the
taken_at column's  shape and the same
fix applied to PATCH /photos/{id} earlier today). Bad input returns
400 with a clear message instead of pydantic's 422.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Claudio
2026-05-10 23:16:09 +02:00
parent 7b153f0d28
commit d580796dc8
2 changed files with 39 additions and 12 deletions

View File

@@ -212,11 +212,12 @@ export const useFilterStore = create<FilterStore>((set) => ({
export function filtersToParams(f: FilterState): Record<string, string | number> {
const params: Record<string, string | number> = {}
if (f.q.trim()) params.q = f.q.trim()
if (f.dateFrom) params.date_from = f.dateFrom
// Make the upper bound inclusive end-of-day: the backend compares
// `taken_at <= date_to` as a full datetime, so a bare "2024-01-15"
// would exclude every photo taken after midnight that day — a
// single-day pick (from==to) would then match nothing.
// Both bounds need an explicit time component because pydantic v2
// rejects bare date strings ("2026-04-10") for datetime params with
// 422. The from is start-of-day; the to is inclusive end-of-day so
// a single-day pick (from==to) still matches every photo taken
// that day.
if (f.dateFrom) params.date_from = `${f.dateFrom}T00:00:00`
if (f.dateTo) params.date_to = `${f.dateTo}T23:59:59`
if (f.mediaTypes.length > 0) params.media_type = f.mediaTypes.join(',')
if (f.ratingMin > 0) params.rating_min = f.ratingMin