From 1dd37a8d2245928127cc5e6e87d87302babc1977 Mon Sep 17 00:00:00 2001 From: Stephen Adamson Date: Mon, 19 Jan 2026 22:32:25 +0000 Subject: [PATCH] Added booking approvals --- backend/handlers/admin/bookings.go | 1 - backend/handlers/bookings/bookings.go | 5 +- backend/main.go | 7 + frontend/package.json | 2 +- .../lib/components/admin/BookingModal.svelte | 123 +++++++++++------- frontend/src/routes/account/+page.svelte | 2 +- local-dev.sh | 20 +++ 7 files changed, 110 insertions(+), 50 deletions(-) delete mode 100644 backend/handlers/admin/bookings.go diff --git a/backend/handlers/admin/bookings.go b/backend/handlers/admin/bookings.go deleted file mode 100644 index d78da5d..0000000 --- a/backend/handlers/admin/bookings.go +++ /dev/null @@ -1 +0,0 @@ -package admin diff --git a/backend/handlers/bookings/bookings.go b/backend/handlers/bookings/bookings.go index 41b288b..e74e54f 100644 --- a/backend/handlers/bookings/bookings.go +++ b/backend/handlers/bookings/bookings.go @@ -771,6 +771,7 @@ func GetAdminBookingHandler(w http.ResponseWriter, r *http.Request) { // ---------------------------- serviceRows, err := db.DB.Query(r.Context(), ` SELECT + bs.service_id, s.name, coalesce(bs.override_price, s.price) as price, coalesce(bs.override_duration_minutes, s.duration_minutes) as duration_minutes @@ -790,11 +791,12 @@ func GetAdminBookingHandler(w http.ResponseWriter, r *http.Request) { var durationMinutesTotal int for serviceRows.Next() { + var serviceID string var name string var price float64 var durationMinutes int - if err := serviceRows.Scan(&name, &price, &durationMinutes); err != nil { + if err := serviceRows.Scan(&serviceID, &name, &price, &durationMinutes); err != nil { log.Printf("Failed to scan service for booking %s: %v", bookingID, err) http.Error(w, "Internal server error", http.StatusInternalServerError) return @@ -804,6 +806,7 @@ func GetAdminBookingHandler(w http.ResponseWriter, r *http.Request) { totalAmount += price durationMinutesTotal += durationMinutes booking.Services = append(booking.Services, BookingService{ + ServiceID: serviceID, // now set ServiceName: &name, Price: &price, DurationMinutes: &durationMinutes, diff --git a/backend/main.go b/backend/main.go index da3f9f9..a0cc0fc 100644 --- a/backend/main.go +++ b/backend/main.go @@ -20,6 +20,7 @@ import ( "crussell/handlers/notifications" "crussell/handlers/scheduling" "crussell/handlers/services" + "crussell/handlers/today" "crussell/handlers/user" ) @@ -140,6 +141,12 @@ func main() { r.Get("/{id}", user.GetAdminUserHandler) }) + r.Route("/admin/today", func(r chi.Router) { + r.Get("/current-next", today.GetCurrentAndNextHandler) + r.Get("/appointments", today.GetTodayAppointmentsHandler) + r.Get("/pending-approvals", today.GetPendingApprovalsHandler) + }) + // --- Admin Notifications --- r.Route("/admin/notifications", func(r chi.Router) { // List all unacknowledged notifications diff --git a/frontend/package.json b/frontend/package.json index e5f682a..4350c1e 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -42,7 +42,7 @@ "svelte-sonner": "^1.0.5", "sveltekit-superforms": "^2.27.1", "tailwind-merge": "^3.3.1", - "tailwind-variants": "^3.1.1", + "tailwind-variants": "^3.2.2", "tailwindcss": "^4.0.0", "tw-animate-css": "^1.3.8", "typescript": "^5.0.0", diff --git a/frontend/src/lib/components/admin/BookingModal.svelte b/frontend/src/lib/components/admin/BookingModal.svelte index e514ac2..1fcee13 100644 --- a/frontend/src/lib/components/admin/BookingModal.svelte +++ b/frontend/src/lib/components/admin/BookingModal.svelte @@ -4,6 +4,7 @@ import { toast } from 'svelte-sonner'; import * as Modal from '$lib/components/ui/dialog'; import { Button } from '$lib/components/ui/button'; + import ApprovalModal from '$lib/components/admin/ApprovalModal.svelte'; interface Props { open: boolean; @@ -31,6 +32,8 @@ user?: { id: string; + first_name: string; + last_name: string; full_name: string; email?: string; phone?: string; @@ -80,6 +83,13 @@ }; let selectedBooking = $state(null); + let showApprovalModal = $state(false); + + // Calculate total duration from services + let totalDuration = $derived( + selectedBooking?.services?.reduce((sum, service) => sum + (service.duration_minutes || 0), 0) || + 0 + ); async function fetchBookingDetails() { if (!bookingId) return; @@ -104,6 +114,8 @@ user: data.user ? { id: data.user.id, + first_name: data.user.first_name, + last_name: data.user.last_name, full_name: data.user.full_name, email: data.user.email, phone: data.user.phone, @@ -178,28 +190,35 @@ {/if} {#if selectedBooking} - - {selectedBooking.status.charAt(0).toUpperCase() + selectedBooking.status.slice(1)} - + {#if selectedBooking.status === 'pending'} +
+ + + + Pending + +
+ {/if} {/if} @@ -233,7 +252,7 @@
Duration
-
{selectedBooking.duration_minutes} minutes
+
{totalDuration} minutes
Created
@@ -262,6 +281,29 @@ {/if}
+ + {#if selectedBooking.services && selectedBooking.services.length > 0} +
+

+ Services +

+
+ {#each selectedBooking.services as service, index (index)} +
+
{service.service_name || '—'}
+ {#if service.service_description} +
{service.service_description}
+ {/if} +
+ {service.duration_minutes} min + £{service.price?.toFixed(2) || '0.00'} +
+
+ {/each} +
+
+ {/if} +

@@ -311,29 +353,6 @@ {/if}

- - {#if selectedBooking.services && selectedBooking.services.length > 0} -
-

- Services -

-
- {#each selectedBooking.services as service, index (index)} -
-
{service.service_name || '—'}
- {#if service.service_description} -
{service.service_description}
- {/if} -
- {service.duration_minutes} min - £{service.price?.toFixed(2) || '0.00'} -
-
- {/each} -
-
- {/if} -

@@ -435,3 +454,15 @@ + + +{#if selectedBooking && showApprovalModal} + { + showApprovalModal = false; + fetchBookingDetails(); + }} + /> +{/if} diff --git a/frontend/src/routes/account/+page.svelte b/frontend/src/routes/account/+page.svelte index c146063..e0570f8 100644 --- a/frontend/src/routes/account/+page.svelte +++ b/frontend/src/routes/account/+page.svelte @@ -711,7 +711,7 @@
  • • Share your referral code with friends
  • • They get 10% off their first booking
  • • You get 10% off your next booking after they claim
  • -
  • • You earn 1 loyalty stamp for each use to keep the savings going
  • +
  • • You earn 3 loyalty stamp for each use to keep the savings going
  • diff --git a/local-dev.sh b/local-dev.sh index 6f7aa50..d901ee1 100755 --- a/local-dev.sh +++ b/local-dev.sh @@ -262,6 +262,26 @@ format_london_time() { TZ=Europe/London date -d "$DATE_PART $TIME_PART" +"%Y-%m-%dT%H:%M:%S%:z" } +# --- TODAY BOOKINGS (4 bookings) --- +echo "" +echo "Creating 4 bookings for today..." + +TODAY_DATE=$(TZ=Europe/London date +%Y-%m-%d) + +create_booking "$USER_TOKEN" "$(format_london_time "$TODAY_DATE" "09:30:00")" \ + "[\"${SERVICE_IDS[0]}\"]" "" "Today - Classic Manicure 09:30" + +create_booking "$USER_TOKEN" "$(format_london_time "$TODAY_DATE" "11:00:00")" \ + "[\"${SERVICE_IDS[1]}\"]" "" "Today - Gel Manicure 11:00" + +create_booking "$USER_TOKEN" "$(format_london_time "$TODAY_DATE" "13:30:00")" \ + "[\"${SERVICE_IDS[3]}\"]" "Lunch break slot" "Today - Express Mani & Pedi 13:30" + +create_booking "$USER_TOKEN" "$(format_london_time "$TODAY_DATE" "16:00:00")" \ + "[\"${SERVICE_IDS[0]}\",\"${SERVICE_IDS[5]}\"]" \ + "End of day treat" "Today - Classic + Nail Art 16:00" + + # --- PAST BOOKINGS (35 bookings from October-November 2025) --- echo "" echo "Creating 35 past bookings..."