feat: enriched edit request system with side-by-side snapshots, calendar preloading, and admin review UI

Backend:
- Add enriched response types (EditSnapshot, EnrichedEditRequest) with original vs proposed snapshots
- Add 4 new GET endpoints for viewing edit requests (user and admin scoped)
- Remove github.com/lib/pq dependency — use native PostgreSQL array scanning
- Clean up edit requests, time blockers, and notifications on booking cancellation
- Validate exceptional closed hours on admin approve (409 Conflict)
- Notification upsert on edit request replace (no duplicate admin notifications)

Frontend:
- New user EditRequestModal with time/services/both modes and lunch protection
- New admin EditRequestModal with side-by-side diff (date/time, services, notes)
- Integrate edit requests into PendingApprovals card and notifications page
- Preload 3 months of availability to prevent calendar snap-back
- Apply lunch protection to isDateUnavailable in BookingFlow and BookingCreateModal
- Fix accessibility: card list items use <button> instead of <div>

Dev & Docs:
- Seed edit requests in local-dev-2.sh
- Update all Obsidian manuals with enriched edit request documentation
- 42 new tests (438/441 passing)
This commit is contained in:
2026-05-26 11:59:07 +01:00
parent 3ccc017716
commit 8574bf2221
19 changed files with 5270 additions and 599 deletions
+4 -1
View File
@@ -227,6 +227,8 @@ func main() {
r.Delete("/bookings/{id}", bookings.DeleteBookingHandler)
r.Post("/bookings/{id}/edit-request", bookings.RequestEditHandler)
r.Delete("/bookings/{id}/edit-request", bookings.DeleteEditRequestHandler)
r.Get("/bookings/{id}/edit-request", bookings.GetMyEditRequestHandler)
r.Get("/bookings/edit-requests", bookings.GetMyEditRequestsHandler)
// User payment routes
r.Post("/bookings/{id}/payment", payments.CreateBookingPayment)
@@ -262,7 +264,8 @@ func main() {
r.Post("/{id}/cancel", bookings.AdminCancelBookingHandler)
r.Post("/reserve", bookings.AdminReserveSlotHandler)
// Edit request endpoints
r.Get("/{id}/edit-requests", bookings.AdminListEditRequestsHandler)
r.Get("/edit-requests", bookings.AdminListAllEditRequestsHandler)
r.Get("/{id}/edit-request", bookings.AdminGetBookingEditRequestHandler)
r.Post("/{id}/edit-requests/{request_id}/approve", bookings.AdminApproveEditRequestHandler)
r.Post("/{id}/edit-requests/{request_id}/deny", bookings.AdminRejectEditRequestHandler)
})