partial edit fixes
This commit is contained in:
@@ -11,6 +11,7 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
@@ -372,12 +373,8 @@ func AdminEditBookingHandler(w http.ResponseWriter, r *http.Request) {
|
||||
// Clear any pending edit requests for this booking (admin edit takes priority)
|
||||
_, err = db.DB.Exec(r.Context(), `
|
||||
DELETE FROM booking_edit_requests
|
||||
WHERE booking_id = $1 AND status = 'pending'
|
||||
WHERE booking_id = $1
|
||||
`, bookingID)
|
||||
if err != nil {
|
||||
log.Printf("Failed to clear edit requests for booking %s: %v", bookingID, err)
|
||||
// Don't fail the request, just log the error
|
||||
}
|
||||
|
||||
// Return warnings if any
|
||||
if len(warnings) > 0 {
|
||||
@@ -685,20 +682,72 @@ func AdminCreateBookingForUserHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// BookingEditRequest represents a user's request to edit a booking
|
||||
type BookingEditRequest struct {
|
||||
ID string `json:"id"`
|
||||
BookingID string `json:"booking_id"`
|
||||
RequestedStartTime time.Time `json:"requested_start_time"`
|
||||
Status string `json:"status"`
|
||||
AdminNotes *string `json:"admin_notes,omitempty"`
|
||||
RequestedBy string `json:"requested_by"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
ID string `json:"id"`
|
||||
BookingID string `json:"booking_id"`
|
||||
RequestedBy string `json:"requested_by"`
|
||||
NewStartTime *time.Time `json:"new_start_time,omitempty"`
|
||||
NewServices []string `json:"new_services"`
|
||||
Notes *string `json:"notes,omitempty"`
|
||||
HasOverrides bool `json:"has_overrides"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
// Joined fields
|
||||
Booking *Booking `json:"booking,omitempty"`
|
||||
Booking *Booking `json:"booking,omitempty"`
|
||||
User *UserSummary `json:"user,omitempty"`
|
||||
}
|
||||
// DeleteEditRequestHandler allows a user to delete/cancel their pending edit request
|
||||
func DeleteEditRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
bookingID := chi.URLParam(r, "id")
|
||||
if bookingID == "" || !validators.IsValidID(bookingID) {
|
||||
http.Error(w, "Booking not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
// RequestEditHandler allows a user to request an edit to their booking's start time
|
||||
userID, ok := r.Context().Value(mw.UserIDKey).(string)
|
||||
if !ok || userID == "" {
|
||||
http.Error(w, "Authentication required", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
// Verify user owns this booking
|
||||
var ownerID string
|
||||
err := db.DB.QueryRow(r.Context(), "SELECT user_id FROM bookings WHERE id = $1", bookingID).Scan(&ownerID)
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
http.Error(w, "Booking not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
log.Printf("Failed to get booking %s: %v", bookingID, err)
|
||||
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if ownerID != userID {
|
||||
http.Error(w, "Access denied", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
// Delete the edit request for this booking
|
||||
res, err := db.DB.Exec(r.Context(), `
|
||||
DELETE FROM booking_edit_requests
|
||||
WHERE booking_id = $1 AND requested_by = $2
|
||||
`, bookingID, userID)
|
||||
if err != nil {
|
||||
log.Printf("Failed to delete edit request for booking %s: %v", bookingID, err)
|
||||
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
rowsAffected := res.RowsAffected()
|
||||
if rowsAffected == 0 {
|
||||
http.Error(w, "No edit request found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
|
||||
|
||||
// RequestEditHandler allows a user to request an edit to their booking
|
||||
func RequestEditHandler(w http.ResponseWriter, r *http.Request) {
|
||||
bookingID := chi.URLParam(r, "id")
|
||||
if bookingID == "" || !validators.IsValidID(bookingID) {
|
||||
@@ -713,13 +762,21 @@ func RequestEditHandler(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
var req struct {
|
||||
RequestedStartTime time.Time `json:"requested_start_time" validate:"required"`
|
||||
NewStartTime *time.Time `json:"new_start_time,omitempty"`
|
||||
NewServices []string `json:"new_services"`
|
||||
Notes *string `json:"notes,omitempty"`
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, "Invalid request body", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// Validate: at least one of new_start_time, new_services, or notes must be provided
|
||||
if req.NewStartTime == nil && len(req.NewServices) == 0 && req.Notes == nil {
|
||||
http.Error(w, "At least one of new_start_time, new_services, or notes is required", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// Verify user owns this booking
|
||||
var ownerID string
|
||||
err := db.DB.QueryRow(r.Context(), "SELECT user_id FROM bookings WHERE id = $1", bookingID).Scan(&ownerID)
|
||||
@@ -751,20 +808,56 @@ func RequestEditHandler(w http.ResponseWriter, r *http.Request) {
|
||||
http.Error(w, "Cannot edit a completed or cancelled booking", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
if len(req.NewServices) > 0 {
|
||||
var overrideCount int
|
||||
err = db.DB.QueryRow(r.Context(), `
|
||||
SELECT COUNT(*) FROM booking_services
|
||||
WHERE booking_id = $1 AND (override_price IS NOT NULL OR override_duration_minutes IS NOT NULL)
|
||||
`, bookingID).Scan(&overrideCount)
|
||||
if err != nil {
|
||||
log.Printf("Failed to check overrides for booking %s: %v", bookingID, err)
|
||||
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if overrideCount > 0 {
|
||||
http.Error(w, "Cannot change services on a booking that has overrides. Please contact the salon.", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Create edit request
|
||||
tx, err := db.DB.Begin(r.Context())
|
||||
if err != nil {
|
||||
log.Printf("Failed to start transaction: %v", err)
|
||||
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer tx.Rollback(r.Context())
|
||||
|
||||
// Delete any existing edit request for this booking (upsert behavior)
|
||||
_, err = tx.Exec(r.Context(), `
|
||||
DELETE FROM booking_edit_requests
|
||||
WHERE booking_id = $1 AND requested_by = $2
|
||||
`, bookingID, userID)
|
||||
if err != nil {
|
||||
log.Printf("Failed to delete existing edit request for booking %s: %v", bookingID, err)
|
||||
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// Create edit request - has_overrides is false since user can't override
|
||||
var editReq BookingEditRequest
|
||||
err = db.DB.QueryRow(r.Context(), `
|
||||
INSERT INTO booking_edit_requests (booking_id, requested_start_time, requested_by)
|
||||
VALUES ($1, $2, $3)
|
||||
RETURNING id, booking_id, requested_start_time, status, requested_by, created_at, updated_at
|
||||
`, bookingID, req.RequestedStartTime, userID).Scan(
|
||||
err = tx.QueryRow(r.Context(), `
|
||||
INSERT INTO booking_edit_requests (booking_id, requested_by, new_start_time, new_services, notes, has_overrides)
|
||||
VALUES ($1, $2, $3, $4, $5, $6)
|
||||
RETURNING id, booking_id, requested_by, new_start_time, new_services, notes, has_overrides, updated_at
|
||||
`, bookingID, userID, req.NewStartTime, req.NewServices, req.Notes, false).Scan(
|
||||
&editReq.ID,
|
||||
&editReq.BookingID,
|
||||
&editReq.RequestedStartTime,
|
||||
&editReq.Status,
|
||||
&editReq.RequestedBy,
|
||||
&editReq.CreatedAt,
|
||||
&editReq.NewStartTime,
|
||||
&editReq.NewServices,
|
||||
&editReq.Notes,
|
||||
&editReq.HasOverrides,
|
||||
&editReq.UpdatedAt,
|
||||
)
|
||||
if err != nil {
|
||||
@@ -773,39 +866,67 @@ func RequestEditHandler(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
// Create admin notification with reason 'edit_request'
|
||||
_, err = tx.Exec(r.Context(), `
|
||||
INSERT INTO admin_notifications (reason, booking_id, user_id)
|
||||
VALUES ($1, $2, $3)
|
||||
`, "edit_request", bookingID, userID)
|
||||
if err != nil {
|
||||
log.Printf("Failed to create admin notification for edit request %s: %v", bookingID, err)
|
||||
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// If booking status is 'pending', acknowledge existing pending_booking notification and create new one
|
||||
if currentStatus == "pending" {
|
||||
// Acknowledge existing pending_booking notification
|
||||
_, err = tx.Exec(r.Context(), `
|
||||
UPDATE admin_notifications
|
||||
SET acknowledged_at = NOW()
|
||||
WHERE booking_id = $1 AND reason = 'pending_booking' AND acknowledged_at IS NULL
|
||||
`, bookingID)
|
||||
if err != nil {
|
||||
log.Printf("Failed to acknowledge pending booking notification for %s: %v", bookingID, err)
|
||||
}
|
||||
|
||||
// Create new pending_booking notification (admin will see the edit request)
|
||||
_, err = tx.Exec(r.Context(), `
|
||||
INSERT INTO admin_notifications (reason, booking_id, user_id)
|
||||
VALUES ($1, $2, $3)
|
||||
`, "pending_booking", bookingID, userID)
|
||||
if err != nil {
|
||||
log.Printf("Failed to create pending booking notification for %s: %v", bookingID, err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := tx.Commit(r.Context()); err != nil {
|
||||
log.Printf("Failed to commit edit request: %v", err)
|
||||
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
json.NewEncoder(w).Encode(editReq)
|
||||
}
|
||||
|
||||
// AdminListEditRequestsHandler returns all pending edit requests
|
||||
// AdminListEditRequestsHandler returns all edit requests
|
||||
func AdminListEditRequestsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
query := r.URL.Query()
|
||||
status := query.Get("status")
|
||||
|
||||
baseQuery := `
|
||||
SELECT ber.id, ber.booking_id, ber.requested_start_time, ber.status,
|
||||
ber.admin_notes, ber.requested_by, ber.created_at, ber.updated_at,
|
||||
SELECT ber.id, ber.booking_id, ber.requested_by, ber.new_start_time,
|
||||
ber.new_services, ber.notes, ber.has_overrides, ber.updated_at,
|
||||
b.start_time as original_start_time, b.status as booking_status,
|
||||
u.fn as user_name
|
||||
FROM booking_edit_requests ber
|
||||
JOIN bookings b ON ber.booking_id = b.id
|
||||
JOIN users u ON ber.requested_by = u.id
|
||||
`
|
||||
|
||||
|
||||
countQuery := `SELECT COUNT(*) FROM booking_edit_requests ber`
|
||||
|
||||
|
||||
var args []interface{}
|
||||
paramCount := 1
|
||||
|
||||
if status != "" {
|
||||
baseQuery += fmt.Sprintf(" WHERE ber.status = $%d", paramCount)
|
||||
countQuery += fmt.Sprintf(" WHERE ber.status = $%d", paramCount)
|
||||
args = append(args, status)
|
||||
paramCount++
|
||||
}
|
||||
|
||||
baseQuery += " ORDER BY ber.created_at DESC"
|
||||
baseQuery += " ORDER BY ber.updated_at DESC"
|
||||
|
||||
// Get total count
|
||||
var total int
|
||||
@@ -830,15 +951,16 @@ func AdminListEditRequestsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
var origStartTime time.Time
|
||||
var bookingStatus string
|
||||
var userName string
|
||||
var newServicesJSON []byte
|
||||
|
||||
err := rows.Scan(
|
||||
&req.ID,
|
||||
&req.BookingID,
|
||||
&req.RequestedStartTime,
|
||||
&req.Status,
|
||||
&req.AdminNotes,
|
||||
&req.RequestedBy,
|
||||
&req.CreatedAt,
|
||||
&req.NewStartTime,
|
||||
&newServicesJSON,
|
||||
&req.Notes,
|
||||
&req.HasOverrides,
|
||||
&req.UpdatedAt,
|
||||
&origStartTime,
|
||||
&bookingStatus,
|
||||
@@ -849,6 +971,13 @@ func AdminListEditRequestsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Unmarshal JSON services
|
||||
if len(newServicesJSON) > 0 {
|
||||
if err := json.Unmarshal(newServicesJSON, &req.NewServices); err != nil {
|
||||
log.Printf("Failed to unmarshal new_services: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
req.Booking = &Booking{
|
||||
ID: req.BookingID,
|
||||
StartTime: origStartTime,
|
||||
@@ -881,14 +1010,6 @@ func AdminApproveEditRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
AdminNotes *string `json:"admin_notes,omitempty"`
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, "Invalid request body", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
tx, err := db.DB.Begin(r.Context())
|
||||
if err != nil {
|
||||
log.Printf("Failed to start transaction: %v", err)
|
||||
@@ -899,14 +1020,18 @@ func AdminApproveEditRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// Get the edit request
|
||||
var bookingID string
|
||||
var newStartTime time.Time
|
||||
var newStartTime *time.Time
|
||||
var newServicesJSON []byte
|
||||
var notes *string
|
||||
var hasOverrides bool
|
||||
err = tx.QueryRow(r.Context(), `
|
||||
SELECT booking_id, requested_start_time FROM booking_edit_requests
|
||||
WHERE id = $1 AND status = 'pending'
|
||||
`, requestID).Scan(&bookingID, &newStartTime)
|
||||
SELECT booking_id, new_start_time, new_services, notes, has_overrides
|
||||
FROM booking_edit_requests
|
||||
WHERE id = $1
|
||||
`, requestID).Scan(&bookingID, &newStartTime, &newServicesJSON, ¬es, &hasOverrides)
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
http.Error(w, "Edit request not found or already processed", http.StatusNotFound)
|
||||
http.Error(w, "Edit request not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
log.Printf("Failed to get edit request %s: %v", requestID, err)
|
||||
@@ -914,24 +1039,140 @@ func AdminApproveEditRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
// Update the booking
|
||||
_, err = tx.Exec(r.Context(), `
|
||||
UPDATE bookings SET start_time = $1, updated_at = NOW() WHERE id = $2
|
||||
`, newStartTime, bookingID)
|
||||
if err != nil {
|
||||
log.Printf("Failed to update booking %s: %v", bookingID, err)
|
||||
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
||||
// Parse new_services
|
||||
var newServices []string
|
||||
if len(newServicesJSON) > 0 {
|
||||
if err := json.Unmarshal(newServicesJSON, &newServices); err != nil {
|
||||
log.Printf("Failed to unmarshal new_services: %v", err)
|
||||
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// If new_services provided and has_overrides is true, block with error
|
||||
if len(newServices) > 0 && hasOverrides {
|
||||
http.Error(w, "Cannot change services on a booking that has overrides. Please update services manually.", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
// Mark request as approved
|
||||
_, err = tx.Exec(r.Context(), `
|
||||
UPDATE booking_edit_requests
|
||||
SET status = 'approved', admin_notes = $1, updated_at = NOW()
|
||||
WHERE id = $2
|
||||
`, req.AdminNotes, requestID)
|
||||
// Calculate duration for overlap check - use overrides if has_overrides is true
|
||||
var durationMinutes int
|
||||
if hasOverrides {
|
||||
// Use the existing booking_services with overrides
|
||||
err = tx.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)
|
||||
} else {
|
||||
// Use standard durations or new_services if provided
|
||||
if len(newServices) > 0 {
|
||||
// Use new services to calculate duration
|
||||
err = tx.QueryRow(r.Context(), `
|
||||
SELECT COALESCE(SUM(s.duration_minutes), 60)
|
||||
FROM services s
|
||||
WHERE s.id = ANY($1)
|
||||
`, newServices).Scan(&durationMinutes)
|
||||
} else {
|
||||
// Use existing booking services
|
||||
err = tx.QueryRow(r.Context(), `
|
||||
SELECT COALESCE(SUM(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 update edit request %s: %v", requestID, err)
|
||||
log.Printf("Failed to calculate duration: %v", err)
|
||||
durationMinutes = 60 // fallback
|
||||
}
|
||||
|
||||
// Check for overlapping bookings if start time is being changed
|
||||
if newStartTime != nil {
|
||||
newEndTime := newStartTime.Add(time.Duration(durationMinutes) * time.Minute)
|
||||
var overlapCount int
|
||||
err = tx.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, *newStartTime, newEndTime).Scan(&overlapCount)
|
||||
if err != nil {
|
||||
log.Printf("Failed to check overlap: %v", err)
|
||||
}
|
||||
if overlapCount > 0 {
|
||||
http.Error(w, "This edit would cause an overlap with an existing booking", http.StatusConflict)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Build update query for bookings table
|
||||
if newStartTime != nil || notes != nil {
|
||||
var setClauses []string
|
||||
var args []interface{}
|
||||
argNum := 1
|
||||
|
||||
if newStartTime != nil {
|
||||
setClauses = append(setClauses, fmt.Sprintf("start_time = $%d", argNum))
|
||||
args = append(args, *newStartTime)
|
||||
argNum++
|
||||
}
|
||||
if notes != nil {
|
||||
setClauses = append(setClauses, fmt.Sprintf("notes = $%d", argNum))
|
||||
args = append(args, *notes)
|
||||
argNum++
|
||||
}
|
||||
setClauses = append(setClauses, fmt.Sprintf("updated_at = $%d", argNum))
|
||||
args = append(args, time.Now())
|
||||
argNum++
|
||||
|
||||
args = append(args, bookingID)
|
||||
|
||||
query := fmt.Sprintf("UPDATE bookings SET %s WHERE id = $%d", strings.Join(setClauses, ", "), argNum)
|
||||
_, err = tx.Exec(r.Context(), query, args...)
|
||||
if err != nil {
|
||||
log.Printf("Failed to update booking %s: %v", bookingID, err)
|
||||
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Handle new_services (only if has_overrides is false)
|
||||
if len(newServices) > 0 && !hasOverrides {
|
||||
// Delete existing booking_services
|
||||
_, err = tx.Exec(r.Context(), "DELETE FROM booking_services WHERE booking_id = $1", bookingID)
|
||||
if err != nil {
|
||||
log.Printf("Failed to delete existing services for booking %s: %v", bookingID, err)
|
||||
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// Insert new services
|
||||
for _, serviceID := range newServices {
|
||||
_, err = tx.Exec(r.Context(), `
|
||||
INSERT INTO booking_services (booking_id, service_id)
|
||||
VALUES ($1, $2)
|
||||
`, bookingID, serviceID)
|
||||
if err != nil {
|
||||
log.Printf("Failed to insert booking service %s: %v", serviceID, err)
|
||||
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Delete the edit request row (not update status)
|
||||
_, err = tx.Exec(r.Context(), "DELETE FROM booking_edit_requests WHERE id = $1", requestID)
|
||||
if err != nil {
|
||||
log.Printf("Failed to delete edit request %s: %v", requestID, err)
|
||||
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
@@ -945,7 +1186,7 @@ func AdminApproveEditRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
|
||||
// AdminRejectEditRequestHandler rejects an edit request
|
||||
// AdminRejectEditRequestHandler rejects an edit request by deleting it (deny without notification)
|
||||
func AdminRejectEditRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
requestID := chi.URLParam(r, "request_id")
|
||||
if requestID == "" || !validators.IsValidID(requestID) {
|
||||
@@ -953,19 +1194,10 @@ func AdminRejectEditRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
AdminNotes string `json:"admin_notes" validate:"required"`
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, "Invalid request body", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
_, err := db.DB.Exec(r.Context(), `
|
||||
UPDATE booking_edit_requests
|
||||
SET status = 'rejected', admin_notes = $1, updated_at = NOW()
|
||||
WHERE id = $2 AND status = 'pending'
|
||||
`, req.AdminNotes, requestID)
|
||||
DELETE FROM booking_edit_requests
|
||||
WHERE id = $1
|
||||
`, requestID)
|
||||
if err != nil {
|
||||
log.Printf("Failed to reject edit request %s: %v", requestID, err)
|
||||
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
||||
|
||||
@@ -158,6 +158,9 @@ func main() {
|
||||
r.Get("/{id}/calendar", bookings.GetBookingCalendarHandler)
|
||||
r.Put("/{id}", bookings.EditBookingHandler)
|
||||
r.Delete("/{id}", bookings.DeleteBookingHandler)
|
||||
// Edit request endpoints
|
||||
r.Post("/{id}/edit-request", bookings.RequestEditHandler)
|
||||
r.Delete("/{id}/edit-request", bookings.DeleteEditRequestHandler)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -182,6 +185,10 @@ func main() {
|
||||
r.Put("/{id}/progress", bookings.ProgressBookingHandler)
|
||||
r.Post("/{id}/confirm", bookings.ConfirmBookingHandler)
|
||||
r.Post("/{id}/cancel", bookings.CancelBookingHandler)
|
||||
// Edit request endpoints
|
||||
r.Get("/{id}/edit-requests", bookings.AdminListEditRequestsHandler)
|
||||
r.Post("/{id}/edit-requests/{request_id}/approve", bookings.AdminApproveEditRequestHandler)
|
||||
r.Post("/{id}/edit-requests/{request_id}/deny", bookings.AdminRejectEditRequestHandler)
|
||||
})
|
||||
|
||||
r.Route("/admin/users", func(r chi.Router) {
|
||||
|
||||
Reference in New Issue
Block a user