Refactor patch test system and add booking edit requests
- Replace patch_test_duration_hours on services with separate patch_tests table - Add user_patch_tests table to track user patch test records - Add booking edit request system: users can request time changes - Add admin handlers to list, approve, and reject edit requests - Add validation to prevent editing completed/cancelled bookings - Add overlap and closed-day checks for booking edits
This commit is contained in:
@@ -1350,6 +1350,92 @@ func EditBookingHandler(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
// Check if user owns this booking and get current status
|
||||
var currentStatus string
|
||||
err := db.DB.QueryRow(r.Context(), "SELECT status FROM bookings WHERE id = $1 AND user_id = $2", bookingID, userID).Scan(¤tStatus)
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
http.Error(w, "Booking not found or access denied", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
log.Printf("Failed to get booking %s: %v", bookingID, err)
|
||||
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// Users cannot edit completed or cancelled bookings
|
||||
if currentStatus == "completed" || currentStatus == "client_cancelled" || currentStatus == "we_cancelled" {
|
||||
http.Error(w, "Cannot edit a completed or cancelled booking", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
// Get booking duration for overlap check
|
||||
var durationMinutes int
|
||||
err = db.DB.QueryRow(r.Context(), `
|
||||
SELECT COALESCE(SUM(COALESCE(bs.override_duration_minutes, s.duration_minutes)), 60)
|
||||
FROM booking_services bs
|
||||
JOIN services s ON bs.service_id = s.id
|
||||
WHERE bs.booking_id = $1
|
||||
`, bookingID).Scan(&durationMinutes)
|
||||
if err != nil {
|
||||
log.Printf("Failed to get booking duration %s: %v", bookingID, err)
|
||||
durationMinutes = 60
|
||||
}
|
||||
|
||||
// Check for overlapping bookings (user is blocked if overlap exists)
|
||||
var overlapCount int
|
||||
newEndTime := req.StartTime.Add(time.Duration(durationMinutes) * time.Minute)
|
||||
err = db.DB.QueryRow(r.Context(), `
|
||||
SELECT COUNT(*) FROM bookings
|
||||
WHERE id != $1
|
||||
AND status NOT IN ('completed', 'client_cancelled', 'we_cancelled')
|
||||
AND start_time < $3
|
||||
AND start_time + (INTERVAL '1 minute' * (
|
||||
SELECT COALESCE(SUM(COALESCE(bs.override_duration_minutes, s.duration_minutes)), 60)
|
||||
FROM booking_services bs
|
||||
JOIN services s ON bs.service_id = s.id
|
||||
WHERE bs.booking_id = bookings.id
|
||||
)) > $2
|
||||
`, bookingID, req.StartTime, newEndTime).Scan(&overlapCount)
|
||||
if err != nil {
|
||||
log.Printf("Failed to check overlap %s: %v", bookingID, err)
|
||||
}
|
||||
if overlapCount > 0 {
|
||||
http.Error(w, "This time slot overlaps with an existing booking", http.StatusConflict)
|
||||
return
|
||||
}
|
||||
|
||||
// Check if salon is closed (exceptional hours) - user is blocked on closed days
|
||||
weekday := int(req.StartTime.Weekday())
|
||||
bookingTime := req.StartTime.Format("15:04:05")
|
||||
daysToMonday := weekday
|
||||
if daysToMonday == 0 {
|
||||
daysToMonday = 7
|
||||
}
|
||||
weekStart := req.StartTime.AddDate(0, 0, -daysToMonday+1).Truncate(24 * time.Hour)
|
||||
|
||||
// Check if salon is closed (exceptional hours)
|
||||
var isClosed bool
|
||||
err = db.DB.QueryRow(r.Context(), `
|
||||
SELECT EXISTS (
|
||||
SELECT 1 FROM exceptional_working_hours ewh
|
||||
JOIN exceptional_group_applications ega ON ewh.group_id = ega.group_id
|
||||
WHERE ega.week_start = $1
|
||||
AND ewh.weekday = $2
|
||||
AND ewh.is_open = false
|
||||
AND ewh.start_time <= $3
|
||||
AND ewh.end_time >= $3
|
||||
)
|
||||
`, weekStart, weekday, bookingTime).Scan(&isClosed)
|
||||
if err != nil {
|
||||
log.Printf("Failed to check exceptional hours: %v", err)
|
||||
}
|
||||
|
||||
if isClosed {
|
||||
http.Error(w, "Cannot book on a closed day", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// Update booking start time (only for user's own bookings)
|
||||
query := `
|
||||
UPDATE bookings
|
||||
@@ -1360,7 +1446,7 @@ func EditBookingHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
var booking Booking
|
||||
booking.User = &UserSummary{}
|
||||
err := db.DB.QueryRow(r.Context(),
|
||||
err = db.DB.QueryRow(r.Context(),
|
||||
query,
|
||||
req.StartTime,
|
||||
bookingID,
|
||||
|
||||
Reference in New Issue
Block a user