From 2b56c5640d7d904982c0893da7d83ff0e07292ac Mon Sep 17 00:00:00 2001 From: Stephen Adamson Date: Thu, 28 May 2026 16:48:56 +0100 Subject: [PATCH] docs: update all documentation for reschedule modal, time blockers UI, and booking flow improvements Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- README.md | 13 +- obsidian/Crussell/Admin Manual.md | 127 ++++++++++++++++-- .../Crussell/Future Work - Gap Backlog.md | 23 ++-- obsidian/Crussell/Overview.md | 19 ++- obsidian/Crussell/Technical Manual.md | 68 ++++++++-- obsidian/Crussell/User Manual.md | 5 +- 6 files changed, 208 insertions(+), 47 deletions(-) diff --git a/README.md b/README.md index 7b20c3f..bab38b2 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,11 @@ Nail salon booking platform — Go 1.25 backend + SvelteKit 5 frontend + Docker. - **Admin notifications**: priority-sorted queue with bell icon, `/notifications` page, acknowledge flow - **User notification preferences**: per-channel toggles (email, SMS, browser) in account settings - **Enriched edit requests**: side-by-side original vs proposed booking snapshots (time, services, prices, durations, user details) for admin review +- **Admin reschedule modal**: full reschedule UI on Today page with real-time available slot lookup, conflict detection, and one-click confirmation +- **Time blockers UI**: admin can create/manage one-off and recurring time blockers directly from the Admin dashboard, with overlap detection against existing bookings +- **Today page enhancements**: interactive daily calendar grid with visual time blockers, today stats summary, responsive layout improvements +- **Auto-select availability**: all booking flows (self-service, admin create, edit request) automatically select the first available date when data loads +- **Shared time slot utilities**: extracted common time slot generation, lunch protection, and formatting logic into a reusable module ## Project Structure @@ -34,7 +39,11 @@ Crussell/ ├─ frontend/ # SvelteKit 5 SPA (static build) │ └─ src/lib/components/ │ ├─ payments/ # PaymentModal (admin), UserPaymentModal (user) -│ └─ booking/ # BookingFlow (5-step wizard) +│ ├─ booking/ # BookingFlow (5-step wizard), DatePicker, TimeSlotPicker, TimeSlotList, SelectedTimeSummary +│ ├─ admin/ # BookingCreateModal, RescheduleModal, TimeBlockers, WeeklySchedule, HolidayHours, etc. +│ ├─ today/ # TodayCalendar (interactive grid), CurrentAppointment, PendingApprovals, TodayStats +│ └─ account/ # EditRequestModal, UserBookingModal +├─ frontend/src/lib/utils/ # Shared utilities (timeSlots.ts) ├─ sabredav/ # PHP + Composer for DAV ├─ nginx/ # Nginx reverse-proxy for HTTP & HTTPS ├─ init-scripts/ # PostgreSQL init SQL @@ -90,7 +99,7 @@ cd backend && go build -o bin/backend ./main.go # Frontend cd frontend && npm ci && npm run build -# Tests (438/441 passing, 3 skipped) +# Tests (444/447 passing, 3 skipped) cd backend && go test -tags "test,dev" ./... ``` diff --git a/obsidian/Crussell/Admin Manual.md b/obsidian/Crussell/Admin Manual.md index 11935fd..fcd7ffe 100644 --- a/obsidian/Crussell/Admin Manual.md +++ b/obsidian/Crussell/Admin Manual.md @@ -19,7 +19,17 @@ Go to the website and log in with your admin email and password. Once logged in ## Today Page -This is the page you'll use most often during the working day. It's divided into three main sections. +This is the page you'll use most often during the working day. It's divided into several sections. + +### Today Stats + +At the top, you'll see a summary of today's key metrics: +- **Total appointments** — how many bookings are scheduled for today +- **Confirmed** — how many are confirmed and ready to go +- **Pending** — how many are still waiting for your approval +- **Completed** — how many have already finished today + +This gives you a quick snapshot of the day at a glance. ### Current and Next Appointment @@ -40,6 +50,8 @@ At the very top of the page, you'll see two cards: **Edit** — Opens a window where you can change which services are included in the appointment. You can add services, remove services, or swap them out. You can also adjust the price or duration of individual services if needed (for example, if a service took longer than expected last time). You can also update the appointment notes here. +**Reschedule** — Opens the reschedule modal where you can move this appointment to a different date and time. The system shows available slots, checks for conflicts with existing bookings, and lets you confirm the change in one click. See the "Rescheduling Appointments" section below for full details. + **Take Payment** — Opens the payment modal where you can choose between Card (Square Terminal), Cash, or Gift Card payment. See the "Taking Payments" section below for full details. **Extend** and **Cancel** — These buttons are placeholders and don't do anything yet. @@ -88,9 +100,29 @@ If you chose **Gift Card**, enter the 12-digit ID printed on the gift card: After any payment method succeeds, a receipt summary is shown with the payment amount, method, and a success indicator. -### Today's Appointments +### Today's Calendar -Below the current/next cards is a full list of every appointment scheduled for today, in chronological order. +Below the current/next cards is an **interactive daily calendar grid** showing every appointment scheduled for today, plotted on a visual timeline. + +**What you'll see:** +- A time axis running down the left side (from your opening time to closing time) +- Each appointment shown as a coloured bar spanning its start time to end time +- The customer's name and services inside each bar +- Colour-coded status indicators (pending, confirmed, in progress, completed, cancelled) +- **Time blockers** shown as red/hatched bars — periods when the salon is unavailable (staff meetings, lunch breaks, personal appointments) + +**Interacting with the calendar:** +- **Tap or click any appointment** to open its full details window +- **Scroll** if the day extends beyond the visible area +- The calendar automatically adjusts to your working hours for that day (including any holiday schedules) + +**Creating a time blocker from the calendar:** +- You can add new time blockers directly from the Admin dashboard (see the "Time Blockers" section below) +- Blockers appear on the calendar immediately and prevent new bookings during those periods + +### Today's Appointments List + +Below the calendar is a full list of every appointment scheduled for today, in chronological order. Each appointment shows: - The customer's name @@ -107,7 +139,9 @@ Each appointment shows: ### Pending Approvals -This section shows all bookings that are waiting for your review. When a customer books online, their booking starts as "pending" — it won't appear in the main schedule until you confirm it. +This section shows all bookings that are waiting for your review, plus any pending edit requests from customers. The list refreshes in the background without showing loading spinners on subsequent updates, so it stays current without disrupting your workflow. + +**For pending bookings**, when a customer books online, their booking starts as "pending" — it won't appear in the main schedule until you confirm it. For each pending booking, you'll see: - The customer's name @@ -118,6 +152,44 @@ For each pending booking, you'll see: **Decline** — Cancels the booking. The customer's appointment is removed and marked as cancelled by the salon. +**For edit requests**, when a customer requests to reschedule their appointment: +- The request appears here with the customer's name, current time, and requested new time +- Click to open the full edit request details with the side-by-side comparison +- **Approve** — Updates the booking to the new time +- **Decline** — Keeps the original appointment unchanged + +--- + +## Rescheduling Appointments + +When you need to move an appointment to a different date or time (for example, if the salon needs to close early or if you're running behind), you can use the reschedule modal. + +### How to Reschedule + +1. **Open the reschedule modal** — Click **Reschedule** on the Current/Next Appointment card on the Today page, or from the booking details window +2. **Select new services** (optional) — You can change which services are included, just like when creating a new booking +3. **Pick a new date** — The calendar shows available dates. The system automatically selects the first available date for you +4. **Pick a new time** — Available time slots are displayed for the selected date. The system checks: + - Your working hours for that day (including any holiday schedules) + - Existing appointments (no overlaps allowed) + - Time blockers (you can't schedule during a blocked period) + - Slot reservations (slots other customers are currently booking) +5. **Review the changes** — The modal shows a summary of the new date, time, and services +6. **Confirm** — The booking is updated immediately. The customer's original appointment time is replaced with the new one + +### What the System Checks + +- The new time doesn't clash with another appointment +- The new time falls within your working hours (including holiday schedules) +- The new time doesn't fall during a time blocker period +- If you're changing services, the new total duration fits in the slot +- Any existing edit requests for this booking are cleared when you reschedule + +### Difference from Customer Edit Requests + +- **Customer edit requests**: The customer proposes a new time, and you approve or decline it (see "Reschedule Requests from Customers" below) +- **Admin reschedule**: You directly choose the new time and confirm it — no approval step needed. This is useful when the salon needs to proactively move appointments. + --- ## Edit Booking Window @@ -205,6 +277,10 @@ This is where you set up special opening hours for holidays, bank holidays, or a **To Remove:** Delete the schedule group when it's no longer needed. +### Time Blockers + +See the dedicated "Time Blockers" section above for full details. The Time Blockers card on the Admin dashboard lets you create, view, and delete one-off and recurring unavailable periods. + ### Weekly Schedule This is where you set your regular, week-to-week opening hours. @@ -465,27 +541,46 @@ The portfolio is the salon's gallery of nail art photos that customers can brows ## Time Blockers -Time blockers are periods when the salon is unavailable for bookings — for example, a staff meeting, a personal appointment, or a lunch break. +Time blockers are periods when the salon is unavailable for bookings — for example, a staff meeting, a personal appointment, or a lunch break. You can manage them directly from the Admin dashboard. + +### The Time Blockers Card + +On the Admin dashboard, you'll find the **Time Blockers** card. It shows: +- A list of all existing time blockers (one-off and recurring) +- Each blocker shows its description, start time, and duration +- Recurring blockers show their cron pattern (e.g., "Every Wednesday at 1:00 PM") ### How to Add a Time Blocker -1. Go to the **Admin** dashboard -2. Find the time blockers section -3. Set the **start date and time** -4. Set the **duration** (how long the block lasts) -5. Add a **description** (for example, "Staff meeting" or "Lunch break") -6. Save +1. Click **Add Blocker** on the Time Blockers card +2. Set the **start date and time** +3. Set the **end time** (or duration) +4. Add a **description** (for example, "Staff meeting" or "Lunch break") +5. Choose **One-off** or **Recurring**: + - **One-off**: happens once on the specified date + - **Recurring**: set a pattern (e.g., every Wednesday, every 2nd Friday of the month) +6. **Check for overlaps** — the system automatically checks if your blocker conflicts with existing bookings. If there are overlaps, you'll see which appointments are affected before you confirm +7. **Save** — the blocker is created and the affected time slots become unavailable for new bookings -The blocked period will no longer appear as available when customers or you are booking appointments. +### Overlap Detection + +When you create a time blocker, the system checks for any existing bookings that fall within the blocked period. You'll see: +- A list of overlapping appointments with customer names and times +- The option to proceed anyway (the bookings remain, but no new bookings can be made during that period) +- This prevents accidental blocking of already-scheduled appointments ### One-Off vs. Recurring - **One-off blockers** happen once — for example, "Dentist appointment on 15th March at 2pm" -- **Recurring blockers** happen on a schedule — for example, "Lunch break every Wednesday from 1pm to 2pm". You set a pattern and the system applies it automatically +- **Recurring blockers** happen on a schedule — for example, "Lunch break every Wednesday from 1pm to 2pm". You set a pattern using a cron expression and the system applies it automatically ### Removing a Time Blocker -Simply delete it from the list. The time slot becomes available for booking again. +Click the delete button next to any blocker. A confirmation prompt appears before deletion. Once deleted, the time slot becomes available for booking again. + +### Time Blockers on the Today Calendar + +Time blockers also appear on the Today page's interactive calendar grid as red/hatched bars, so you can see at a glance when the salon is unavailable. --- @@ -553,6 +648,10 @@ This lets you see exactly what will change before you approve or decline. **Decline** — The booking stays as it is. The customer's request is cleared and their original appointment remains unchanged. +### Admin-Initiated Reschedule + +You can also proactively reschedule a customer's appointment using the **Reschedule** button on the Current/Next Appointment card. This opens the reschedule modal where you pick a new date and time directly — no approval step needed. See the "Rescheduling Appointments" section above for details. + ### Things to Check Before Approving - The new time doesn't clash with another appointment diff --git a/obsidian/Crussell/Future Work - Gap Backlog.md b/obsidian/Crussell/Future Work - Gap Backlog.md index dd7bf5f..eb4257e 100644 --- a/obsidian/Crussell/Future Work - Gap Backlog.md +++ b/obsidian/Crussell/Future Work - Gap Backlog.md @@ -1,4 +1,4 @@ -**Last Updated:** May 2026 — Multi-method payments live (Card/Cash/Gift Card), saved cards management, online payment system expanded, amount precision (pence), tip page redesigned with percentages, 396/399 tests passing (3 skipped) +**Last Updated:** May 2026 — Admin reschedule modal, time blockers UI, Today calendar with interactive grid, auto-select across all booking flows, shared time slot utilities, GetBookingsByCreatedRange endpoint, 444/447 tests passing (3 skipped) **Status:** Living backlog — add to this as gaps are discovered --- @@ -14,7 +14,7 @@ No external dependencies. No paid services. No API keys needed. | 1 | ~~`DELETE /api/user/account` is a no-op~~ ✅ | S (1-2h) | Backend | Wired to `anonymize_user()` for registered users and `delete_guest_user()` for guests. CardDAV contact deleted best-effort. | | 2 | ~~**WalkInCreateModal guest booking errors out**~~ ✅ | S (1-2h) | Frontend | Guest creation now fires at submit time in both walk-in and call-in flows. Phone defaults to +447700900000 if left blank. | | 3 | ~~**ApprovalModal decline/cancel stub**~~ ✅ | S (2-3h) | Frontend | `handleDecline()` now calls `POST /api/admin/bookings/{id}/cancel`. Backend sets status to `we_cancelled`, acknowledges pending notification, creates cancelled_booking notification. | -| 4 | **CurrentAppointment action stubs** | M (1d) | Frontend | `handleEdit()` ✅ — now opens `EditBookingModal` for service management. `handleTakePayment()` ✅ — wired to multi-method PaymentModal (Card/Cash/Gift Card). `handleExtend()`, `handleCancel()` — dead buttons. | +| 4 | **CurrentAppointment action stubs** | M (1d) | Frontend | `handleEdit()` ✅ — opens `EditBookingModal` for service management. `handleTakePayment()` ✅ — wired to multi-method PaymentModal. `handleReschedule()` ✅ — opens RescheduleModal with available slot lookup and conflict detection. `handleExtend()`, `handleCancel()` — dead buttons. | ## P1 — High @@ -28,8 +28,8 @@ No external dependencies. No paid services. No API keys needed. | 10 | **Password reset flow not wired to frontend** | S (2-3h) | Frontend | Backend has `/api/verify/generate` and `/api/verify/check` endpoints. Login page has no "forgot password" link or form. | | 11 | **Email verification flow not wired to frontend** | S (2-3h) | Frontend | Users register with `unverified_email` role. No UI to enter verification code or resend code. `+layout.svelte` has alert-based prototype. | | 12 | **Booking cancellation from user account** | S (2-3h) | Frontend | UserBookingModal shows booking details but no cancel button. Users must call/email to cancel. Backend endpoint exists (`DELETE /api/bookings/{id}`). | -| 13 | ~~**Booking rescheduling for users**~~ ⚠️ | M (1-2d) | Full-stack | Backend fully wired (request/approve/deny + time_blocker reservation). Frontend re-validation on submit implemented. **Remaining:** Admin UI to view/approve/deny edit requests — consider extending existing Pending Approvals panel. User notification on approve/deny tracked as TODO (blocked on E5). | -| 50 | **Admin UI for edit request approval/denial** | M (1-2d) | Frontend | Backend endpoints exist: `GET /api/admin/bookings/{id}/edit-requests`, `POST .../approve`, `POST .../deny`. Needs UI to display pending requests with proposed vs original time, approve/deny buttons. **Consider:** Extend existing Pending Approvals panel on Today page rather than building separate page. | +| 13 | ~~**Booking rescheduling for users**~~ ✅ | ~~M (1-2d)~~ | ~~Full-stack~~ | ~~Backend fully wired (request/approve/deny + time_blocker reservation). Frontend re-validation on submit implemented.~~ **Complete May 2026.** User-facing edit request modal with auto-select, responsive layout, 15-min grid alignment. Admin reschedule modal with real-time available slot lookup, conflict detection, one-click confirm. Enriched side-by-side snapshots in Pending Approvals. | +| ~~50~~ | ~~**Admin UI for edit request approval/denial**~~ ✅ | ~~M (1-2d)~~ | ~~Frontend~~ | ~~Backend endpoints exist~~ **Complete May 2026.** Edit requests appear in Pending Approvals on Today page with side-by-side comparison. Approve/deny buttons wired. Admin reschedule modal provides direct reschedule without approval step. | ## P2 — Medium @@ -38,7 +38,7 @@ No external dependencies. No paid services. No API keys needed. | 14 | ~~`delete_guest_user()` SQL function missing~~ ✅ | S (1h) | DB | Created next to `anonymize_user()` in init-script.sql. Called by `DeleteAccountHandler` for guest users. | | ~~15~~ | ~~**User notification preferences UI**~~ ✅ | ~~S (2-3h)~~ | ~~Frontend~~ | ~~DB table `user_notification_preferences` exists with email/sms/push flags. No settings page to toggle them.~~ GET/PUT endpoints wired. Toggle section in /account Admin tab. Email, SMS, Browser push channels. Auto-save on toggle. | | 16 | **One-off custom services** | M (1-2d) | Full-stack | Admin can't create single-use services outside the catalog. Every custom job (bridal party, special request) must be added to permanent service list. | -| 17 | **One-off exceptional hours** | M (1d) | Full-stack | Single-day overrides (dentist appointment, afternoon off) require creating a full exceptional group. Should support one-off date blocks without group overhead. | +| 17 | **One-off exceptional hours** | M (1d) | Full-stack | Single-day overrides (dentist appointment, afternoon off) require creating a full exceptional group. ~~Should support one-off date blocks without group overhead.~~ **Partially addressed:** Time blockers UI handles one-off unavailable periods. Remaining: one-off *open* hours (e.g., "open 2pm-5pm on a Sunday") still needs exceptional group simplification. | | 18 | ~~**HSTS header**~~ ✅ | XS (15min) | Backend | Added as a TODO-comment in the security headers middleware. Will be uncommented when HTTPS is enabled in production. | | 19 | ~~**Referrer-Policy header**~~ ✅ | XS (15min) | Backend | Added as a TODO-comment in the security headers middleware. Will be uncommented when ready for production. | | 20 | **Business settings management UI** | M (1-2d) | Full-stack | `business_settings` table exists (VAT registration, business name, etc.). No admin page to configure. Changes require direct SQL. | @@ -55,7 +55,7 @@ No external dependencies. No paid services. No API keys needed. | 31 | **Per-user rate limiting** | M (1d) | Backend | Rate limiter is IP-based. Authenticated users could abuse from multiple IPs. Should track by user ID + IP. Pure Go — no Redis needed for single-instance. | | 32 | **CSRF protection** | S (2-3h) | Backend | SvelteKit handles some CSRF for its own forms, but direct API calls to `/api/*` bypass it. Consider double-submit cookie or SameSite cookies. | | 33 | **Begin button (Today page)** | S (2-3h) | Full-stack | Manual start for early arrivals. Gray out if >3hrs away. Currently auto-infer only. | -| 34 | **Auto lunch protection** | M (1d) | Backend | Block bookings that remove lunch break. 1h customer auto-block, 30min admin with warning. | +| 34 | ~~**Auto lunch protection**~~ ✅ | ~~M (1d)~~ | ~~Backend~~ | ~~Block bookings that remove lunch break. 1h customer auto-block, 30min admin with warning.~~ **Complete May 2026.** `findAllLunchGaps()` replaces `findLargestLunchGap()` — returns all gaps sorted descending. `buildLunchProtection()` in shared `timeSlots.ts` consolidates logic across all booking flows. Admin journey warns if <1h gap, requires 30min minimum. User journey requires 1h minimum. | | 35 | ~~**Walk-in slot blocking**~~ ✅ | S (1-2h) | Frontend | **Resolved May 2026.** `WalkInBooking.svelte` reserves slot via `POST /api/admin/bookings/reserve` (15-min TTL) before opening `WalkInCreateModal`. Backend `AdminReserveSlotHandler` creates `time_blocker` entry with `RESERVATION:admin:walkin:*` description, blocking concurrent bookings. Minor gap: reservation time_blocker not deleted after booking creation (relies on TTL expiry via `CleanupOldReservations`). | | ~~36~~ | ~~**No idempotency keys for bookings**~~ ✅ | S (2-3h) | Full-stack | **Implemented May 2026.** `idempotency_key VARCHAR(64) UNIQUE` column added to bookings table. Both `POST /api/bookings` and `POST /api/admin/bookings` extract `Idempotency-Key` header, check for existing booking with that key, return existing booking with 200 if found (no duplicate). Frontend BookingFlow.svelte and WalkInCreateModal.svelte generate UUID via `crypto.randomUUID()`, reuse same key on retry. | | 37 | **No booking conflict detection for users** | S (2-3h) | Backend | Users can theoretically double-book themselves if they open two tabs. Reservation system helps but doesn't fully prevent. | @@ -180,10 +180,13 @@ Require paid accounts, API approval, or external service credentials. **Do not a 9. **#2** Wire WalkInCreateModal guest booking (1-2h) ✅ 10. **#3** ApprovalModal decline/cancel (2-3h) ✅ 11. ~~**#5** Admin notification panel (1-2d)~~ ✅ -12. **#4** CurrentAppointment Extend + Cancel actions (1d) — skip TakePayment (blocked on E1). **Edit** ✅ — now opens `EditBookingModal` for service management. **Take Payment** ✅ — multi-method PaymentModal (Card/Cash/Gift Card) with service price overrides and tip presets. +12. **#4** CurrentAppointment Extend + Cancel actions (1d) — skip TakePayment (blocked on E1). **Edit** ✅ — now opens `EditBookingModal` for service management. **Take Payment** ✅ — multi-method PaymentModal (Card/Cash/Gift Card) with service price overrides and tip presets. **Reschedule** ✅ — RescheduleModal with available slot lookup and conflict detection. 13. **#12** Booking cancellation from user account (2-3h) 14. **#40** No-show tracking dashboard (2-3h) 15. **#35** Walk-in slot blocking (1-2h) ✅ +16. ~~**#13**~~ ~~Booking rescheduling~~ ✅ — complete (user edit request + admin reschedule modal) +17. ~~**#50**~~ ~~Admin UI for edit requests~~ ✅ — complete (Pending Approvals integration) +18. ~~**#34**~~ ~~Auto lunch protection~~ ✅ — complete (findAllLunchGaps + shared timeSlots.ts) ### Phase 3 — Compliance + Reliability (Week 3) @@ -200,10 +203,10 @@ Require paid accounts, API approval, or external service credentials. **Do not a 23. **#10** Password reset flow (2-3h) 24. **#11** Email verification flow (2-3h) -25. **#13** Booking rescheduling for users (1-2d) +25. ~~**#13**~~ ~~Booking rescheduling for users~~ ✅ — complete 26. **#20** Business settings management UI (1-2d) 27. **#16** One-off custom services (1-2d) -28. **#17** One-off exceptional hours (1d) +28. **#17** One-off exceptional hours (1d) — partially addressed by time blockers UI 29. ~~**#35**~~ ~~Walk-in slot blocking~~ ✅ — resolved 30. **#38** Service category management (1-2d) @@ -217,7 +220,7 @@ Require paid accounts, API approval, or external service credentials. **Do not a 36. ~~**#31**~~ Per-user rate limiting (1d) 37. **#32** CSRF protection (2-3h) 38. **#33** Begin button (Today page) (2-3h) -39. **#34** Auto lunch protection (1d) +39. ~~**#34**~~ ~~Auto lunch protection~~ ✅ — complete 40. **#37** Booking conflict detection (2-3h) 41. **#45** Loyalty stamp redemption (partial — UI + redeem endpoint, payment apply blocked on E1) 42. ~~**#46**~~ ~~Staff management~~ 🗑️ — single employee business diff --git a/obsidian/Crussell/Overview.md b/obsidian/Crussell/Overview.md index 03203b0..c9baf7f 100644 --- a/obsidian/Crussell/Overview.md +++ b/obsidian/Crussell/Overview.md @@ -104,8 +104,9 @@ flowchart TD ### Scheduling - Default weekly working hours (Mon-Fri, closed Sat/Sun) - Exceptional schedule groups (holiday periods, special hours) -- Time blockers (admin-defined unavailable periods, plus slot reservations) +- Time blockers (admin-defined unavailable periods, plus slot reservations) — with full UI for creation and management - Available hours calculation accounting for bookings, blockers, and gaps +- **GetBookingsByCreatedRange**: admin endpoint to query bookings within a created_at date range ### Customer Features - Profile management with profile picture upload (cropper, separate S3 bucket) @@ -116,12 +117,15 @@ flowchart TD - **Online payments**: pay deposits, pay early, partial payments, balance payments, tips on completed bookings - **Saved cards**: manage cards in Account → Cards tab — add/remove cards for faster checkout (soft-deleted on removal, 7-year retention) - **Tip page**: percentage-based tips (10%, 15%, 20%) or custom amount on completed bookings via `/pay-tip/[id]` +- **Auto-select**: booking flows automatically select the first available date when availability data loads ### Admin Features -- Today page (/today): current/next appointment, today's grid, pending approvals -- Admin dashboard (/admin): services CRUD, user management, bookings list, scheduling +- Today page (/today): current/next appointment, today's interactive calendar grid, pending approvals, today stats summary +- Admin dashboard (/admin): services CRUD, user management, bookings list, scheduling, time blockers UI - Walk-in booking wizard (3-step) with slot reservation - Call-in booking wizard (4-step) with slot reservation +- **Reschedule modal**: full reschedule UI on Today page — search available slots, detect conflicts, one-click confirm +- **Time blockers UI**: create/manage one-off and recurring blockers from Admin dashboard with overlap detection - User modal: profile, booking history, customer relationship data (spend, visits, top services), patch test recording, loyalty/referrals, privacy/consent - Booking modal: view details, approve/cancel - Approval modal for pending bookings @@ -133,6 +137,7 @@ flowchart TD - **Tip selection**: 10%, 15%, 20% presets or custom amount during card payments - **Refunds**: process partial or full refunds on completed payments - **Financial tracking**: fees column on payments for actual Square deductions, deposit batch tracking for bank reconciliation +- **GetBookingsByCreatedRange**: query bookings by created_at date range for admin reporting ### Deposit System - Simplified tracking: `deposits_required` integer (0-3) on users table @@ -181,7 +186,7 @@ flowchart TD | **Walk-In** | Admin | Admin panel → WalkInBooking → `POST /api/admin/bookings/reserve` → WalkInCreateModal | 5min | | **Call-In** | Admin | Admin panel → CallInBooking → `POST /api/admin/bookings/reserve` → BookingCreateModal | 1h | -All flows integrate with holiday/exceptional hours and time blockers. +All flows integrate with holiday/exceptional hours and time blockers. All booking flows feature **auto-select** — the first available date is automatically selected when availability data loads. --- @@ -196,7 +201,7 @@ All flows integrate with holiday/exceptional hours and time blockers. ## Test Coverage -**438/441 tests passing** (3 skipped) across 12+ test packages. Comprehensive coverage of online payments (deposit, full, partial, balance), saved card operations, tip payments, terminal payments, refunds, idempotency, webhook handling, cash/gift card payments, and enriched edit request workflows. +**444/447 tests passing** (3 skipped) across 12+ test packages. Comprehensive coverage of online payments (deposit, full, partial, balance), saved card operations, tip payments, terminal payments, refunds, idempotency, webhook handling, cash/gift card payments, enriched edit request workflows, GetBookingsByCreatedRange endpoint, and scheduling exceptional hours. | Package | Coverage Area | |---------|--------------| @@ -204,8 +209,8 @@ All flows integrate with holiday/exceptional hours and time blockers. | `handlers/bookings` | User booking flow, guest bookings, reservations, edit requests (create/delete/view/enriched), approval/rejection, time-blocker lifecycle, exceptional hours validation, cancellation cleanup, cross-user isolation | | `handlers/payments` | Square payments (terminal, online, refunds, tips, saved cards) | | `internal/square` | Square client interface, dev mock, prod stub | -| `handlers/admin` | Admin bookings, today view, users, services | -| `handlers/scheduling` | Working hours, exceptional groups, available hours, time blockers | +| `handlers/admin` | Admin bookings, today view, users, services, GetBookingsByCreatedRange | +| `handlers/scheduling` | Working hours, exceptional groups, available hours, time blockers, exceptional hours validation | | `handlers/services` | Service eligibility (age + patch test filtering) | | `handlers/user` | User profile, guest creation, loyalty | | `handlers/portfolio` | Image upload, listing, tags, filters | diff --git a/obsidian/Crussell/Technical Manual.md b/obsidian/Crussell/Technical Manual.md index cf4d852..784ffff 100644 --- a/obsidian/Crussell/Technical Manual.md +++ b/obsidian/Crussell/Technical Manual.md @@ -48,7 +48,7 @@ Backend (:8080) | Package | File(s) | Purpose | |---------|---------|---------| | `handlers/auth` | local.go, social.go | Registration, login, refresh, email verification | -| `handlers/bookings` | bookings.go, reserve.go, manage.go, admin_reserve.go | Booking CRUD, reservations, admin management, edit requests, discounts, closing hours validation, active booking limits | +| `handlers/bookings` | bookings.go, reserve.go, manage.go, admin_reserve.go | Booking CRUD, reservations, admin management, edit requests, discounts, closing hours validation, active booking limits, GetBookingsByCreatedRange | | `handlers/payments` | handlers.go, service.go, validators.go | Square payments: terminal, online, refunds, tips, saved cards | | `handlers/webhooks` | square.go | Square webhook handler for payment status updates | | `handlers/admin` | users.go, analytics.go, discount_campaigns.go | Admin user management, discount campaigns, analytics (stub) | @@ -117,7 +117,7 @@ Backend (:8080) src/lib/components/ ├── admin/ │ ├── ApprovalModal.svelte # Booking approval/decline -│ ├── BookingCreateModal.svelte # Admin booking creation (4-step) +│ ├── BookingCreateModal.svelte # Admin booking creation (4-step, uses shared timeSlots utils) │ ├── BookingModal.svelte # View booking details │ ├── BookingsCard.svelte # Bookings list │ ├── CallInBooking.svelte # Call-in booking flow @@ -130,25 +130,30 @@ src/lib/components/ │ ├── UserModal.svelte # User details + relationship data │ ├── UsersCard.svelte # Users list │ ├── WalkInBooking.svelte # Walk-in booking flow -│ └── WalkInCreateModal.svelte # Walk-in 3-step wizard +│ ├── WalkInCreateModal.svelte # Walk-in 3-step wizard +│ ├── RescheduleModal.svelte # Admin reschedule — available slot lookup, conflict detection +│ └── TimeBlockers.svelte # Time blocker CRUD — one-off + recurring, overlap detection ├── booking/ │ ├── BookingActions.svelte # Next/Back buttons -│ ├── BookingFlow.svelte # 5-step booking wizard (deposit step conditional, confirmation screen) +│ ├── BookingFlow.svelte # 5-step booking wizard (auto-select, shared timeSlots utils) │ ├── BookingSummary.svelte # Booking review summary │ ├── DatePicker.svelte # Calendar date selection │ ├── ServiceCard.svelte # Individual service display │ ├── ServiceSelector.svelte # Service selection UI │ ├── StepIndicator.svelte # Progress steps -│ └── TimeSlotPicker.svelte # Time slot grid +│ ├── TimeSlotPicker.svelte # Time slot grid +│ ├── TimeSlotList.svelte # Scrollable time slot list (admin booking) +│ └── SelectedTimeSummary.svelte # Selected date/time display ├── payments/ │ ├── PaymentModal.svelte # Admin payment modal — multi-method: Card (Terminal), Cash (change calc), Gift Card (12-digit ID), service price overrides, tip presets (10/15/20%) │ └── UserPaymentModal.svelte # User payment modal (deposit, partial, full, balance — no tip) ├── today/ -│ ├── CurrentAppointment.svelte # Active appointment display -│ ├── PendingApprovals.svelte # Pending booking approvals -│ └── TodayCalendar.svelte # Day view calendar +│ ├── CurrentAppointment.svelte # Active appointment display (responsive) +│ ├── PendingApprovals.svelte # Pending booking approvals + edit requests (dedup refresh) +│ ├── TodayCalendar.svelte # Interactive day view calendar with time blockers +│ └── TodayStats.svelte # Today's appointment stats summary ├── layout/ -│ ├── NavBar.svelte # Navigation bar +│ ├── NavBar.svelte # Navigation bar (responsive mobile menu) │ └── PortfolioCarousel.svelte # Home page gallery ├── ui/ # shadcn-svelte components └── account/ # Account-related components @@ -161,6 +166,17 @@ src/lib/components/ - **Token refresh**: Every hour, refreshes if < 14 days remaining - **Role-based UI**: `hasRole()`, `isAdmin()`, `isVerified()` control visible elements +### Shared Utilities + +- **`lib/utils/timeSlots.ts`**: Extracted common time slot logic used across booking and admin components + - `buildLunchProtection()` — calculates lunch break proximity warnings for a given date + - `generateAvailableTimeSlots()` — generates individual 15-min slot times for a duration + - `generateGroupedTimeSlots()` — generates slots grouped by availability (available/unavailable) + - `formatTime()` — formats minutes-since-midnight to HH:MM + - `calculateEndTime()` — computes end time from start + duration + - `getDayWithOrdinal()` — formats a CalendarDate with ordinal suffix (e.g., "January 15th") + - Types: `DayHours`, `DayAvailability` — shared type definitions for working/available hours data + --- ## API Reference @@ -229,6 +245,7 @@ src/lib/components/ | GET | `/api/admin/bookings` | List all bookings | | POST | `/api/admin/bookings` | Create booking for user | | GET | `/api/admin/bookings/search` | Search bookings | +| GET | `/api/admin/bookings/by-created-range` | Get bookings within a created_at date range (query params: `start`, `end` as ISO 8601) | | GET | `/api/admin/bookings/user/{user_id}` | User's bookings | | GET | `/api/admin/bookings/{id}` | Get booking details | | PUT | `/api/admin/bookings/{id}` | Update booking services, overrides, notes | @@ -603,6 +620,31 @@ type EnrichedEditRequest struct { --- +### GetBookingsByCreatedRange + +**How it works:** `GET /api/admin/bookings/by-created-range?start=ISO8601&end=ISO8601` returns all bookings whose `created_at` falls within the specified range. Results are ordered by `created_at` ascending. + +**Use case:** Admin reporting — "show me all bookings created this week" or "show me all bookings created in January". + +**Response format:** Returns `OverlappingBookingsResponse` with `bookings` array — same structure as the overlapping bookings endpoint, including user details and services. + +**Validation:** +- Both `start` and `end` query parameters are required (400 if missing) +- Must be valid ISO 8601 timestamps (400 if invalid format) +- Requires admin authentication + +--- + +### Lunch Protection Refactor + +**Before:** `findLargestLunchGap()` returned a single number — the largest gap in the middle window after accounting for bookings. + +**After:** `findAllLunchGaps()` returns an array of all gap durations in the middle window, sorted descending. This enables the Today calendar to show all available lunch break opportunities rather than just the best one. + +**Frontend:** `buildLunchProtection()` in `lib/utils/timeSlots.ts` consolidates lunch protection logic that was previously duplicated across BookingFlow, BookingCreateModal, and EditRequestModal. All booking flows now use the shared utility. + +--- + ### Idempotency Keys **How it works:** `Idempotency-Key` header (optional, 64-char max). If provided, backend checks for existing booking with that key. If found, returns existing booking (200, no duplicate). If not found, creates new booking with key stored. @@ -696,13 +738,13 @@ go test -tags "test,dev" -v -p 1 -count=2 ./... # Run twice for flaky detection ### Test Coverage -**438/441 tests passing** (3 skipped) across 12+ packages. Comprehensive coverage of online payments (deposit, full, partial, balance), saved card operations, tip payments, terminal payments, refunds, idempotency, webhook handling, and cash/gift card payments. +**444/447 tests passing** (3 skipped) across 12+ packages. Comprehensive coverage of online payments (deposit, full, partial, balance), saved card operations, tip payments, terminal payments, refunds, idempotency, webhook handling, and cash/gift card payments, GetBookingsByCreatedRange endpoint, and scheduling exceptional hours validation. - `handlers/auth` — Authentication -- `handlers/bookings` — User booking flow, guest bookings, reservations, edit requests, discounts, closing hours validation, active booking limits +- `handlers/bookings` — User booking flow, guest bookings, reservations, edit requests, discounts, closing hours validation, active booking limits, GetBookingsByCreatedRange - `handlers/payments` — Square payments (terminal, online, refunds, tips, saved cards) - `internal/square` — Square client dev mock tests -- `handlers/admin` — Admin bookings, today view, users, services -- `handlers/scheduling` — Working hours, exceptional groups, available hours, time blockers +- `handlers/admin` — Admin bookings, today view, users, services, GetBookingsByCreatedRange +- `handlers/scheduling` — Working hours, exceptional groups, available hours, time blockers, exceptional hours validation - `handlers/services` — Service eligibility - `handlers/user` — User profile, guest creation, loyalty - `handlers/portfolio` — Image upload, listing, tags, filters diff --git a/obsidian/Crussell/User Manual.md b/obsidian/Crussell/User Manual.md index ad95f4f..4c75c8f 100644 --- a/obsidian/Crussell/User Manual.md +++ b/obsidian/Crussell/User Manual.md @@ -41,6 +41,9 @@ If you're not logged in, age-based filtering is skipped — the system doesn't k A calendar appears showing which dates have availability: - **Highlighted dates** have open slots - **Greyed-out dates** are fully booked, the salon is closed that day, or the salon has blocked out that time (for example, a holiday or personal appointment) +- A small dot appears under today's date to help orient you + +**The system automatically selects the first available date** for you when the calendar loads, so you can skip straight to picking a time if the suggested date works. You can always click a different date if you prefer. The calendar takes into account: - The salon's regular opening hours (Monday to Friday, closed Saturday and Sunday for example) @@ -52,7 +55,7 @@ Tap or click a date that works for you. ### Step 4: Pick a Time -Once you've chosen a date, you'll see the available time slots for that day. The system calculates these by looking at: +Once you've chosen a date, you'll see the available time slots for that day displayed as a scrollable list. Each slot shows the start time, and the system groups nearby slots together so you can see gaps at a glance. The system calculates these by looking at: - The salon's opening hours for that day - Existing appointments (there's always a small gap between appointments for cleaning and setup) - Any blocked-out times the salon has set