fix edit requests
This commit is contained in:
@@ -33,6 +33,7 @@ require (
|
||||
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
||||
github.com/kovidgoyal/go-parallel v1.1.1 // indirect
|
||||
github.com/kovidgoyal/go-shm v1.0.0 // indirect
|
||||
github.com/lib/pq v1.11.2 // indirect
|
||||
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd // indirect
|
||||
golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa // indirect
|
||||
golang.org/x/image v0.36.0 // indirect
|
||||
|
||||
@@ -75,6 +75,8 @@ github.com/lestrrat-go/jwx/v2 v2.1.6 h1:hxM1gfDILk/l5ylers6BX/Eq1m/pnxe9NBwW6lVf
|
||||
github.com/lestrrat-go/jwx/v2 v2.1.6/go.mod h1:Y722kU5r/8mV7fYDifjug0r8FK8mZdw0K0GpJw/l8pU=
|
||||
github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU=
|
||||
github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
|
||||
github.com/lib/pq v1.11.2 h1:x6gxUeu39V0BHZiugWe8LXZYZ+Utk7hSJGThs8sdzfs=
|
||||
github.com/lib/pq v1.11.2/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA=
|
||||
github.com/nyaruka/phonenumbers v1.6.10 h1:kGTxTzd320dUamRB/MPeZSIwKNLn4vHlysOt5Cp8uoU=
|
||||
github.com/nyaruka/phonenumbers v1.6.10/go.mod h1:IUu45lj2bSeYXQuxDyyuzOrdV10tyRa1YSsfH8EKN5c=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/lib/pq"
|
||||
)
|
||||
|
||||
// UserCancelBookingHandler allows an authenticated user to cancel a booking they own.
|
||||
@@ -726,8 +727,17 @@ func DeleteEditRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
// Use transaction to delete edit request and associated admin notification
|
||||
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 the edit request for this booking
|
||||
res, err := db.DB.Exec(r.Context(), `
|
||||
res, err := tx.Exec(r.Context(), `
|
||||
DELETE FROM booking_edit_requests
|
||||
WHERE booking_id = $1 AND requested_by = $2
|
||||
`, bookingID, userID)
|
||||
@@ -743,6 +753,23 @@ func DeleteEditRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
// Delete the admin notification for this edit request
|
||||
_, err = tx.Exec(r.Context(), `
|
||||
DELETE FROM admin_notifications
|
||||
WHERE booking_id = $1 AND reason = 'edit_request' AND user_id = $2
|
||||
`, bookingID, userID)
|
||||
if err != nil {
|
||||
log.Printf("Failed to delete admin notification for booking %s: %v", bookingID, err)
|
||||
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if err := tx.Commit(r.Context()); err != nil {
|
||||
log.Printf("Failed to commit delete edit request: %v", err)
|
||||
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
|
||||
@@ -951,14 +978,14 @@ func AdminListEditRequestsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
var origStartTime time.Time
|
||||
var bookingStatus string
|
||||
var userName string
|
||||
var newServicesJSON []byte
|
||||
var newServices []string
|
||||
|
||||
err := rows.Scan(
|
||||
&req.ID,
|
||||
&req.BookingID,
|
||||
&req.RequestedBy,
|
||||
&req.NewStartTime,
|
||||
&newServicesJSON,
|
||||
pq.Array(&newServices),
|
||||
&req.Notes,
|
||||
&req.HasOverrides,
|
||||
&req.UpdatedAt,
|
||||
@@ -971,12 +998,7 @@ 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.NewServices = newServices
|
||||
|
||||
req.Booking = &Booking{
|
||||
ID: req.BookingID,
|
||||
@@ -1021,14 +1043,14 @@ func AdminApproveEditRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
// Get the edit request
|
||||
var bookingID string
|
||||
var newStartTime *time.Time
|
||||
var newServicesJSON []byte
|
||||
var newServices []string
|
||||
var notes *string
|
||||
var hasOverrides bool
|
||||
err = tx.QueryRow(r.Context(), `
|
||||
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)
|
||||
`, requestID).Scan(&bookingID, &newStartTime, pq.Array(&newServices), ¬es, &hasOverrides)
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
http.Error(w, "Edit request not found", http.StatusNotFound)
|
||||
@@ -1039,16 +1061,6 @@ func AdminApproveEditRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
// 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)
|
||||
|
||||
@@ -241,37 +241,18 @@ CREATE TABLE booking_edit_requests (
|
||||
booking_id CHAR(12) NOT NULL REFERENCES bookings(id) ON DELETE CASCADE,
|
||||
requested_by CHAR(12) NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
new_start_time TIMESTAMPTZ,
|
||||
new_services JSONB DEFAULT '[]'::jsonb, -- Array of service IDs to replace booking_services
|
||||
new_services CHAR(12)[] DEFAULT '{}', -- Array of service IDs to replace booking_services
|
||||
notes TEXT,
|
||||
has_overrides BOOLEAN NOT NULL DEFAULT FALSE, -- If TRUE, cannot change services, use existing overrides for duration
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
CONSTRAINT chk_at_least_one_field CHECK (
|
||||
new_start_time IS NOT NULL OR
|
||||
new_services IS NOT NULL OR
|
||||
array_length(new_services, 1) IS NOT NULL OR
|
||||
notes IS NOT NULL
|
||||
)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_booking_edit_requests_booking ON booking_edit_requests(booking_id);
|
||||
id CHAR(12) PRIMARY KEY DEFAULT generate_booking_id(),
|
||||
booking_id CHAR(12) NOT NULL REFERENCES bookings(id) ON DELETE CASCADE,
|
||||
requested_by CHAR(12) NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
new_start_time TIMESTAMPTZ,
|
||||
new_services JSONB DEFAULT '[]'::jsonb,
|
||||
new_notes TEXT,
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'pending',
|
||||
admin_notes TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
CONSTRAINT chk_at_least_one_field CHECK (
|
||||
new_start_time IS NOT NULL OR
|
||||
new_services IS NOT NULL OR
|
||||
new_notes IS NOT NULL
|
||||
)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_booking_edit_requests_booking ON booking_edit_requests(booking_id);
|
||||
CREATE INDEX idx_booking_edit_requests_status ON booking_edit_requests(status);
|
||||
|
||||
CREATE TABLE user_referrals (
|
||||
referrer_id CHAR(12) NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
|
||||
Reference in New Issue
Block a user