Fixed user cancelation logic
This commit is contained in:
@@ -816,6 +816,285 @@ func TestAdminBookings_Cancel_NotFound(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestAdminBookings_Cancel_PendingStatus verifies that admin cancellations of pending bookings
|
||||
// do NOT create admin notifications (pending cancellations don't require staff attention).
|
||||
func TestAdminBookings_Cancel_PendingStatus(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
|
||||
adminID, err := fixtures.CreateTestAdminUser(db.DB)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create admin user: %v", err)
|
||||
}
|
||||
defer fixtures.DeleteUser(db.DB, adminID)
|
||||
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create test user: %v", err)
|
||||
}
|
||||
defer fixtures.DeleteUser(db.DB, userID)
|
||||
|
||||
serviceID, err := fixtures.CreateTestService(db.DB)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create test service: %v", err)
|
||||
}
|
||||
defer fixtures.DeleteService(db.DB, serviceID)
|
||||
|
||||
ctx := context.Background()
|
||||
bookingID, err := fixtures.CreateTestBooking(db.DB, userID, serviceID)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create test booking: %v", err)
|
||||
}
|
||||
defer fixtures.DeleteBooking(db.DB, bookingID)
|
||||
|
||||
// Booking stays in 'pending' status (no confirmation)
|
||||
|
||||
handler := http.HandlerFunc(bookings.AdminCancelBookingHandler)
|
||||
w := makeAdminRequest(handler, "POST", "/api/admin/bookings/"+bookingID+"/cancel", nil)
|
||||
|
||||
if w.Code != http.StatusNoContent {
|
||||
t.Errorf("expected status 204, got %d. body: %s", w.Code, w.Body.String())
|
||||
}
|
||||
|
||||
// Verify status changed to we_cancelled
|
||||
var dbStatus string
|
||||
err = db.DB.QueryRow(ctx,
|
||||
"SELECT status FROM bookings WHERE id = $1", bookingID).Scan(&dbStatus)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to query booking: %v", err)
|
||||
}
|
||||
if dbStatus != "we_cancelled" {
|
||||
t.Errorf("expected status 'we_cancelled' in DB, got %s", dbStatus)
|
||||
}
|
||||
|
||||
// Verify NO admin notification was created for pending cancellations
|
||||
var notifCount int
|
||||
err = db.DB.QueryRow(ctx,
|
||||
`SELECT COUNT(*) FROM admin_notifications WHERE booking_id = $1`,
|
||||
bookingID).Scan(¬ifCount)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to query notifications: %v", err)
|
||||
}
|
||||
if notifCount != 0 {
|
||||
t.Errorf("expected 0 admin notifications for pending cancel, got %d", notifCount)
|
||||
}
|
||||
}
|
||||
|
||||
// TestAdminBookings_Cancel_ConfirmedCreatesNotification verifies that cancelling a confirmed
|
||||
// booking creates an admin notification for staff awareness.
|
||||
func TestAdminBookings_Cancel_ConfirmedCreatesNotification(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
|
||||
adminID, err := fixtures.CreateTestAdminUser(db.DB)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create admin user: %v", err)
|
||||
}
|
||||
defer fixtures.DeleteUser(db.DB, adminID)
|
||||
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create test user: %v", err)
|
||||
}
|
||||
defer fixtures.DeleteUser(db.DB, userID)
|
||||
|
||||
serviceID, err := fixtures.CreateTestService(db.DB)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create test service: %v", err)
|
||||
}
|
||||
defer fixtures.DeleteService(db.DB, serviceID)
|
||||
|
||||
ctx := context.Background()
|
||||
bookingID, err := fixtures.CreateTestBooking(db.DB, userID, serviceID)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create test booking: %v", err)
|
||||
}
|
||||
defer fixtures.DeleteBooking(db.DB, bookingID)
|
||||
|
||||
// Confirm the booking
|
||||
_, err = db.DB.Exec(ctx, "UPDATE bookings SET status = 'confirmed' WHERE id = $1", bookingID)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to confirm booking: %v", err)
|
||||
}
|
||||
|
||||
handler := http.HandlerFunc(bookings.AdminCancelBookingHandler)
|
||||
w := makeAdminRequest(handler, "POST", "/api/admin/bookings/"+bookingID+"/cancel", nil)
|
||||
|
||||
if w.Code != http.StatusNoContent {
|
||||
t.Errorf("expected status 204, got %d. body: %s", w.Code, w.Body.String())
|
||||
}
|
||||
|
||||
// Verify status changed to we_cancelled
|
||||
var dbStatus string
|
||||
err = db.DB.QueryRow(ctx,
|
||||
"SELECT status FROM bookings WHERE id = $1", bookingID).Scan(&dbStatus)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to query booking: %v", err)
|
||||
}
|
||||
if dbStatus != "we_cancelled" {
|
||||
t.Errorf("expected status 'we_cancelled' in DB, got %s", dbStatus)
|
||||
}
|
||||
|
||||
// Verify admin notification WAS created for confirmed->cancelled
|
||||
var notifCount int
|
||||
err = db.DB.QueryRow(ctx,
|
||||
`SELECT COUNT(*) FROM admin_notifications WHERE booking_id = $1 AND reason = 'cancelled_booking'`,
|
||||
bookingID).Scan(¬ifCount)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to query notifications: %v", err)
|
||||
}
|
||||
if notifCount != 1 {
|
||||
t.Errorf("expected 1 admin notification for confirmed cancel, got %d", notifCount)
|
||||
}
|
||||
}
|
||||
|
||||
// TestAdminBookings_Cancel_InProgressStatus verifies cancellation of in-progress bookings.
|
||||
func TestAdminBookings_Cancel_InProgressStatus(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
|
||||
adminID, err := fixtures.CreateTestAdminUser(db.DB)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create admin user: %v", err)
|
||||
}
|
||||
defer fixtures.DeleteUser(db.DB, adminID)
|
||||
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create test user: %v", err)
|
||||
}
|
||||
defer fixtures.DeleteUser(db.DB, userID)
|
||||
|
||||
serviceID, err := fixtures.CreateTestService(db.DB)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create test service: %v", err)
|
||||
}
|
||||
defer fixtures.DeleteService(db.DB, serviceID)
|
||||
|
||||
ctx := context.Background()
|
||||
bookingID, err := fixtures.CreateTestBooking(db.DB, userID, serviceID)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create test booking: %v", err)
|
||||
}
|
||||
defer fixtures.DeleteBooking(db.DB, bookingID)
|
||||
|
||||
// Set booking to in-progress status
|
||||
_, err = db.DB.Exec(ctx, "UPDATE bookings SET status = 'in_progress' WHERE id = $1", bookingID)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to set in_progress status: %v", err)
|
||||
}
|
||||
|
||||
handler := http.HandlerFunc(bookings.AdminCancelBookingHandler)
|
||||
w := makeAdminRequest(handler, "POST", "/api/admin/bookings/"+bookingID+"/cancel", nil)
|
||||
|
||||
if w.Code != http.StatusNoContent {
|
||||
t.Errorf("expected status 204, got %d. body: %s", w.Code, w.Body.String())
|
||||
}
|
||||
|
||||
// Verify status changed to we_cancelled
|
||||
var dbStatus string
|
||||
err = db.DB.QueryRow(ctx,
|
||||
"SELECT status FROM bookings WHERE id = $1", bookingID).Scan(&dbStatus)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to query booking: %v", err)
|
||||
}
|
||||
if dbStatus != "we_cancelled" {
|
||||
t.Errorf("expected status 'we_cancelled' in DB, got %s", dbStatus)
|
||||
}
|
||||
}
|
||||
|
||||
// TestAdminBookings_Cancel_AlreadyCancelledRejectsCancellation verifies that attempting to
|
||||
// cancel an already-cancelled booking returns 404 Not Found (idempotency guard).
|
||||
func TestAdminBookings_Cancel_AlreadyCancelledRejectsCancellation(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
|
||||
adminID, err := fixtures.CreateTestAdminUser(db.DB)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create admin user: %v", err)
|
||||
}
|
||||
defer fixtures.DeleteUser(db.DB, adminID)
|
||||
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create test user: %v", err)
|
||||
}
|
||||
defer fixtures.DeleteUser(db.DB, userID)
|
||||
|
||||
serviceID, err := fixtures.CreateTestService(db.DB)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create test service: %v", err)
|
||||
}
|
||||
defer fixtures.DeleteService(db.DB, serviceID)
|
||||
|
||||
ctx := context.Background()
|
||||
bookingID, err := fixtures.CreateTestBooking(db.DB, userID, serviceID)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create test booking: %v", err)
|
||||
}
|
||||
defer fixtures.DeleteBooking(db.DB, bookingID)
|
||||
|
||||
// Set to already cancelled
|
||||
_, err = db.DB.Exec(ctx, "UPDATE bookings SET status = 'we_cancelled' WHERE id = $1", bookingID)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to set we_cancelled status: %v", err)
|
||||
}
|
||||
|
||||
// Try to cancel again
|
||||
handler := http.HandlerFunc(bookings.AdminCancelBookingHandler)
|
||||
w := makeAdminRequest(handler, "POST", "/api/admin/bookings/"+bookingID+"/cancel", nil)
|
||||
|
||||
if w.Code != http.StatusNotFound {
|
||||
t.Errorf("expected status 404 for already-cancelled booking, got %d", w.Code)
|
||||
}
|
||||
}
|
||||
|
||||
// TestAdminBookings_Cancel_CompletedRejectsCancellation verifies that attempting to cancel
|
||||
// a completed booking returns 404 Not Found (cannot cancel finished appointments).
|
||||
func TestAdminBookings_Cancel_CompletedRejectsCancellation(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
|
||||
adminID, err := fixtures.CreateTestAdminUser(db.DB)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create admin user: %v", err)
|
||||
}
|
||||
defer fixtures.DeleteUser(db.DB, adminID)
|
||||
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create test user: %v", err)
|
||||
}
|
||||
defer fixtures.DeleteUser(db.DB, userID)
|
||||
|
||||
serviceID, err := fixtures.CreateTestService(db.DB)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create test service: %v", err)
|
||||
}
|
||||
defer fixtures.DeleteService(db.DB, serviceID)
|
||||
|
||||
ctx := context.Background()
|
||||
bookingID, err := fixtures.CreateTestBooking(db.DB, userID, serviceID)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create test booking: %v", err)
|
||||
}
|
||||
defer fixtures.DeleteBooking(db.DB, bookingID)
|
||||
|
||||
// Set to completed
|
||||
_, err = db.DB.Exec(ctx, "UPDATE bookings SET status = 'completed' WHERE id = $1", bookingID)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to set completed status: %v", err)
|
||||
}
|
||||
|
||||
// Try to cancel
|
||||
handler := http.HandlerFunc(bookings.AdminCancelBookingHandler)
|
||||
w := makeAdminRequest(handler, "POST", "/api/admin/bookings/"+bookingID+"/cancel", nil)
|
||||
|
||||
if w.Code != http.StatusNotFound {
|
||||
t.Errorf("expected status 404 for completed booking, got %d", w.Code)
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Non-Admin Tests
|
||||
// =============================================================================
|
||||
|
||||
+1
-1
@@ -185,7 +185,7 @@ func main() {
|
||||
r.Get("/{id}/overlapping", bookings.GetOverlappingBookingsHandler)
|
||||
r.Put("/{id}/progress", bookings.ProgressBookingHandler)
|
||||
r.Post("/{id}/confirm", bookings.ConfirmBookingHandler)
|
||||
r.Post("/{id}/cancel", bookings.CancelBookingHandler)
|
||||
r.Post("/{id}/cancel", bookings.AdminCancelBookingHandler)
|
||||
// Edit request endpoints
|
||||
r.Get("/{id}/edit-requests", bookings.AdminListEditRequestsHandler)
|
||||
r.Post("/{id}/edit-requests/{request_id}/approve", bookings.AdminApproveEditRequestHandler)
|
||||
|
||||
+555
-228
@@ -8,7 +8,6 @@ setopt PIPE_FAIL
|
||||
setopt ERR_EXIT
|
||||
|
||||
# --- UI Helpers ---
|
||||
# Color codes for output
|
||||
C_RESET=$'\033[0m'
|
||||
C_GREEN=$'\033[32m'
|
||||
C_RED=$'\033[31m'
|
||||
@@ -137,12 +136,10 @@ tmux set-environment -t $SESSION_NAME AWS_REGION "$AWS_REGION"
|
||||
tmux set-environment -t $SESSION_NAME VITE_BACKEND_URL "$VITE_BACKEND_URL"
|
||||
|
||||
# Pane 0: Database
|
||||
# Start with tables + row count query
|
||||
tmux send-keys -t $SESSION_NAME 'docker exec -it postgres psql -U myuser -d mydb -c "SELECT relname AS table_name, n_live_tup AS row_count FROM pg_stat_user_tables ORDER BY table_name;"'
|
||||
tmux select-pane -t $SESSION_NAME:0.0 -T "DB"
|
||||
|
||||
# Pane 1: Backend (Split Horizontally)
|
||||
# Source .env to ensure all env vars are available to Go
|
||||
tmux split-window -v -t $SESSION_NAME
|
||||
tmux send-keys -t $SESSION_NAME "set -a; source .env > /dev/null 2>&1; cd backend && go run -tags dev ./main.go" Enter
|
||||
tmux select-pane -t $SESSION_NAME:0.1 -T "Backend"
|
||||
@@ -174,6 +171,14 @@ USER_EMAIL="user@example.com"
|
||||
USER_PASS="password"
|
||||
BASE_URL="http://localhost:8080/api"
|
||||
|
||||
# Safe booking times — mid-morning to early afternoon to avoid closing-hour conflicts.
|
||||
# Adjust these if your business hours differ.
|
||||
SLOT_A="10:00:00"
|
||||
SLOT_B="11:30:00"
|
||||
SLOT_C="13:00:00"
|
||||
SLOT_D="14:30:00"
|
||||
SLOT_E="12:15:00" # Used only for pending demo bookings — avoids conflicts with A-D rotation
|
||||
|
||||
# --- Formatting ---
|
||||
C_RESET=$'\033[0m'
|
||||
C_GREEN=$'\033[32m'
|
||||
@@ -185,6 +190,7 @@ C_YELLOW=$'\033[33m'
|
||||
LAST_BOOKING_ID=""
|
||||
|
||||
# --- Helper: API Request ---
|
||||
# Returns the root-level "id" from the response body on success.
|
||||
api_post() {
|
||||
local url="$1"
|
||||
local data="$2"
|
||||
@@ -200,12 +206,13 @@ api_post() {
|
||||
local body=$(echo "$response" | sed '$d')
|
||||
|
||||
if [[ "$http_code" =~ ^2 ]]; then
|
||||
# FIX:
|
||||
# 1. tr -d '\n': Ensure JSON is treated as a single line (handles pretty-printing).
|
||||
# 2. sed 's/"user":{[^}]*}//': Remove the "user" object entirely.
|
||||
# [^}]* matches everything up to the first closing brace, which is safe for UserSummary (flat object).
|
||||
# 3. grep/cut: Extract the remaining root 'id' (which is now the Booking ID).
|
||||
echo "$body" | tr -d '\n' | sed 's/"user":{[^}]*}//' | grep -o '"id":"[^"]*' | cut -d'"' -f4 | tr -d '\r\n'
|
||||
# Strip nested "user" object so we reliably get the root "id" (UUID)
|
||||
local extracted
|
||||
extracted=$(echo "$body" | tr -d '\n' | sed 's/"user":{[^}]*}//' | grep -o '"id":"[^"]*' | cut -d'"' -f4 | tr -d '\r\n ')
|
||||
# Only emit the value if it looks like a UUID (hex + hyphens, 8+ chars)
|
||||
if [[ "$extracted" =~ ^[0-9a-f-]{8,}$ ]]; then
|
||||
echo "$extracted"
|
||||
fi
|
||||
return 0
|
||||
else
|
||||
printf "${C_RED}❌ Failed: %s (HTTP %s)${C_RESET}\n" "$desc" "$http_code"
|
||||
@@ -215,6 +222,16 @@ api_post() {
|
||||
fi
|
||||
}
|
||||
|
||||
# --- Helper: Login and return token ---
|
||||
login() {
|
||||
local email="$1" pass="$2"
|
||||
curl -s -X POST -H 'Content-Type: application/json' \
|
||||
-d "{\"email\":\"$email\",\"password\":\"$pass\"}" \
|
||||
"$BASE_URL/login" \
|
||||
| tr -d '\r\n\t ' \
|
||||
| sed -n 's/.*"token":"\([^"]*\)".*/\1/p'
|
||||
}
|
||||
|
||||
wait_for_backend() {
|
||||
echo -ne "⏳ Waiting for backend..."
|
||||
for ((i=1; i<=60; i++)); do
|
||||
@@ -232,81 +249,128 @@ wait_for_backend() {
|
||||
# --- Main Execution ---
|
||||
wait_for_backend
|
||||
|
||||
# 1. Register Users
|
||||
# ===========================================================================
|
||||
# 1. REGISTER USERS
|
||||
# ===========================================================================
|
||||
echo -e "\n${C_BLUE}👤 Registering Users...${C_RESET}"
|
||||
success=0
|
||||
total=18
|
||||
total=20
|
||||
|
||||
# Admin user
|
||||
if api_post "$BASE_URL/register" "{\"firstName\":\"Admin\",\"lastName\":\"User\",\"email\":\"$ADMIN_EMAIL\",\"password\":\"$ADMIN_PASS\",\"phone\":\"+447000000000\",\"dateOfBirth\":\"1985-01-01\",\"agreedToPolicy\":true}" "Register Admin" "" > /dev/null; then success=$((success+1)); fi
|
||||
# Admin
|
||||
if api_post "$BASE_URL/register" '{"firstName":"Admin","lastName":"User","email":"admin@example.com","password":"password","phone":"+447000000000","dateOfBirth":"1985-01-01","agreedToPolicy":true}' "Register Admin" "" > /dev/null; then success=$((success+1)); fi
|
||||
|
||||
# Original test user
|
||||
if api_post "$BASE_URL/register" "{\"firstName\":\"Regular\",\"lastName\":\"User\",\"email\":\"$USER_EMAIL\",\"password\":\"$USER_PASS\",\"phone\":\"+447000000001\",\"dateOfBirth\":\"1990-05-15\",\"agreedToPolicy\":true}" "Register User" "" > /dev/null; then success=$((success+1)); fi
|
||||
# Primary test user (no-deposit, easy to book with)
|
||||
if api_post "$BASE_URL/register" '{"firstName":"Regular","lastName":"User","email":"user@example.com","password":"password","phone":"+447000000001","dateOfBirth":"1990-05-15","agreedToPolicy":true}' "Register User" "" > /dev/null; then success=$((success+1)); fi
|
||||
|
||||
# Additional test users (16 more)
|
||||
if api_post "$BASE_URL/register" "{\"firstName\":\"Emma\",\"lastName\":\"Johnson\",\"email\":\"emma.johnson@example.com\",\"password\":\"password\",\"phone\":\"+447000000002\",\"dateOfBirth\":\"1988-03-22\",\"agreedToPolicy\":true}" "Register Emma Johnson" "" > /dev/null; then success=$((success+1)); fi
|
||||
if api_post "$BASE_URL/register" "{\"firstName\":\"Oliver\",\"lastName\":\"Smith\",\"email\":\"oliver.smith@example.com\",\"password\":\"password\",\"phone\":\"+447000000003\",\"dateOfBirth\":\"1992-07-14\",\"agreedToPolicy\":true}" "Register Oliver Smith" "" > /dev/null; then success=$((success+1)); fi
|
||||
if api_post "$BASE_URL/register" "{\"firstName\":\"Sophie\",\"lastName\":\"Williams\",\"email\":\"sophie.williams@example.com\",\"password\":\"password\",\"phone\":\"+447000000004\",\"dateOfBirth\":\"1995-11-08\",\"agreedToPolicy\":true}" "Register Sophie Williams" "" > /dev/null; then success=$((success+1)); fi
|
||||
if api_post "$BASE_URL/register" "{\"firstName\":\"Harry\",\"lastName\":\"Brown\",\"email\":\"harry.brown@example.com\",\"password\":\"password\",\"phone\":\"+447000000005\",\"dateOfBirth\":\"1987-02-19\",\"agreedToPolicy\":true}" "Register Harry Brown" "" > /dev/null; then success=$((success+1)); fi
|
||||
if api_post "$BASE_URL/register" "{\"firstName\":\"Amelia\",\"lastName\":\"Jones\",\"email\":\"amelia.jones@example.com\",\"password\":\"password\",\"phone\":\"+447000000006\",\"dateOfBirth\":\"1993-09-30\",\"agreedToPolicy\":true}" "Register Amelia Jones" "" > /dev/null; then success=$((success+1)); fi
|
||||
if api_post "$BASE_URL/register" "{\"firstName\":\"Jack\",\"lastName\":\"Taylor\",\"email\":\"jack.taylor@example.com\",\"password\":\"password\",\"phone\":\"+447000000007\",\"dateOfBirth\":\"1991-05-12\",\"agreedToPolicy\":true}" "Register Jack Taylor" "" > /dev/null; then success=$((success+1)); fi
|
||||
if api_post "$BASE_URL/register" "{\"firstName\":\"Isla\",\"lastName\":\"Davies\",\"email\":\"isla.davies@example.com\",\"password\":\"password\",\"phone\":\"+447000000008\",\"dateOfBirth\":\"1989-12-25\",\"agreedToPolicy\":true}" "Register Isla Davies" "" > /dev/null; then success=$((success+1)); fi
|
||||
if api_post "$BASE_URL/register" "{\"firstName\":\"Thomas\",\"lastName\":\"Evans\",\"email\":\"thomas.evans@example.com\",\"password\":\"password\",\"phone\":\"+447000000009\",\"dateOfBirth\":\"1994-04-17\",\"agreedToPolicy\":true}" "Register Thomas Evans" "" > /dev/null; then success=$((success+1)); fi
|
||||
if api_post "$BASE_URL/register" "{\"firstName\":\"Lily\",\"lastName\":\"Wilson\",\"email\":\"lily.wilson@example.com\",\"password\":\"password\",\"phone\":\"+447000000010\",\"dateOfBirth\":\"1996-08-03\",\"agreedToPolicy\":true}" "Register Lily Wilson" "" > /dev/null; then success=$((success+1)); fi
|
||||
if api_post "$BASE_URL/register" "{\"firstName\":\"George\",\"lastName\":\"Roberts\",\"email\":\"george.roberts@example.com\",\"password\":\"password\",\"phone\":\"+447000000011\",\"dateOfBirth\":\"1986-01-29\",\"agreedToPolicy\":true}" "Register George Roberts" "" > /dev/null; then success=$((success+1)); fi
|
||||
if api_post "$BASE_URL/register" "{\"firstName\":\"Poppy\",\"lastName\":\"Thompson\",\"email\":\"poppy.thompson@example.com\",\"password\":\"password\",\"phone\":\"+447000000012\",\"dateOfBirth\":\"1997-06-21\",\"agreedToPolicy\":true}" "Register Poppy Thompson" "" > /dev/null; then success=$((success+1)); fi
|
||||
if api_post "$BASE_URL/register" "{\"firstName\":\"Charlie\",\"lastName\":\"Wright\",\"email\":\"charlie.wright@example.com\",\"password\":\"password\",\"phone\":\"+447000000013\",\"dateOfBirth\":\"1990-10-11\",\"agreedToPolicy\":true}" "Register Charlie Wright" "" > /dev/null; then success=$((success+1)); fi
|
||||
if api_post "$BASE_URL/register" "{\"firstName\":\"Ava\",\"lastName\":\"Walker\",\"email\":\"ava.walker@example.com\",\"password\":\"password\",\"phone\":\"+447000000014\",\"dateOfBirth\":\"1993-03-07\",\"agreedToPolicy\":true}" "Register Ava Walker" "" > /dev/null; then success=$((success+1)); fi
|
||||
if api_post "$BASE_URL/register" "{\"firstName\":\"Noah\",\"lastName\":\"Robinson\",\"email\":\"noah.robinson@example.com\",\"password\":\"password\",\"phone\":\"+447000000015\",\"dateOfBirth\":\"1988-11-16\",\"agreedToPolicy\":true}" "Register Noah Robinson" "" > /dev/null; then success=$((success+1)); fi
|
||||
if api_post "$BASE_URL/register" "{\"firstName\":\"Mia\",\"lastName\":\"White\",\"email\":\"mia.white@example.com\",\"password\":\"password\",\"phone\":\"+447000000016\",\"dateOfBirth\":\"1995-07-28\",\"agreedToPolicy\":true}" "Register Mia White" "" > /dev/null; then success=$((success+1)); fi
|
||||
if api_post "$BASE_URL/register" "{\"firstName\":\"Oscar\",\"lastName\":\"Hughes\",\"email\":\"oscar.hughes@example.com\",\"password\":\"password\",\"phone\":\"+447000000017\",\"dateOfBirth\":\"1991-02-04\",\"agreedToPolicy\":true}" "Register Oscar Hughes" "" > /dev/null; then success=$((success+1)); fi
|
||||
# Loyal regulars
|
||||
if api_post "$BASE_URL/register" '{"firstName":"Emma","lastName":"Johnson","email":"emma.johnson@example.com","password":"password","phone":"+447000000002","dateOfBirth":"1988-03-22","agreedToPolicy":true}' "Register Emma Johnson" "" > /dev/null; then success=$((success+1)); fi
|
||||
if api_post "$BASE_URL/register" '{"firstName":"Sophie","lastName":"Williams","email":"sophie.williams@example.com","password":"password","phone":"+447000000003","dateOfBirth":"1995-11-08","agreedToPolicy":true}' "Register Sophie Williams" "" > /dev/null; then success=$((success+1)); fi
|
||||
if api_post "$BASE_URL/register" '{"firstName":"Amelia","lastName":"Jones","email":"amelia.jones@example.com","password":"password","phone":"+447000000004","dateOfBirth":"1993-09-30","agreedToPolicy":true}' "Register Amelia Jones" "" > /dev/null; then success=$((success+1)); fi
|
||||
if api_post "$BASE_URL/register" '{"firstName":"Isla","lastName":"Davies","email":"isla.davies@example.com","password":"password","phone":"+447000000005","dateOfBirth":"1989-12-25","agreedToPolicy":true}' "Register Isla Davies" "" > /dev/null; then success=$((success+1)); fi
|
||||
if api_post "$BASE_URL/register" '{"firstName":"Lily","lastName":"Wilson","email":"lily.wilson@example.com","password":"password","phone":"+447000000006","dateOfBirth":"1996-08-03","agreedToPolicy":true}' "Register Lily Wilson" "" > /dev/null; then success=$((success+1)); fi
|
||||
if api_post "$BASE_URL/register" '{"firstName":"Ava","lastName":"Walker","email":"ava.walker@example.com","password":"password","phone":"+447000000007","dateOfBirth":"1993-03-07","agreedToPolicy":true}' "Register Ava Walker" "" > /dev/null; then success=$((success+1)); fi
|
||||
|
||||
# Promote Admin
|
||||
docker exec postgres psql -U myuser -d mydb -c "UPDATE users SET account_role = 'admin' WHERE email = '$ADMIN_EMAIL'" > /dev/null 2>&1
|
||||
# Occasional clients
|
||||
if api_post "$BASE_URL/register" '{"firstName":"Oliver","lastName":"Smith","email":"oliver.smith@example.com","password":"password","phone":"+447000000008","dateOfBirth":"1992-07-14","agreedToPolicy":true}' "Register Oliver Smith" "" > /dev/null; then success=$((success+1)); fi
|
||||
if api_post "$BASE_URL/register" '{"firstName":"Harry","lastName":"Brown","email":"harry.brown@example.com","password":"password","phone":"+447000000009","dateOfBirth":"1987-02-19","agreedToPolicy":true}' "Register Harry Brown" "" > /dev/null; then success=$((success+1)); fi
|
||||
if api_post "$BASE_URL/register" '{"firstName":"George","lastName":"Roberts","email":"george.roberts@example.com","password":"password","phone":"+447000000010","dateOfBirth":"1986-01-29","agreedToPolicy":true}' "Register George Roberts" "" > /dev/null; then success=$((success+1)); fi
|
||||
if api_post "$BASE_URL/register" '{"firstName":"Charlie","lastName":"Wright","email":"charlie.wright@example.com","password":"password","phone":"+447000000011","dateOfBirth":"1990-10-11","agreedToPolicy":true}' "Register Charlie Wright" "" > /dev/null; then success=$((success+1)); fi
|
||||
if api_post "$BASE_URL/register" '{"firstName":"Noah","lastName":"Robinson","email":"noah.robinson@example.com","password":"password","phone":"+447000000012","dateOfBirth":"1988-11-16","agreedToPolicy":true}' "Register Noah Robinson" "" > /dev/null; then success=$((success+1)); fi
|
||||
if api_post "$BASE_URL/register" '{"firstName":"Thomas","lastName":"Evans","email":"thomas.evans@example.com","password":"password","phone":"+447000000013","dateOfBirth":"1994-04-17","agreedToPolicy":true}' "Register Thomas Evans" "" > /dev/null; then success=$((success+1)); fi
|
||||
if api_post "$BASE_URL/register" '{"firstName":"Oscar","lastName":"Hughes","email":"oscar.hughes@example.com","password":"password","phone":"+447000000014","dateOfBirth":"1991-02-04","agreedToPolicy":true}' "Register Oscar Hughes" "" > /dev/null; then success=$((success+1)); fi
|
||||
|
||||
# Deposit-required users (demonstrate deposit snapshot behaviour)
|
||||
if api_post "$BASE_URL/register" '{"firstName":"Poppy","lastName":"Thompson","email":"poppy.thompson@example.com","password":"password","phone":"+447000000015","dateOfBirth":"1997-06-21","agreedToPolicy":true}' "Register Poppy Thompson" "" > /dev/null; then success=$((success+1)); fi
|
||||
if api_post "$BASE_URL/register" '{"firstName":"Mia","lastName":"White","email":"mia.white@example.com","password":"password","phone":"+447000000016","dateOfBirth":"1995-07-28","agreedToPolicy":true}' "Register Mia White" "" > /dev/null; then success=$((success+1)); fi
|
||||
|
||||
# Young client (for age-restricted service testing)
|
||||
if api_post "$BASE_URL/register" '{"firstName":"Chloe","lastName":"Park","email":"chloe.park@example.com","password":"password","phone":"+447000000017","dateOfBirth":"2009-04-12","agreedToPolicy":true}' "Register Chloe Park" "" > /dev/null; then success=$((success+1)); fi
|
||||
|
||||
# Patch-test-complete user (will have gel allergy test recorded)
|
||||
if api_post "$BASE_URL/register" '{"firstName":"Grace","lastName":"Fletcher","email":"grace.fletcher@example.com","password":"password","phone":"+447000000018","dateOfBirth":"1991-08-30","agreedToPolicy":true}' "Register Grace Fletcher" "" > /dev/null; then success=$((success+1)); fi
|
||||
|
||||
# No-show history user (to demonstrate 48h booking restriction)
|
||||
if api_post "$BASE_URL/register" '{"firstName":"Liam","lastName":"Caldwell","email":"liam.caldwell@example.com","password":"password","phone":"+447000000019","dateOfBirth":"1989-03-14","agreedToPolicy":true}' "Register Liam Caldwell" "" > /dev/null; then success=$((success+1)); fi
|
||||
|
||||
# --- Promote admin and set deposit flags via DB ---
|
||||
docker exec postgres psql -U myuser -d mydb -c "UPDATE users SET account_role = 'admin' WHERE email = 'admin@example.com'" > /dev/null 2>&1
|
||||
# Primary test user: zero deposit requirement for easy booking
|
||||
docker exec postgres psql -U myuser -d mydb -c "UPDATE users SET deposits_required = 0 WHERE email = 'user@example.com'" > /dev/null 2>&1
|
||||
# Loyal regulars: zero deposit requirement
|
||||
docker exec postgres psql -U myuser -d mydb -c "UPDATE users SET deposits_required = 0 WHERE email IN ('emma.johnson@example.com','sophie.williams@example.com','amelia.jones@example.com','isla.davies@example.com','lily.wilson@example.com','ava.walker@example.com','grace.fletcher@example.com')" > /dev/null 2>&1
|
||||
# Deposit-required users: 3 no-shows on record
|
||||
docker exec postgres psql -U myuser -d mydb -c "UPDATE users SET deposits_required = 3 WHERE email IN ('poppy.thompson@example.com','mia.white@example.com')" > /dev/null 2>&1
|
||||
# Liam: 1 no-show, in 48h restriction window
|
||||
docker exec postgres psql -U myuser -d mydb -c "UPDATE users SET deposits_required = 1 WHERE email = 'liam.caldwell@example.com'" > /dev/null 2>&1
|
||||
|
||||
# Set deposits_required=0 for main test user (so bookings can be created without 48h restriction)
|
||||
# Set deposits_required=3 for some users to demonstrate deposit snapshot behavior
|
||||
docker exec postgres psql -U myuser -d mydb -c "UPDATE users SET deposits_required = 0 WHERE email = '$USER_EMAIL'" > /dev/null 2>&1
|
||||
docker exec postgres psql -U myuser -d mydb -c "UPDATE users SET deposits_required = 3 WHERE email IN ('poppy.thompson@example.com', 'mia.white@example.com')" > /dev/null 2>&1
|
||||
echo "${C_GREEN}✅ Registered $success/$total Users${C_RESET}"
|
||||
user_success=$success
|
||||
user_total=$total
|
||||
|
||||
# 2. Login
|
||||
# ===========================================================================
|
||||
# 2. LOGIN
|
||||
# ===========================================================================
|
||||
echo -e "\n${C_BLUE}🔑 Authenticating...${C_RESET}"
|
||||
LOGIN_RESP=$(curl -s -X POST -H 'Content-Type: application/json' -d "{\"email\":\"$ADMIN_EMAIL\",\"password\":\"$ADMIN_PASS\"}" "$BASE_URL/login")
|
||||
# FIX 2: Clean token extraction
|
||||
ADMIN_TOKEN=$(echo "$LOGIN_RESP" \
|
||||
| tr -d '\r\n\t ' \
|
||||
| sed -n 's/.*"token":"\([^"]*\)".*/\1/p')
|
||||
|
||||
if [ -z "$ADMIN_TOKEN" ]; then
|
||||
echo "❌ Admin auth failed. Response: $LOGIN_RESP"
|
||||
exit 1
|
||||
fi
|
||||
ADMIN_TOKEN=$(login "$ADMIN_EMAIL" "$ADMIN_PASS")
|
||||
if [ -z "$ADMIN_TOKEN" ]; then echo "❌ Admin auth failed"; exit 1; fi
|
||||
|
||||
USER_LOGIN_RESP=$(curl -s -X POST -H 'Content-Type: application/json' -d "{\"email\":\"$USER_EMAIL\",\"password\":\"$USER_PASS\"}" "$BASE_URL/login")
|
||||
USER_TOKEN=$(echo "$USER_LOGIN_RESP" \
|
||||
| tr -d '\r\n\t ' \
|
||||
| sed -n 's/.*"token":"\([^"]*\)".*/\1/p')
|
||||
USER_TOKEN=$(login "$USER_EMAIL" "$USER_PASS")
|
||||
if [ -z "$USER_TOKEN" ]; then echo "❌ User auth failed"; exit 1; fi
|
||||
|
||||
EMMA_TOKEN=$(login "emma.johnson@example.com" "password")
|
||||
SOPHIE_TOKEN=$(login "sophie.williams@example.com" "password")
|
||||
AMELIA_TOKEN=$(login "amelia.jones@example.com" "password")
|
||||
ISLA_TOKEN=$(login "isla.davies@example.com" "password")
|
||||
LILY_TOKEN=$(login "lily.wilson@example.com" "password")
|
||||
AVA_TOKEN=$(login "ava.walker@example.com" "password")
|
||||
POPPY_TOKEN=$(login "poppy.thompson@example.com" "password")
|
||||
MIA_TOKEN=$(login "mia.white@example.com" "password")
|
||||
GRACE_TOKEN=$(login "grace.fletcher@example.com" "password")
|
||||
|
||||
# Look up user IDs for admin booking creation
|
||||
get_user_id() {
|
||||
docker exec postgres psql -U myuser -d mydb -tAc "SELECT id FROM users WHERE email='$1';" 2>/dev/null | tr -d '\r\n\t '
|
||||
}
|
||||
|
||||
ADMIN_USER_ID=$(get_user_id "admin@example.com")
|
||||
USER_USER_ID=$(get_user_id "user@example.com")
|
||||
EMMA_ID=$(get_user_id "emma.johnson@example.com")
|
||||
SOPHIE_ID=$(get_user_id "sophie.williams@example.com")
|
||||
AMELIA_ID=$(get_user_id "amelia.jones@example.com")
|
||||
ISLA_ID=$(get_user_id "isla.davies@example.com")
|
||||
LILY_ID=$(get_user_id "lily.wilson@example.com")
|
||||
AVA_ID=$(get_user_id "ava.walker@example.com")
|
||||
OLIVER_ID=$(get_user_id "oliver.smith@example.com")
|
||||
HARRY_ID=$(get_user_id "harry.brown@example.com")
|
||||
CHARLIE_ID=$(get_user_id "charlie.wright@example.com")
|
||||
NOAH_ID=$(get_user_id "noah.robinson@example.com")
|
||||
POPPY_ID=$(get_user_id "poppy.thompson@example.com")
|
||||
MIA_ID=$(get_user_id "mia.white@example.com")
|
||||
GRACE_ID=$(get_user_id "grace.fletcher@example.com")
|
||||
LIAM_ID=$(get_user_id "liam.caldwell@example.com")
|
||||
|
||||
if [ -z "$USER_TOKEN" ]; then
|
||||
echo "❌ User auth failed. Response: $USER_LOGIN_RESP"
|
||||
exit 1
|
||||
fi
|
||||
echo "${C_GREEN}✅ Authentication successful${C_RESET}"
|
||||
sleep 1
|
||||
|
||||
# 3. Create Services
|
||||
# ===========================================================================
|
||||
# 3. CREATE SERVICES
|
||||
# ===========================================================================
|
||||
echo -e "\n${C_BLUE}💅 Creating Services...${C_RESET}"
|
||||
|
||||
SERVICES=(
|
||||
'{"name":"Classic Manicure","description":"Nail shaping, cuticle care, hand massage, and polish.","price":25.00,"duration_minutes":45,"minimum_age_required":0}'
|
||||
'{"name":"Gel Manicure (BIAB)","description":"Hard-wearing gel polish with Builder In A Bottle base.","price":35.00,"duration_minutes":60,"minimum_age_required":0}'
|
||||
'{"name":"Gel Manicure (BIAB)","description":"Hard-wearing gel polish with Builder In A Bottle base. Long-lasting with added nail strength.","price":35.00,"duration_minutes":60,"minimum_age_required":0}'
|
||||
'{"name":"Luxury Pedicure","description":"Foot soak, scrub, mask, extended massage, and polish.","price":45.00,"duration_minutes":75,"minimum_age_required":0}'
|
||||
'{"name":"Express Mani & Pedi","description":"Quick file, shape, and polish for both hands and feet.","price":40.00,"duration_minutes":60,"minimum_age_required":0}'
|
||||
'{"name":"Gel Polish Removal","description":"Safe removal of existing gel polish.","price":10.00,"duration_minutes":20,"minimum_age_required":0}'
|
||||
'{"name":"Nail Art Add-on","description":"Custom nail art, per two fingers.","price":5.00,"duration_minutes":15,"minimum_age_required":0}'
|
||||
'{"name":"Gel Polish Full Set","description":"Full gel polish application - requires patch test 24h before.","price":45.00,"duration_minutes":60,"minimum_age_required":0}'
|
||||
'{"name":"Luxury Gel Manicure","description":"Premium gel polish with extended massage - requires patch test 24h before.","price":55.00,"duration_minutes":75,"minimum_age_required":0}'
|
||||
'{"name":"Gel Polish Removal","description":"Safe soak-off removal of existing gel polish, including aftercare oil treatment.","price":10.00,"duration_minutes":20,"minimum_age_required":0}'
|
||||
'{"name":"Nail Art Add-on","description":"Custom nail art per two fingers — French tips, florals, gems, and more.","price":5.00,"duration_minutes":15,"minimum_age_required":0}'
|
||||
'{"name":"Gel Polish Full Set","description":"Full gel polish application over natural nails. Requires patch test 24h before first appointment.","price":45.00,"duration_minutes":60,"minimum_age_required":0}'
|
||||
'{"name":"Luxury Gel Manicure","description":"Premium gel polish with extended hand massage and cuticle treatment. Requires patch test 24h before first appointment.","price":55.00,"duration_minutes":75,"minimum_age_required":0}'
|
||||
'{"name":"Acrylic Full Set","description":"Full set of acrylic extensions, shaped and polished to your preference.","price":55.00,"duration_minutes":90,"minimum_age_required":16}'
|
||||
'{"name":"Acrylic Infill","description":"Maintenance infill for existing acrylic extensions.","price":35.00,"duration_minutes":60,"minimum_age_required":16}'
|
||||
'{"name":"Paraffin Wax Treatment","description":"Deeply moisturising paraffin wax hand or foot treatment, great as an add-on.","price":12.00,"duration_minutes":20,"minimum_age_required":0}'
|
||||
'{"name":"Bridal Nail Package","description":"Luxury manicure and pedicure with nail art, paraffin wax, and extended massage for the big day.","price":120.00,"duration_minutes":150,"minimum_age_required":0}'
|
||||
)
|
||||
|
||||
SERVICE_IDS=()
|
||||
@@ -316,52 +380,64 @@ total=${#SERVICES[@]}
|
||||
for svc in "${SERVICES[@]}"; do
|
||||
NAME=$(echo "$svc" | grep -o '"name":"[^"]*' | cut -d'"' -f4)
|
||||
ID=$(api_post "$BASE_URL/admin/services" "$svc" "Create $NAME" "$ADMIN_TOKEN")
|
||||
if [[ -n "$ID" ]]; then
|
||||
ID=$(echo "$ID" | tr -cd '[:alnum:]-')
|
||||
if [[ -n "$ID" && "$ID" =~ ^[0-9a-f-]{8,}$ ]]; then
|
||||
SERVICE_IDS+=("$ID")
|
||||
success=$((success+1))
|
||||
fi
|
||||
done
|
||||
echo "${C_GREEN}✅ Created $success/$total Services${C_RESET}"
|
||||
|
||||
# 3b. Create Patch Tests (for gel services that require testing)
|
||||
get_svc() { echo "${SERVICE_IDS[$1]}"; }
|
||||
|
||||
# ===========================================================================
|
||||
# 3b. CREATE PATCH TESTS (seeded directly via SQL — no creation API endpoint)
|
||||
# ===========================================================================
|
||||
echo -e "\n${C_BLUE}🧪 Creating Patch Tests...${C_RESET}"
|
||||
|
||||
# Get the gel service IDs (indices 6 and 7)
|
||||
GEL_SERVICE_IDS=("${SERVICE_IDS[6]}" "${SERVICE_IDS[7]}")
|
||||
# Gel Allergy Test covers: Gel Polish Full Set (idx 6), Luxury Gel Manicure (idx 7)
|
||||
GEL_SVC_1="${SERVICE_IDS[6]}"
|
||||
GEL_SVC_2="${SERVICE_IDS[7]}"
|
||||
|
||||
# Create patch test linking to gel services
|
||||
docker exec postgres psql -U myuser -d mydb -c "
|
||||
INSERT INTO patch_tests (name, description, notice_duration_hours, expiry_months, service_ids)
|
||||
VALUES (
|
||||
'Gel Allergy Test',
|
||||
'Patch test for gel polish products - must be completed 24h before first gel service',
|
||||
24,
|
||||
6,
|
||||
ARRAY['${GEL_SERVICE_IDS[0]}', '${GEL_SERVICE_IDS[1]}']
|
||||
);
|
||||
" > /dev/null 2>&1
|
||||
# Insert patch test directly via SQL (no creation API endpoint exists)
|
||||
# service_ids cast explicitly to uuid[] to handle typed columns
|
||||
PATCH_TEST_RESULT=$(docker exec postgres psql -U myuser -d mydb -tAc \
|
||||
"INSERT INTO patch_tests (name, description, notice_duration_hours, expiry_months, service_ids) VALUES ('Gel Allergy Test', 'Mandatory patch test for all gel polish services. Must be completed at least 24 hours before your first gel appointment.', 24, 6, ARRAY['${GEL_SVC_1}','${GEL_SVC_2}']) RETURNING id;" 2>&1)
|
||||
PATCH_TEST_ID=$(echo "$PATCH_TEST_RESULT" | head -1 | tr -d '\r\t ')
|
||||
|
||||
echo "${C_GREEN}✅ Created patch test for gel services${C_RESET}"
|
||||
# Validate it looks like a UUID; print raw output if not to help diagnose
|
||||
if [[ -z "$PATCH_TEST_ID" || ! "$PATCH_TEST_ID" =~ ^[0-9a-f-]{8,}$ ]]; then
|
||||
echo "${C_YELLOW}⚠️ Patch test SQL failed. Raw output: $PATCH_TEST_RESULT${C_RESET}"
|
||||
PATCH_TEST_ID=""
|
||||
fi
|
||||
|
||||
# 4. Create Bookings
|
||||
# Record that Grace has already completed her patch test via the API
|
||||
if [[ -n "$PATCH_TEST_ID" && -n "$GRACE_ID" ]]; then
|
||||
api_post "$BASE_URL/admin/users/$GRACE_ID/patch-tests" \
|
||||
"{\"patch_test_id\":\"$PATCH_TEST_ID\"}" \
|
||||
"Record patch test for Grace" "$ADMIN_TOKEN" > /dev/null
|
||||
echo "${C_GREEN}✅ Patch test seeded (SQL) and recorded for Grace Fletcher${C_RESET}"
|
||||
else
|
||||
echo "${C_YELLOW}⚠️ Patch test seeding failed (check DB connection or patch_tests table)${C_RESET}"
|
||||
fi
|
||||
|
||||
# ===========================================================================
|
||||
# 4. BOOKINGS
|
||||
# ===========================================================================
|
||||
echo -e "\n${C_BLUE}📅 Creating Bookings...${C_RESET}"
|
||||
|
||||
format_london_time() {
|
||||
TZ=Europe/London date -d "$1 $2" +"%Y-%m-%dT%H:%M:%S%:z"
|
||||
}
|
||||
|
||||
# Create a booking as a regular user, capture LAST_BOOKING_ID
|
||||
create_booking() {
|
||||
local token=$1 time=$2 services=$3 notes=$4 name=$5
|
||||
local json="{\"start_time\":\"$time\",\"service_ids\":$services"
|
||||
[[ -n "$notes" ]] && json="$json,\"notes\":\"$notes\""
|
||||
json="$json}"
|
||||
|
||||
# Call API and capture ID
|
||||
local id=$(api_post "$BASE_URL/bookings" "$json" "Book: $name" "$token")
|
||||
|
||||
if [[ -n "$id" ]]; then
|
||||
id=$(echo "$id" | tr -cd '[:alnum:]-')
|
||||
if [[ -n "$id" && "$id" =~ ^[0-9a-f-]{8,}$ ]]; then
|
||||
LAST_BOOKING_ID="$id"
|
||||
return 0
|
||||
else
|
||||
@@ -370,186 +446,441 @@ create_booking() {
|
||||
fi
|
||||
}
|
||||
|
||||
get_svc() { echo "${SERVICE_IDS[$1]}"; }
|
||||
# Create a booking as admin (no advance restriction, always confirmed)
|
||||
create_admin_booking() {
|
||||
local user_id=$1 time=$2 services=$3 notes=$4 name=$5
|
||||
local json="{\"user_id\":\"$user_id\",\"start_time\":\"$time\",\"service_ids\":$services,\"service_overrides\":[],\"enforce_deposits\":false"
|
||||
[[ -n "$notes" ]] && json="$json,\"notes\":\"$notes\""
|
||||
json="$json}"
|
||||
|
||||
# Counters
|
||||
local id=$(api_post "$BASE_URL/admin/bookings" "$json" "Admin Book: $name" "$ADMIN_TOKEN")
|
||||
if [[ -n "$id" && "$id" =~ ^[0-9a-f-]{8,}$ ]]; then
|
||||
LAST_BOOKING_ID="$id"
|
||||
return 0
|
||||
else
|
||||
LAST_BOOKING_ID=""
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Returns the nearest OPEN business day at or after the given date.
|
||||
# Schema: Monday=0 closed, Sunday=6 closed. Tue-Sat open.
|
||||
# dow: date's day-of-week as 0=Sun,1=Mon,...,6=Sat (GNU date %w)
|
||||
open_day() {
|
||||
local d="$1"
|
||||
local max=7
|
||||
for ((i=0; i<max; i++)); do
|
||||
local dow
|
||||
dow=$(TZ=Europe/London date -d "$d" +%w) # 0=Sun, 1=Mon, 6=Sat
|
||||
# Closed: Sunday (0) or Monday (1)
|
||||
if [[ "$dow" == "0" || "$dow" == "1" ]]; then
|
||||
d=$(TZ=Europe/London date -d "$d +1 day" +%Y-%m-%d)
|
||||
else
|
||||
echo "$d"
|
||||
return
|
||||
fi
|
||||
done
|
||||
echo "$d" # fallback
|
||||
}
|
||||
|
||||
# Same but shifts BACKWARDS to find an open day (for past bookings)
|
||||
open_day_past() {
|
||||
local d="$1"
|
||||
local max=7
|
||||
for ((i=0; i<max; i++)); do
|
||||
local dow
|
||||
dow=$(TZ=Europe/London date -d "$d" +%w)
|
||||
if [[ "$dow" == "0" || "$dow" == "1" ]]; then
|
||||
d=$(TZ=Europe/London date -d "$d -1 day" +%Y-%m-%d)
|
||||
else
|
||||
echo "$d"
|
||||
return
|
||||
fi
|
||||
done
|
||||
echo "$d"
|
||||
}
|
||||
|
||||
TODAY=$(TZ=Europe/London date +%Y-%m-%d)
|
||||
TOMORROW=$(open_day "$(TZ=Europe/London date -d "tomorrow" +%Y-%m-%d)")
|
||||
|
||||
# Arrays to collect upcoming booking IDs that need notes confirmed
|
||||
PENDING_BOOKING_IDS=()
|
||||
PENDING_BOOKING_NAMES=()
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# PAST BOOKINGS — all via admin (bypasses past-time rejection)
|
||||
# ---------------------------------------------------------------------------
|
||||
echo -e "\n${C_YELLOW}📅 Creating Past Bookings...${C_RESET}"
|
||||
count_past=0
|
||||
|
||||
# Emma — loyal weekly gel client
|
||||
for day_offset in 7 14 21 28; do
|
||||
D=$(open_day_past "$(TZ=Europe/London date -d "today -$day_offset days" +%Y-%m-%d)")
|
||||
if create_admin_booking "$EMMA_ID" "$(format_london_time "$D" "$SLOT_A")" "[\"$(get_svc 1)\"]" "" "Emma - Gel Manicure $D"; then count_past=$((count_past+1)); fi
|
||||
done
|
||||
|
||||
# Sophie — pedicure every few weeks
|
||||
for day_offset in 6 20; do
|
||||
D=$(open_day_past "$(TZ=Europe/London date -d "today -$day_offset days" +%Y-%m-%d)")
|
||||
if create_admin_booking "$SOPHIE_ID" "$(format_london_time "$D" "$SLOT_C")" "[\"$(get_svc 2)\"]" "" "Sophie - Luxury Pedicure $D"; then count_past=$((count_past+1)); fi
|
||||
done
|
||||
|
||||
# Amelia — classic manicure regular
|
||||
for day_offset in 5 19; do
|
||||
D=$(open_day_past "$(TZ=Europe/London date -d "today -$day_offset days" +%Y-%m-%d)")
|
||||
if create_admin_booking "$AMELIA_ID" "$(format_london_time "$D" "$SLOT_B")" "[\"$(get_svc 0)\"]" "" "Amelia - Classic Manicure $D"; then count_past=$((count_past+1)); fi
|
||||
done
|
||||
|
||||
# Isla — mixed services
|
||||
D=$(open_day_past "$(TZ=Europe/London date -d "today -3 days" +%Y-%m-%d)")
|
||||
if create_admin_booking "$ISLA_ID" "$(format_london_time "$D" "$SLOT_A")" "[\"$(get_svc 3)\"]" "" "Isla - Express Mani & Pedi"; then count_past=$((count_past+1)); fi
|
||||
D=$(open_day_past "$(TZ=Europe/London date -d "today -17 days" +%Y-%m-%d)")
|
||||
if create_admin_booking "$ISLA_ID" "$(format_london_time "$D" "$SLOT_C")" "[\"$(get_svc 0)\",\"$(get_svc 5)\"]" "" "Isla - Classic + Nail Art"; then count_past=$((count_past+1)); fi
|
||||
|
||||
# Lily — gel with nail art combo
|
||||
D=$(open_day_past "$(TZ=Europe/London date -d "today -10 days" +%Y-%m-%d)")
|
||||
if create_admin_booking "$LILY_ID" "$(format_london_time "$D" "$SLOT_D")" "[\"$(get_svc 1)\",\"$(get_svc 5)\"]" "" "Lily - Gel + Nail Art"; then count_past=$((count_past+1)); fi
|
||||
|
||||
# Ava — occasional pedicure
|
||||
D=$(open_day_past "$(TZ=Europe/London date -d "today -12 days" +%Y-%m-%d)")
|
||||
if create_admin_booking "$AVA_ID" "$(format_london_time "$D" "$SLOT_B")" "[\"$(get_svc 2)\"]" "" "Ava - Luxury Pedicure"; then count_past=$((count_past+1)); fi
|
||||
|
||||
# Grace — gel client (patch test already recorded)
|
||||
D=$(open_day_past "$(TZ=Europe/London date -d "today -9 days" +%Y-%m-%d)")
|
||||
if create_admin_booking "$GRACE_ID" "$(format_london_time "$D" "$SLOT_A")" "[\"$(get_svc 6)\"]" "" "Grace - Gel Full Set"; then count_past=$((count_past+1)); fi
|
||||
D=$(open_day_past "$(TZ=Europe/London date -d "today -23 days" +%Y-%m-%d)")
|
||||
if create_admin_booking "$GRACE_ID" "$(format_london_time "$D" "$SLOT_A")" "[\"$(get_svc 7)\"]" "" "Grace - Luxury Gel Manicure"; then count_past=$((count_past+1)); fi
|
||||
|
||||
# Primary test user — variety of past bookings
|
||||
for day_offset in 2 4 8 11 15; do
|
||||
D=$(open_day_past "$(TZ=Europe/London date -d "today -$day_offset days" +%Y-%m-%d)")
|
||||
SVC_IDX=$(( (day_offset % 4) ))
|
||||
if create_admin_booking "$USER_USER_ID" "$(format_london_time "$D" "$SLOT_C")" "[\"$(get_svc $SVC_IDX)\"]" "" "User - Past booking $D"; then count_past=$((count_past+1)); fi
|
||||
done
|
||||
|
||||
# Poppy / Mia — deposit-required past visits
|
||||
D=$(open_day_past "$(TZ=Europe/London date -d "today -5 days" +%Y-%m-%d)")
|
||||
if create_admin_booking "$POPPY_ID" "$(format_london_time "$D" "$SLOT_C")" "[\"$(get_svc 0)\"]" "" "Poppy - Past (deposit snapshotted)"; then count_past=$((count_past+1)); fi
|
||||
D=$(open_day_past "$(TZ=Europe/London date -d "today -13 days" +%Y-%m-%d)")
|
||||
if create_admin_booking "$MIA_ID" "$(format_london_time "$D" "$SLOT_D")" "[\"$(get_svc 2)\"]" "" "Mia - Past"; then count_past=$((count_past+1)); fi
|
||||
|
||||
echo "${C_GREEN}✅ Created $count_past Past Bookings${C_RESET}"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# TODAY'S BOOKINGS — all via admin (bypass 1-hour advance)
|
||||
# ---------------------------------------------------------------------------
|
||||
echo -e "\n${C_YELLOW}📅 Creating Today's Bookings...${C_RESET}"
|
||||
count_today=0
|
||||
|
||||
if create_admin_booking "$EMMA_ID" "$(format_london_time "$TODAY" "$SLOT_A")" "[\"$(get_svc 1)\"]" "" "Emma - Gel Manicure (today)"; then count_today=$((count_today+1)); fi
|
||||
if create_admin_booking "$SOPHIE_ID" "$(format_london_time "$TODAY" "$SLOT_B")" "[\"$(get_svc 2)\"]" "" "Sophie - Luxury Pedicure (today)"; then count_today=$((count_today+1)); fi
|
||||
if create_admin_booking "$AMELIA_ID" "$(format_london_time "$TODAY" "$SLOT_C")" "[\"$(get_svc 0)\",\"$(get_svc 5)\"]" "" "Amelia - Classic + Nail Art (today)"; then count_today=$((count_today+1)); fi
|
||||
if create_admin_booking "$USER_USER_ID" "$(format_london_time "$TODAY" "$SLOT_D")" "[\"$(get_svc 3)\"]" "" "User - Express Mani & Pedi (today)"; then count_today=$((count_today+1)); fi
|
||||
if create_admin_booking "$ISLA_ID" "$(format_london_time "$TODAY" "$SLOT_A")" "[\"$(get_svc 1)\"]" "" "Isla - Gel Manicure (today)"; then count_today=$((count_today+1)); fi
|
||||
if create_admin_booking "$GRACE_ID" "$(format_london_time "$TODAY" "$SLOT_D")" "[\"$(get_svc 6)\"]" "" "Grace - Gel Full Set (today)"; then count_today=$((count_today+1)); fi
|
||||
if create_admin_booking "$LILY_ID" "$(format_london_time "$TODAY" "$SLOT_C")" "[\"$(get_svc 0)\"]" "" "Lily - Classic Manicure (today)"; then count_today=$((count_today+1)); fi
|
||||
|
||||
echo "${C_GREEN}✅ Created $count_today Today's Bookings${C_RESET}"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# TOMORROW'S BOOKINGS — all via admin (avoids booking_status enum bug in
|
||||
# POST /bookings). We simulate the pending/notes scenario by creating 2
|
||||
# bookings via user tokens (which intentionally hit the notes path) and
|
||||
# leaving them unconfirmed for admin review.
|
||||
# ---------------------------------------------------------------------------
|
||||
echo -e "\n${C_YELLOW}📅 Creating Tomorrow's Bookings...${C_RESET}"
|
||||
count_tomorrow=0
|
||||
|
||||
# Bulk of tomorrow's bookings — admin-created, all confirmed
|
||||
if create_admin_booking "$EMMA_ID" "$(format_london_time "$TOMORROW" "$SLOT_A")" "[\"$(get_svc 1)\"]" "" "Emma - Gel Manicure (tomorrow)"; then count_tomorrow=$((count_tomorrow+1)); fi
|
||||
if create_admin_booking "$SOPHIE_ID" "$(format_london_time "$TOMORROW" "$SLOT_C")" "[\"$(get_svc 2)\"]" "" "Sophie - Luxury Pedicure (tomorrow)"; then count_tomorrow=$((count_tomorrow+1)); fi
|
||||
if create_admin_booking "$AMELIA_ID" "$(format_london_time "$TOMORROW" "$SLOT_D")" "[\"$(get_svc 3)\"]" "" "Amelia - Express Mani & Pedi (tomorrow)"; then count_tomorrow=$((count_tomorrow+1)); fi
|
||||
if create_admin_booking "$USER_USER_ID" "$(format_london_time "$TOMORROW" "$SLOT_B")" "[\"$(get_svc 0)\"]" "" "User - Classic Manicure (tomorrow)"; then count_tomorrow=$((count_tomorrow+1)); fi
|
||||
if create_admin_booking "$LILY_ID" "$(format_london_time "$TOMORROW" "$SLOT_C")" "[\"$(get_svc 2)\"]" "" "Lily - Luxury Pedicure (tomorrow)"; then count_tomorrow=$((count_tomorrow+1)); fi
|
||||
|
||||
# Pending bookings — admin-created (avoids enum bug), then force to pending via DB.
|
||||
# Notes are included so the admin UI shows the request context.
|
||||
if create_admin_booking "$ISLA_ID" "$(format_london_time "$TOMORROW" "$SLOT_E")" "[\"$(get_svc 1)\",\"$(get_svc 5)\"]" "Would love a French tip look with some floral art on the ring fingers if possible?" "Isla - Gel + Nail Art (tomorrow, pending)"; then
|
||||
count_tomorrow=$((count_tomorrow+1))
|
||||
if [[ -n "$LAST_BOOKING_ID" ]]; then
|
||||
docker exec postgres psql -U myuser -d mydb -c "UPDATE bookings SET status = 'pending' WHERE id = '${LAST_BOOKING_ID}'" > /dev/null 2>&1
|
||||
PENDING_BOOKING_IDS+=("$LAST_BOOKING_ID"); PENDING_BOOKING_NAMES+=("Isla - Gel + Nail Art (pending)")
|
||||
fi
|
||||
fi
|
||||
if create_admin_booking "$AVA_ID" "$(format_london_time "$TOMORROW" "$SLOT_E")" "[\"$(get_svc 0)\"]" "Could I get a specific nail shape — stiletto if possible?" "Ava - Classic Manicure (tomorrow, pending)"; then
|
||||
count_tomorrow=$((count_tomorrow+1))
|
||||
if [[ -n "$LAST_BOOKING_ID" ]]; then
|
||||
docker exec postgres psql -U myuser -d mydb -c "UPDATE bookings SET status = 'pending' WHERE id = '${LAST_BOOKING_ID}'" > /dev/null 2>&1
|
||||
PENDING_BOOKING_IDS+=("$LAST_BOOKING_ID"); PENDING_BOOKING_NAMES+=("Ava - Classic Manicure (pending)")
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "${C_GREEN}✅ Created $count_tomorrow Tomorrow's Bookings${C_RESET}"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# UPCOMING — next 14 days, all via admin to avoid booking_status enum bug.
|
||||
# Bridal package uses user token (notes → pending) as the one exception.
|
||||
# ---------------------------------------------------------------------------
|
||||
echo -e "\n${C_YELLOW}📅 Creating Upcoming Bookings (next 14 days)...${C_RESET}"
|
||||
count_future=0
|
||||
|
||||
# Array to hold IDs of upcoming bookings for confirmation step
|
||||
UPCOMING_BOOKING_IDS=()
|
||||
UPCOMING_BOOKING_NAMES=()
|
||||
UP_IDS=( "$EMMA_ID" "$SOPHIE_ID" "$AMELIA_ID" "$ISLA_ID" "$LILY_ID" "$AVA_ID" "$GRACE_ID" "$USER_USER_ID" )
|
||||
UP_SVCS=( "1" "2" "0" "3" "1" "2" "7" "0" )
|
||||
UP_SLOTS=( "$SLOT_A" "$SLOT_C" "$SLOT_D" "$SLOT_B" "$SLOT_C" "$SLOT_A" "$SLOT_D" "$SLOT_B" )
|
||||
UP_NAMES=( "Gel Manicure" "Luxury Pedicure" "Classic Manicure" "Express Mani & Pedi" "Gel Manicure" "Luxury Pedicure" "Luxury Gel Manicure" "Classic Manicure" )
|
||||
|
||||
# --- PAST BOOKINGS (8 Total) ---
|
||||
echo -e "\n${C_YELLOW}📅 Creating 8 Past Bookings (Last 8 days)...${C_RESET}"
|
||||
for day_offset in {1..8}; do
|
||||
PAST_DATE=$(TZ=Europe/London date -d "today -$day_offset days" +%Y-%m-%d)
|
||||
for day_offset in {2..15}; do
|
||||
RAW_DATE=$(TZ=Europe/London date -d "$TODAY +$day_offset days" +%Y-%m-%d)
|
||||
FUTURE_DATE=$(open_day "$RAW_DATE")
|
||||
idx=$(( (day_offset - 2) % ${#UP_IDS[@]} ))
|
||||
uid="${UP_IDS[$idx]}"
|
||||
svc_idx="${UP_SVCS[$idx]}"
|
||||
slot="${UP_SLOTS[$idx]}"
|
||||
label="${UP_NAMES[$idx]}"
|
||||
|
||||
# Alternate times
|
||||
if [ $((day_offset % 2)) -eq 0 ]; then
|
||||
TIME="14:00:00"
|
||||
else
|
||||
TIME="10:00:00"
|
||||
fi
|
||||
|
||||
# Alternate services
|
||||
SVC_IDX=$(( (day_offset % 2) ))
|
||||
NAME="Past ($PAST_DATE) - $(echo "${SERVICES[$SVC_IDX]}" | grep -o '"name":"[^"]*' | cut -d'"' -f4)"
|
||||
|
||||
if create_booking "$USER_TOKEN" "$(format_london_time "$PAST_DATE" "$TIME")" "[\"$(get_svc $SVC_IDX)\"]" "" "$NAME"; then
|
||||
count_past=$((count_past+1))
|
||||
fi
|
||||
done
|
||||
echo "${C_GREEN}✅ Created $count_past/8 Past Bookings${C_RESET}"
|
||||
|
||||
# --- TODAY (3) ---
|
||||
TODAY=$(TZ=Europe/London date +%Y-%m-%d)
|
||||
TOMORROW=$(TZ=Europe/London date -d "tomorrow" +%Y-%m-%d)
|
||||
|
||||
if create_booking "$USER_TOKEN" "$(format_london_time "$TODAY" "09:30:00")" "[\"$(get_svc 0)\"]" "" "Today - Classic Manicure"; then
|
||||
count_today=$((count_today+1)); UPCOMING_BOOKING_IDS+=("$LAST_BOOKING_ID"); UPCOMING_BOOKING_NAMES+=("Today - Classic Manicure");
|
||||
fi
|
||||
if create_booking "$USER_TOKEN" "$(format_london_time "$TODAY" "11:30:00")" "[\"$(get_svc 1)\"]" "" "Today - Gel Manicure"; then
|
||||
count_today=$((count_today+1)); UPCOMING_BOOKING_IDS+=("$LAST_BOOKING_ID"); UPCOMING_BOOKING_NAMES+=("Today - Gel Manicure");
|
||||
fi
|
||||
if create_booking "$USER_TOKEN" "$(format_london_time "$TODAY" "14:00:00")" "[\"$(get_svc 2)\"]" "" "Today - Luxury Pedicure"; then
|
||||
count_today=$((count_today+1)); UPCOMING_BOOKING_IDS+=("$LAST_BOOKING_ID"); UPCOMING_BOOKING_NAMES+=("Today - Luxury Pedicure");
|
||||
fi
|
||||
|
||||
# --- TOMORROW (4) ---
|
||||
if create_booking "$USER_TOKEN" "$(format_london_time "$TOMORROW" "09:00:00")" "[\"$(get_svc 3)\"]" "" "Tomorrow - Express Mani & Pedi"; then
|
||||
count_tomorrow=$((count_tomorrow+1)); UPCOMING_BOOKING_IDS+=("$LAST_BOOKING_ID"); UPCOMING_BOOKING_NAMES+=("Tomorrow - Express Mani & Pedi");
|
||||
fi
|
||||
if create_booking "$USER_TOKEN" "$(format_london_time "$TOMORROW" "10:30:00")" "[\"$(get_svc 0)\",\"$(get_svc 5)\"]" "" "Tomorrow - Classic + Nail Art"; then
|
||||
count_tomorrow=$((count_tomorrow+1)); UPCOMING_BOOKING_IDS+=("$LAST_BOOKING_ID"); UPCOMING_BOOKING_NAMES+=("Tomorrow - Classic + Nail Art");
|
||||
fi
|
||||
if create_booking "$USER_TOKEN" "$(format_london_time "$TOMORROW" "13:00:00")" "[\"$(get_svc 1)\"]" "" "Tomorrow - Gel Manicure"; then
|
||||
count_tomorrow=$((count_tomorrow+1)); UPCOMING_BOOKING_IDS+=("$LAST_BOOKING_ID"); UPCOMING_BOOKING_NAMES+=("Tomorrow - Gel Manicure");
|
||||
fi
|
||||
if create_booking "$USER_TOKEN" "$(format_london_time "$TOMORROW" "15:30:00")" "[\"$(get_svc 2)\"]" "" "Tomorrow - Luxury Pedicure"; then
|
||||
count_tomorrow=$((count_tomorrow+1)); UPCOMING_BOOKING_IDS+=("$LAST_BOOKING_ID"); UPCOMING_BOOKING_NAMES+=("Tomorrow - Luxury Pedicure");
|
||||
fi
|
||||
|
||||
# --- FUTURE (30) ---
|
||||
# Spread over the next 15 days (Day +2 to Day +16)
|
||||
for day_offset in {2..16}; do
|
||||
FUTURE_DATE=$(TZ=Europe/London date -d "$TODAY +$day_offset days" +%Y-%m-%d)
|
||||
|
||||
# Morning Slot (10:00)
|
||||
if create_booking "$USER_TOKEN" "$(format_london_time "$FUTURE_DATE" "10:00:00")" "[\"$(get_svc 0)\"]" "" "Future ($FUTURE_DATE) - Classic Manicure"; then
|
||||
count_future=$((count_future+1)); UPCOMING_BOOKING_IDS+=("$LAST_BOOKING_ID"); UPCOMING_BOOKING_NAMES+=("Future ($FUTURE_DATE) AM");
|
||||
fi
|
||||
|
||||
# Afternoon Slot (14:30)
|
||||
if create_booking "$USER_TOKEN" "$(format_london_time "$FUTURE_DATE" "14:30:00")" "[\"$(get_svc 1)\"]" "" "Future ($FUTURE_DATE) - Gel Manicure"; then
|
||||
count_future=$((count_future+1)); UPCOMING_BOOKING_IDS+=("$LAST_BOOKING_ID"); UPCOMING_BOOKING_NAMES+=("Future ($FUTURE_DATE) PM");
|
||||
if create_admin_booking "$uid" "$(format_london_time "$FUTURE_DATE" "$slot")" "[\"$(get_svc $svc_idx)\"]" "" "$label ($FUTURE_DATE)"; then
|
||||
count_future=$((count_future+1))
|
||||
# NOTE: do NOT add admin bookings to PENDING_BOOKING_IDS — they're already confirmed
|
||||
fi
|
||||
done
|
||||
|
||||
# Summary
|
||||
TOTAL=$((count_today + count_tomorrow + count_future + count_past))
|
||||
echo "${C_GREEN}✅ Created $count_today/3 Bookings (Today)${C_RESET}"
|
||||
echo "${C_GREEN}✅ Created $count_tomorrow/4 Bookings (Tomorrow)${C_RESET}"
|
||||
echo "${C_GREEN}✅ Created $count_future/30 Bookings (Future)${C_RESET}"
|
||||
echo "${C_GREEN}✅ Created $count_past/8 Bookings (Past)${C_RESET}"
|
||||
echo "${C_GREEN}✅ Created $TOTAL/45 Bookings (Total)${C_RESET}"
|
||||
|
||||
# 4b. Create Booking for Deposit-Required User
|
||||
echo -e "\n${C_BLUE}💰 Creating Booking for Deposit-Required User...${C_RESET}"
|
||||
|
||||
# Login as Poppy (deposits_required=3)
|
||||
POPPY_LOGIN_RESP=$(curl -s -X POST -H 'Content-Type: application/json' -d '{"email":"poppy.thompson@example.com","password":"password"}' "$BASE_URL/login")
|
||||
POPPY_TOKEN=$(echo "$POPPY_LOGIN_RESP" | tr -d '\r\n\t ' | sed -n 's/.*"token":"\([^"]*\).*/\1/p')
|
||||
|
||||
if [[ -n "$POPPY_TOKEN" ]]; then
|
||||
# Book 3 days ahead (50h+ to satisfy 48h requirement for deposit-required users)
|
||||
DEPOSIT_TIME=$(TZ=Europe/London date -d "3 days 10:00" +"%Y-%m-%dT%H:%M:%S%:z")
|
||||
if create_booking "$POPPY_TOKEN" "$DEPOSIT_TIME" "[\"$(get_svc 0)\"]" "" "Poppy (deposit required)"; then
|
||||
echo "${C_GREEN}✅ Created deposit-required booking (deposit_required=true snapshotted)${C_RESET}"
|
||||
else
|
||||
echo "${C_YELLOW}⚠️ Could not create deposit-required booking${C_RESET}"
|
||||
fi
|
||||
else
|
||||
echo "${C_YELLOW}⚠️ Could not login as Poppy to create deposit-required booking${C_RESET}"
|
||||
# Multi-service admin bookings for variety
|
||||
D2=$(open_day "$(TZ=Europe/London date -d "$TODAY +3 days" +%Y-%m-%d)")
|
||||
if create_admin_booking "$USER_USER_ID" "$(format_london_time "$D2" "$SLOT_B")" "[\"$(get_svc 0)\",\"$(get_svc 5)\"]" "" "User - Classic + Nail Art (+3 days)"; then
|
||||
count_future=$((count_future+1))
|
||||
fi
|
||||
|
||||
# 5. Confirm Random Half of Upcoming Bookings
|
||||
echo -e "\n${C_BLUE}🔒 Confirming Random Upcoming Bookings...${C_RESET}"
|
||||
D3=$(open_day "$(TZ=Europe/London date -d "$TODAY +6 days" +%Y-%m-%d)")
|
||||
if create_admin_booking "$EMMA_ID" "$(format_london_time "$D3" "$SLOT_A")" "[\"$(get_svc 1)\",\"$(get_svc 4)\"]" "" "Emma - Gel Manicure + Removal (+6 days)"; then
|
||||
count_future=$((count_future+1))
|
||||
fi
|
||||
|
||||
# Bridal package — admin-created with notes, then forced to pending via DB.
|
||||
D4=$(open_day "$(TZ=Europe/London date -d "$TODAY +12 days" +%Y-%m-%d)")
|
||||
if create_admin_booking "$SOPHIE_ID" "$(format_london_time "$D4" "$SLOT_D")" "[\"$(get_svc 11)\"]" "Bride-to-be — please can we discuss nail art options beforehand?" "Sophie - Bridal Package (+12 days, pending)"; then
|
||||
count_future=$((count_future+1))
|
||||
if [[ -n "$LAST_BOOKING_ID" ]]; then
|
||||
docker exec postgres psql -U myuser -d mydb -c "UPDATE bookings SET status = 'pending' WHERE id = '${LAST_BOOKING_ID}'" > /dev/null 2>&1
|
||||
PENDING_BOOKING_IDS+=("$LAST_BOOKING_ID"); PENDING_BOOKING_NAMES+=("Sophie - Bridal Package (pending)")
|
||||
fi
|
||||
fi
|
||||
|
||||
# Deposit-required user — admin-created to bypass deposit check
|
||||
D5=$(open_day "$(TZ=Europe/London date -d "$TODAY +4 days" +%Y-%m-%d)")
|
||||
if create_admin_booking "$POPPY_ID" "$(format_london_time "$D5" "$SLOT_C")" "[\"$(get_svc 0)\"]" "" "Poppy - Future (deposit snapshotted)"; then
|
||||
count_future=$((count_future+1))
|
||||
fi
|
||||
|
||||
echo "${C_GREEN}✅ Created $count_future Upcoming Bookings${C_RESET}"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# CONFIRM roughly 2/3 of pending bookings, leave ~1/3 for admin review
|
||||
# ---------------------------------------------------------------------------
|
||||
echo -e "\n${C_BLUE}🔒 Confirming Upcoming Bookings (approx. 2/3)...${C_RESET}"
|
||||
confirmed_count=0
|
||||
rejected_count=0
|
||||
attempted_count=0
|
||||
total_upcoming=${#UPCOMING_BOOKING_IDS[@]}
|
||||
|
||||
# Small pause to ensure backend is ready after bulk creation
|
||||
skipped_count=0
|
||||
sleep 1
|
||||
|
||||
for ((i=0; i<${#UPCOMING_BOOKING_IDS[@]}; i++)); do
|
||||
# FIX 3: Sanitize ID again just before use to ensure no hidden characters broke the array
|
||||
id="${UPCOMING_BOOKING_IDS[$i]}"
|
||||
name="${UPCOMING_BOOKING_NAMES[$i]}"
|
||||
for ((i=0; i<${#PENDING_BOOKING_IDS[@]}; i++)); do
|
||||
id="${PENDING_BOOKING_IDS[$i]}"
|
||||
[[ -z "$id" ]] && continue
|
||||
[[ ! "$id" =~ ^[0-9a-f-]{8,}$ ]] && continue
|
||||
|
||||
# Ensure ID is not empty
|
||||
if [ -z "$id" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
# Random coin flip (0 or 1). If 1, confirm.
|
||||
if [ $((RANDOM % 2)) -eq 1 ]; then
|
||||
attempted_count=$((attempted_count+1))
|
||||
# Send proper JSON with empty serviceOverrides array
|
||||
if [ $((RANDOM % 3)) -ne 0 ]; then
|
||||
RESPONSE=$(curl -s -w "\n%{http_code}" -X POST \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer $ADMIN_TOKEN" \
|
||||
-d '{"serviceOverrides":[]}' \
|
||||
-d '{"service_overrides":[],"notes":""}' \
|
||||
"$BASE_URL/admin/bookings/$id/confirm")
|
||||
HTTP_CODE=$(echo "$RESPONSE" | tail -n1)
|
||||
BODY=$(echo "$RESPONSE" | sed '$d')
|
||||
|
||||
if [ "$HTTP_CODE" = "200" ]; then
|
||||
confirmed_count=$((confirmed_count+1))
|
||||
else
|
||||
rejected_count=$((rejected_count+1))
|
||||
fi
|
||||
# Small sleep to prevent overwhelming the server
|
||||
sleep 0.1
|
||||
[ "$HTTP_CODE" = "200" ] && confirmed_count=$((confirmed_count+1))
|
||||
sleep 0.05
|
||||
else
|
||||
skipped_count=$((skipped_count+1))
|
||||
fi
|
||||
done
|
||||
echo "${C_GREEN}✅ Confirmed $confirmed_count/$attempted_count Bookings${C_RESET}"
|
||||
if [ "$rejected_count" -gt 0 ]; then
|
||||
echo "${C_YELLOW}⚠️ Rejected $rejected_count/$attempted_count (deposit/time restrictions)${C_RESET}"
|
||||
echo "${C_GREEN}✅ Confirmed $confirmed_count bookings, left $skipped_count pending for admin review${C_RESET}"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# CANCELLATIONS
|
||||
# Both POST /admin/bookings/{id}/cancel and DELETE /bookings/{id} have backend
|
||||
# bugs (enum value mismatch + FK violation). Patching status directly in DB.
|
||||
# Auto-detects the actual enum values from pg_enum to avoid hardcoding.
|
||||
# ---------------------------------------------------------------------------
|
||||
echo -e "\n${C_BLUE}🚫 Simulating Cancellations...${C_RESET}"
|
||||
cancel_count=0
|
||||
|
||||
# Discover actual booking_status enum values
|
||||
ENUM_VALUES=$(docker exec postgres psql -U myuser -d mydb -tAc \
|
||||
"SELECT string_agg(enumlabel, ',' ORDER BY enumsortorder) FROM pg_enum e JOIN pg_type t ON e.enumtypid = t.oid WHERE t.typname = 'booking_status';" 2>/dev/null | tr -d '\r\n\t ')
|
||||
echo " ℹ️ booking_status enum: $ENUM_VALUES"
|
||||
|
||||
CLIENT_CANCEL_STATUS=$(echo "$ENUM_VALUES" | tr ',' '\n' | grep -i 'client' | head -1)
|
||||
ADMIN_CANCEL_STATUS=$(echo "$ENUM_VALUES" | tr ',' '\n' | grep -iE 'we_|admin' | head -1)
|
||||
# If no distinct admin status, fall back to the first cancel-looking value
|
||||
[[ -z "$CLIENT_CANCEL_STATUS" ]] && CLIENT_CANCEL_STATUS=$(echo "$ENUM_VALUES" | tr ',' '\n' | grep -i 'cancel' | head -1)
|
||||
[[ -z "$ADMIN_CANCEL_STATUS" ]] && ADMIN_CANCEL_STATUS="$CLIENT_CANCEL_STATUS"
|
||||
echo " ℹ️ Cancel statuses — client: '$CLIENT_CANCEL_STATUS' admin: '$ADMIN_CANCEL_STATUS'"
|
||||
|
||||
db_cancel() {
|
||||
local booking_id="$1" status="$2" reason="$3"
|
||||
[[ -z "$booking_id" || -z "$status" ]] && return 1
|
||||
# Discover the cancel reason column name (varies by schema)
|
||||
local reason_col
|
||||
reason_col=$(docker exec postgres psql -U myuser -d mydb -tAc \
|
||||
"SELECT column_name FROM information_schema.columns WHERE table_name='bookings' AND column_name IN ('cancel_reason','cancellation_reason','reason') LIMIT 1;" 2>/dev/null | head -1 | tr -d '\r\t ')
|
||||
local sql
|
||||
if [[ -n "$reason_col" ]]; then
|
||||
sql="UPDATE bookings SET status = '$status', $reason_col = '$reason' WHERE id = '$booking_id' RETURNING id;"
|
||||
else
|
||||
sql="UPDATE bookings SET status = '$status' WHERE id = '$booking_id' RETURNING id;"
|
||||
fi
|
||||
local result
|
||||
result=$(docker exec postgres psql -U myuser -d mydb -tAc "$sql" 2>&1)
|
||||
local rows
|
||||
rows=$(echo "$result" | head -1 | tr -d '\r\t ')
|
||||
if [[ -n "$rows" && "$rows" =~ ^[0-9a-f-]{8,}$ ]]; then
|
||||
return 0
|
||||
else
|
||||
echo "${C_RED} SQL: $sql${C_RESET}"
|
||||
echo "${C_RED} Result: $result${C_RESET}"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
D_CANCEL=$(open_day "$(TZ=Europe/London date -d "$TODAY +5 days" +%Y-%m-%d)")
|
||||
if create_admin_booking "$AVA_ID" "$(format_london_time "$D_CANCEL" "$SLOT_B")" "[\"$(get_svc 0)\"]" "" "Ava - to be cancelled"; then
|
||||
if db_cancel "$LAST_BOOKING_ID" "$CLIENT_CANCEL_STATUS" "Something came up, really sorry!"; then
|
||||
cancel_count=$((cancel_count+1))
|
||||
else
|
||||
echo "${C_RED}❌ DB cancel failed for Ava${C_RESET}"
|
||||
fi
|
||||
fi
|
||||
|
||||
D_CANCEL2=$(open_day "$(TZ=Europe/London date -d "$TODAY +7 days" +%Y-%m-%d)")
|
||||
if create_admin_booking "$USER_USER_ID" "$(format_london_time "$D_CANCEL2" "$SLOT_C")" "[\"$(get_svc 2)\"]" "" "User - to be cancelled"; then
|
||||
if db_cancel "$LAST_BOOKING_ID" "$CLIENT_CANCEL_STATUS" "Plans changed, apologies for the late notice."; then
|
||||
cancel_count=$((cancel_count+1))
|
||||
else
|
||||
echo "${C_RED}❌ DB cancel failed for User${C_RESET}"
|
||||
fi
|
||||
fi
|
||||
|
||||
# 6. Exceptional Groups (2 Total)
|
||||
echo -e "\n${C_BLUE}🗓️ Creating Exceptional Groups...${C_RESET}"
|
||||
NOV_BREAK='{"name":"November Break","description":"Short break period in November","hours":[{"weekday":0,"startTime":"00:00:00","endTime":"00:00:00","isOpen":false},{"weekday":1,"startTime":"00:00:00","endTime":"00:00:00","isOpen":false},{"weekday":2,"startTime":"00:00:00","endTime":"00:00:00","isOpen":false},{"weekday":3,"startTime":"00:00:00","endTime":"00:00:00","isOpen":false},{"weekday":4,"startTime":"00:00:00","endTime":"00:00:00","isOpen":false},{"weekday":5,"startTime":"00:00:00","endTime":"00:00:00","isOpen":false},{"weekday":6,"startTime":"00:00:00","endTime":"00:00:00","isOpen":false}],"weekStarts":["2025-11-10"]}'
|
||||
XMAS_BREAK='{"name":"Christmas Holiday Period","description":"Reduced hours for Christmas and New Year","hours":[{"weekday":0,"startTime":"00:00:00","endTime":"00:00:00","isOpen":false},{"weekday":1,"startTime":"10:00:00","endTime":"15:00:00","isOpen":true},{"weekday":2,"startTime":"10:00:00","endTime":"15:00:00","isOpen":true},{"weekday":3,"startTime":"00:00:00","endTime":"00:00:00","isOpen":false},{"weekday":4,"startTime":"00:00:00","endTime":"00:00:00","isOpen":false},{"weekday":5,"startTime":"00:00:00","endTime":"00:00:00","isOpen":false},{"weekday":6,"startTime":"00:00:00","endTime":"00:00:00","isOpen":false}],"weekStarts":["2025-12-22","2025-12-29"]}'
|
||||
D_ADMIN_CANCEL=$(open_day "$(TZ=Europe/London date -d "$TODAY +9 days" +%Y-%m-%d)")
|
||||
if create_admin_booking "$NOAH_ID" "$(format_london_time "$D_ADMIN_CANCEL" "$SLOT_B")" "[\"$(get_svc 1)\"]" "" "Noah - to be admin-cancelled"; then
|
||||
if db_cancel "$LAST_BOOKING_ID" "$ADMIN_CANCEL_STATUS" "Slot no longer available due to schedule change."; then
|
||||
cancel_count=$((cancel_count+1))
|
||||
else
|
||||
echo "${C_RED}❌ DB cancel failed for Noah${C_RESET}"
|
||||
fi
|
||||
fi
|
||||
|
||||
success=0
|
||||
total=2
|
||||
if api_post "$BASE_URL/scheduling/exceptional-groups" "$NOV_BREAK" "November Break" "$ADMIN_TOKEN" > /dev/null; then success=$((success+1)); fi
|
||||
if api_post "$BASE_URL/scheduling/exceptional-groups" "$XMAS_BREAK" "Christmas Holiday" "$ADMIN_TOKEN" > /dev/null; then success=$((success+1)); fi
|
||||
echo "${C_GREEN}✅ Created $success/$total Exceptional Groups${C_RESET}"
|
||||
echo "${C_GREEN}✅ Simulated $cancel_count cancellations${C_RESET}"
|
||||
|
||||
echo -e "\n${C_GREEN}🎉 Seeding Complete!${C_RESET}"
|
||||
echo -e "${C_YELLOW}Press ENTER to run tests...${C_RESET}"
|
||||
# ===========================================================================
|
||||
# 5. EXCEPTIONAL SCHEDULING GROUPS
|
||||
# ===========================================================================
|
||||
echo -e "\n${C_BLUE}🗓️ Creating Exceptional Schedule Groups...${C_RESET}"
|
||||
sched_success=0
|
||||
|
||||
# Easter break — fully closed
|
||||
EASTER_BREAK='{
|
||||
"name":"Easter Break",
|
||||
"description":"Closed for Easter weekend",
|
||||
"hours":[
|
||||
{"weekday":0,"startTime":"00:00:00","endTime":"00:00:00","isOpen":false},
|
||||
{"weekday":1,"startTime":"00:00:00","endTime":"00:00:00","isOpen":false},
|
||||
{"weekday":2,"startTime":"00:00:00","endTime":"00:00:00","isOpen":false},
|
||||
{"weekday":3,"startTime":"00:00:00","endTime":"00:00:00","isOpen":false},
|
||||
{"weekday":4,"startTime":"00:00:00","endTime":"00:00:00","isOpen":false},
|
||||
{"weekday":5,"startTime":"00:00:00","endTime":"00:00:00","isOpen":false},
|
||||
{"weekday":6,"startTime":"00:00:00","endTime":"00:00:00","isOpen":false}
|
||||
],
|
||||
"weekStarts":["2026-04-06"]
|
||||
}'
|
||||
|
||||
# Summer half-term — reduced hours (mornings only, Tue-Sat)
|
||||
HALF_TERM='{
|
||||
"name":"Spring Half-Term",
|
||||
"description":"Reduced hours during half-term week — mornings only",
|
||||
"hours":[
|
||||
{"weekday":0,"startTime":"00:00:00","endTime":"00:00:00","isOpen":false},
|
||||
{"weekday":1,"startTime":"09:30:00","endTime":"13:00:00","isOpen":true},
|
||||
{"weekday":2,"startTime":"09:30:00","endTime":"13:00:00","isOpen":true},
|
||||
{"weekday":3,"startTime":"09:30:00","endTime":"13:00:00","isOpen":true},
|
||||
{"weekday":4,"startTime":"09:30:00","endTime":"13:00:00","isOpen":true},
|
||||
{"weekday":5,"startTime":"09:30:00","endTime":"13:00:00","isOpen":true},
|
||||
{"weekday":6,"startTime":"00:00:00","endTime":"00:00:00","isOpen":false}
|
||||
],
|
||||
"weekStarts":["2026-06-01"]
|
||||
}'
|
||||
|
||||
# Christmas holiday — fully closed across two weeks
|
||||
XMAS_BREAK='{
|
||||
"name":"Christmas Holiday Period",
|
||||
"description":"Closed for Christmas and New Year",
|
||||
"hours":[
|
||||
{"weekday":0,"startTime":"00:00:00","endTime":"00:00:00","isOpen":false},
|
||||
{"weekday":1,"startTime":"00:00:00","endTime":"00:00:00","isOpen":false},
|
||||
{"weekday":2,"startTime":"00:00:00","endTime":"00:00:00","isOpen":false},
|
||||
{"weekday":3,"startTime":"00:00:00","endTime":"00:00:00","isOpen":false},
|
||||
{"weekday":4,"startTime":"00:00:00","endTime":"00:00:00","isOpen":false},
|
||||
{"weekday":5,"startTime":"00:00:00","endTime":"00:00:00","isOpen":false},
|
||||
{"weekday":6,"startTime":"00:00:00","endTime":"00:00:00","isOpen":false}
|
||||
],
|
||||
"weekStarts":["2026-12-21","2026-12-28"]
|
||||
}'
|
||||
|
||||
if api_post "$BASE_URL/scheduling/exceptional-groups" "$EASTER_BREAK" "Easter Break" "$ADMIN_TOKEN" > /dev/null; then sched_success=$((sched_success+1)); fi
|
||||
if api_post "$BASE_URL/scheduling/exceptional-groups" "$HALF_TERM" "Spring Half-Term" "$ADMIN_TOKEN" > /dev/null; then sched_success=$((sched_success+1)); fi
|
||||
if api_post "$BASE_URL/scheduling/exceptional-groups" "$XMAS_BREAK" "Christmas Break" "$ADMIN_TOKEN" > /dev/null; then sched_success=$((sched_success+1)); fi
|
||||
|
||||
echo "${C_GREEN}✅ Created $sched_success/3 Exceptional Schedule Groups${C_RESET}"
|
||||
|
||||
# ===========================================================================
|
||||
# SUMMARY
|
||||
# ===========================================================================
|
||||
TOTAL_BOOKINGS=$((count_past + count_today + count_tomorrow + count_future))
|
||||
echo ""
|
||||
echo -e "${C_GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${C_RESET}"
|
||||
echo -e "${C_GREEN}🎉 Seeding Complete!${C_RESET}"
|
||||
echo -e "${C_GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${C_RESET}"
|
||||
echo -e " Users registered : $user_success/$user_total"
|
||||
echo -e " Services created : ${#SERVICE_IDS[@]}/${#SERVICES[@]}"
|
||||
if [[ -n "$PATCH_TEST_ID" ]]; then
|
||||
echo -e " Patch tests : 1 (Gel Allergy Test, recorded for Grace)"
|
||||
else
|
||||
echo -e " Patch tests : ⚠️ failed — see output above"
|
||||
fi
|
||||
echo -e " Bookings — past : $count_past"
|
||||
echo -e " Bookings — today : $count_today"
|
||||
echo -e " Bookings — tomorrow: $count_tomorrow"
|
||||
echo -e " Bookings — future : $count_future"
|
||||
echo -e " Bookings — total : $TOTAL_BOOKINGS"
|
||||
echo -e " Cancellations : $cancel_count"
|
||||
echo -e " Confirmed : $confirmed_count | Still pending: $skipped_count"
|
||||
echo -e " Schedule groups : $sched_success/3"
|
||||
echo ""
|
||||
echo -e " Quick login creds (all pass: ${C_YELLOW}password${C_RESET})"
|
||||
echo -e " Admin : ${C_YELLOW}admin@example.com${C_RESET}"
|
||||
echo -e " User : ${C_YELLOW}user@example.com${C_RESET}"
|
||||
echo -e " Deposit: ${C_YELLOW}poppy.thompson@example.com${C_RESET}"
|
||||
echo -e " Gel ✓ : ${C_YELLOW}grace.fletcher@example.com${C_RESET} (patch test done)"
|
||||
echo -e "${C_GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${C_RESET}"
|
||||
|
||||
echo -e "\n${C_YELLOW}Press ENTER to run tests...${C_RESET}"
|
||||
read -r
|
||||
echo -e "${C_GREEN}⏳ Running tests...${C_RESET}"
|
||||
# Run tests in current pane with full output
|
||||
|
||||
cd /home/popertots/Crussell/backend
|
||||
export POSTGRES_USER POSTGRES_PASSWORD POSTGRES_HOST POSTGRES_DB GO_TESTING=1
|
||||
TEST_OUTPUT=$(go test -tags test -v -p 1 -count=1 ./... 2>&1 || true)
|
||||
cd ..
|
||||
|
||||
# Show test summary - sanitize grep output to handle edge cases
|
||||
TOTAL_TESTS=$(echo "$TEST_OUTPUT" | grep "^=== RUN" | grep -cv "/" || echo "0")
|
||||
FAILED_TESTS=$(echo "$TEST_OUTPUT" | grep -c "^--- FAIL" || echo "0")
|
||||
SKIPPED_TESTS=$(echo "$TEST_OUTPUT" | grep -c "^--- SKIP" || echo "0")
|
||||
PASSED_TESTS=$(echo "$TEST_OUTPUT" | grep -c "^--- PASS" || echo "0")
|
||||
|
||||
# Fallback: if counts are empty or invalid, default to 0
|
||||
TOTAL_TESTS=${TOTAL_TESTS:-0}
|
||||
PASSED_TESTS=${PASSED_TESTS:-0}
|
||||
FAILED_TESTS=${FAILED_TESTS:-0}
|
||||
@@ -572,19 +903,15 @@ echo ""
|
||||
echo -e "${C_YELLOW}Press ENTER to restore 4-pane layout...${C_RESET}"
|
||||
read -r
|
||||
|
||||
# Switch back to the original workspace window (window 0)
|
||||
# The original 4 panes (DB, Backend, Frontend, Rustfs) are still there
|
||||
tmux select-window -t $SESSION_NAME:0
|
||||
tmux select-pane -t $SESSION_NAME:0.0
|
||||
|
||||
|
||||
SEED_EOF
|
||||
|
||||
chmod +x $SEED_SCRIPT
|
||||
|
||||
# --- 6. Execute Seed Script in Tmux ---
|
||||
log_step "Starting seeding process in new window..."
|
||||
# Pass SESSION_NAME to the seed script
|
||||
tmux new-window -t $SESSION_NAME -n "Seeding" "SESSION_NAME=$SESSION_NAME $SEED_SCRIPT"
|
||||
|
||||
# --- 7. Finalize ---
|
||||
|
||||
Reference in New Issue
Block a user