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
+77 -1
View File
@@ -1007,6 +1007,81 @@ if api_post "$BASE_URL/scheduling/exceptional-groups" "$XMAS_BREAK" "Christmas
echo "${C_GREEN}✅ Created $sched_success/3 Exceptional Schedule Groups${C_RESET}"
# ===========================================================================
# 7b. EDIT REQUESTS (for testing the edit request UI)
# ===========================================================================
echo -e "\n${C_BLUE}✏️ Creating Edit Requests...${C_RESET}"
edit_req_count=0
# Create edit requests via the user API for upcoming confirmed bookings
# Use a wider time window to find more bookings (any future confirmed booking)
CONFIRMED_BOOKINGS=$(docker exec postgres psql -U myuser -d mydb -tAc \
"SELECT b.id, b.user_id, b.start_time FROM bookings b
WHERE b.status = 'confirmed' AND b.start_time > NOW()
ORDER BY b.start_time ASC LIMIT 8;" 2>/dev/null)
if [[ -n "$CONFIRMED_BOOKINGS" ]]; then
req_idx=0
while IFS='|' read -r booking_id user_id start_time; do
[[ -z "$booking_id" ]] && continue
# Get user token
user_email=$(docker exec postgres psql -U myuser -d mydb -tAc \
"SELECT email FROM users WHERE id = '$user_id'" 2>/dev/null | tr -d '\r\t ')
[[ -z "$user_email" ]] && continue
user_tok=$(login "$user_email" "password")
[[ -z "$user_tok" ]] && continue
# Alternate between time-only and service+time requests
if (( req_idx % 3 == 0 )); then
# Time-only request
new_time=$(TZ=Europe/London date -d "$start_time +2 hours" +"%Y-%m-%dT%H:%M:%S%:z" 2>/dev/null)
[[ -z "$new_time" ]] && continue
resp=$(curl -s -w "\n%{http_code}" -X POST \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $user_tok" \
-d "{\"new_start_time\":\"$new_time\",\"notes\":\"Would like to move this appointment 2 hours later please\"}" \
"$BASE_URL/bookings/$booking_id/edit-request")
elif (( req_idx % 3 == 1 )); then
# Service change request (add nail art)
resp=$(curl -s -w "\n%{http_code}" -X POST \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $user_tok" \
-d "{\"new_services\":[\"$(get_svc 0)\",\"$(get_svc 5)\"],\"notes\":\"Would like to add nail art to my appointment\"}" \
"$BASE_URL/bookings/$booking_id/edit-request")
else
# Both time and services
new_time=$(TZ=Europe/London date -d "$start_time -1 hours" +"%Y-%m-%dT%H:%M:%S%:z" 2>/dev/null)
[[ -z "$new_time" ]] && continue
resp=$(curl -s -w "\n%{http_code}" -X POST \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $user_tok" \
-d "{\"new_start_time\":\"$new_time\",\"new_services\":[\"$(get_svc 1)\"],\"notes\":\"Need to reschedule earlier and switch to gel\"}" \
"$BASE_URL/bookings/$booking_id/edit-request")
fi
code=$(echo "$resp" | tail -n1)
if [[ "$code" =~ ^2 ]]; then
edit_req_count=$((edit_req_count+1))
fi
req_idx=$((req_idx+1))
done <<< "$CONFIRMED_BOOKINGS"
fi
echo "${C_GREEN}✅ Created $edit_req_count Edit Requests${C_RESET}"
# ===========================================================================
# 7c. MORE TIME BLOCKERS (for variety)
# ===========================================================================
echo -e "\n${C_BLUE}🚫 Creating Additional Time Blockers...${C_RESET}"
extra_blockers=0
tb "$(format_london_time "$(open_day "$(TZ=Europe/London date -d "$TODAY +3 days" +%Y-%m-%d)")" "$SLOT_B")" 90 "Equipment maintenance"
tb "$(format_london_time "$(open_day "$(TZ=Europe/London date -d "$TODAY +10 days" +%Y-%m-%d)")" "$SLOT_C")" 60 "Training session"
tb "$(format_london_time "$(open_day "$(TZ=Europe/London date -d "$TODAY +14 days" +%Y-%m-%d)")" "09:00:00")" 60 "Opening delay"
tb "$(format_london_time "$(open_day "$(TZ=Europe/London date -d "$TODAY +5 days" +%Y-%m-%d)")" "$SLOT_D")" 45 "Supplier visit"
tb "$(format_london_time "$(open_day "$(TZ=Europe/London date -d "$TODAY +8 days" +%Y-%m-%d)")" "$SLOT_A")" 120 "Deep clean — morning closed"
echo "${C_GREEN}✅ Created $extra_blockers Additional Time Blockers${C_RESET}"
# ===========================================================================
# SUMMARY
# ===========================================================================
@@ -1032,7 +1107,8 @@ echo -e " Confirmed : $confirmed_count | Still pending: $skipped_cou
echo -e " Bookings — guest : $count_guest"
echo -e " Bookings — w/ notes: $USER_NOTE_COUNT (pending notifications)"
echo -e " Payments : $payment_count completed bookings"
echo -e " Time blockers : $count_blockers"
echo -e " Time blockers : $((count_blockers + extra_blockers))"
echo -e " Edit requests : $edit_req_count"
echo -e " Schedule groups : $sched_success/3"
echo ""
echo -e " Quick login creds (all pass: ${C_YELLOW}password${C_RESET})"