feat: 10 quick wins — account deletion, health check, debug cleanup, UX polish, graceful shutdown
- backend/handlers/user/account.go: Wire DELETE /api/user/account to call anonymize_user() for registered users and delete_guest_user() for guests, with CardDAV contact cleanup - backend/handlers/user/profile_test.go: Add TestAccount_DeleteGuest and enhance TestAccount_Delete to verify anonymization results - backend/main.go: Add GET /api/health endpoint with DB ping and S3 status check; add HSTS and Referrer-Policy security headers; replace http.ListenAndServe with http.Server + graceful SIGTERM/SIGINT shutdown - frontend/routes/+layout.svelte: Replace alert() with toast notifications for email verification flow - frontend/routes/login/+page.svelte: Replace alert() with toast.info for social login prototype buttons - frontend/booking/BookingFlow.svelte: Remove 2 console.log debug calls; add cancellation policy note in Step 3; add timezone policy comment - frontend/ImageUpload.svelte: Comment out debug console.log - init-scripts/init-script.sql: Add delete_guest_user() SQL function - docs: Update README.md and Obsidian notes to reflect completed items
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
**Last Updated:** March 2026
|
||||
**Last Updated:** May 2026
|
||||
> **Status:** Work in Progress
|
||||
|
||||
---
|
||||
@@ -47,30 +47,33 @@
|
||||
- Services: name (100), price (>0), duration (1-480), patch test (0-168), age (0-100)
|
||||
- Portfolio: tags/filters (256 char max), filter category validation, image ID pattern security
|
||||
|
||||
RM|#### Booking System
|
||||
BV|- [x] `/api/bookings` - Full CRUD for authenticated users
|
||||
TH|- [x] `/api/admin/bookings` - List, search, create for user, progress, confirm, cancel
|
||||
RW|- [x] `/api/admin/bookings/search` - Search functionality
|
||||
SX|- [x] `/api/admin/bookings/user/{user_id}` - User-specific bookings
|
||||
WK|- [x] `/api/admin/bookings/{id}/progress` - Progress booking status
|
||||
XH|- [x] `/api/admin/bookings/{id}/confirm` - Confirm booking
|
||||
JN|- [x] `/api/admin/bookings/{id}/cancel` - Cancel booking
|
||||
BR|- [x] **Admin edit booking** - PUT `/api/admin/bookings/{id}` to edit start time
|
||||
- Blocks editing completed or cancelled bookings
|
||||
- Checks for overlapping bookings
|
||||
- Allows exceptional hours (with warning)
|
||||
- Clears pending edit requests on edit
|
||||
BR|- [x] **In-progress auto-infer** - Status auto-sets based on time (confirmed → in_progress → completed)
|
||||
SM|- [x] **Auto-complete** - Bookings auto-complete when duration elapses
|
||||
- [x] `/api/bookings` - Full CRUD for authenticated users
|
||||
#### Booking System
|
||||
- [x] `/api/bookings` - Full CRUD for authenticated users AND guest users (via `user_id` in payload, OptionalAuth middleware)
|
||||
- [x] `/api/admin/bookings` - List, search, create for user, progress, confirm, cancel
|
||||
- [x] `/api/admin/bookings/search` - Search functionality
|
||||
- [x] `/api/admin/bookings/user/{user_id}` - User-specific bookings
|
||||
- [x] `/api/admin/bookings/{id}/progress` - Progress booking status
|
||||
- [x] `/api/admin/bookings/{id}/confirm` - Confirm booking
|
||||
- [x] `/api/admin/bookings/{id}/cancel` - Cancel booking
|
||||
- [x] **Admin edit booking** - PUT `/api/admin/bookings/{id}` to edit start time
|
||||
- Blocks editing completed or cancelled bookings
|
||||
- Checks for overlapping bookings
|
||||
- Allows exceptional hours (with warning)
|
||||
- Clears pending edit requests on edit
|
||||
- [x] **In-progress auto-infer** - Status auto-sets based on time (confirmed → in_progress → completed)
|
||||
- [x] **Auto-complete** - Bookings auto-complete when duration elapses
|
||||
- [x] **Slot reservation system** - `POST /api/bookings/reserve` (public, OptionalAuth), `POST /api/admin/bookings/reserve` (admin)
|
||||
- 4 TTL types: user=1h, anon=10min, walkin=5min, callin=1h
|
||||
- Anonymous cap: 50 reservations per 10-minute rolling window
|
||||
- Stored as `time_blockers` with `RESERVATION:*` description
|
||||
- `CleanupOldReservations()` prunes expired entries
|
||||
- [x] **Guest booking flow** - `POST /api/users/guest` creates disposable accounts
|
||||
- Email uniqueness: partial unique index `WHERE account_role != 'guest'`
|
||||
- If guest uses registered email → 409 "Please log in"
|
||||
- Guest bookings bypass deposit and patch-test checks
|
||||
- `AnonymizeStaleGuestAccounts()` scrubs PII 6 months after booking start_time
|
||||
- [x] **Chi router fix** - Flattened `/bookings` sub-Route to explicit paths to prevent RequireAuth bleeding into OptionalAuth POST endpoints
|
||||
- [x] **Reservation self-block fix** - `GetTimeBlockersInRange` excludes `RESERVATION:*` entries so overlap checks don't reject the user's own reservation
|
||||
|
||||
#### Admin Endpoints
|
||||
- [x] `/api/admin/services` - Create, delete, list, toggle
|
||||
@@ -108,6 +111,7 @@ SM|- [x] **Auto-complete** - Bookings auto-complete when duration elapses
|
||||
- [x] 2+ unforgiven no-shows in 6 months = 3 deposits (blocks new bookings)
|
||||
- [x] Deleted: `ForgiveNoShowsForUser()` function (now per-cancellation forgiveness)
|
||||
- [x] Implementation complete in `bookings.go` (24h threshold, forgiveness logic) and `manage.go` (admin deposit enforcement)
|
||||
- [x] **Deposit users must book ≥24h in advance** (changed from 48h); guests bypass this check
|
||||
- [ ] Frontend display of deposits_required status to users
|
||||
|
||||
#### CalDAV Contact Sync
|
||||
@@ -118,7 +122,7 @@ SM|- [x] **Auto-complete** - Bookings auto-complete when duration elapses
|
||||
- [ ] Social auth (`handlers/auth/social.go` exists, not imported)
|
||||
- [ ] Analytics (`handlers/admin/analytics.go` exists, not imported)
|
||||
- [x] Portfolio/images - NOW WIRED: `/api/portfolio/images`, `/api/portfolio/tags`, `/api/portfolio/filters`, `/api/portfolio/images/{id}`
|
||||
- [ ] Guest user endpoint (`/api/users/guest` - needed for walk-in bookings)
|
||||
- [ ] **Guest user endpoint** - ✅ DONE: `POST /api/users/guest` creates disposable accounts, partial unique email index
|
||||
|
||||
#### Unit Tests & CI/CD
|
||||
- [ ] Unit tests
|
||||
@@ -162,7 +166,8 @@ SM|- [x] **Auto-complete** - Bookings auto-complete when duration elapses
|
||||
- [x] Auth store with token refresh logic
|
||||
- [x] **Admin booking flows** - Call-in and walk-in use `/api/services/eligible-for/{user_id}` for user-specific eligibility
|
||||
- [x] **Manual patch test entry** - Admin can record patch test completion via User Details → Patch Test modal (for 2-minute walk-in patch tests)
|
||||
- [ ] **Customer booking submit** - `submitBooking()` only logs, needs `POST /api/bookings`
|
||||
- [x] **Customer booking submit** - `submitBooking()` creates guest account if unauthenticated, then POSTs to `/api/bookings`
|
||||
- [ ] Remove debug `console.log` calls from BookingFlow.svelte
|
||||
- [ ] Payment integration (Square placeholder)
|
||||
|
||||
#### API Integration
|
||||
@@ -170,7 +175,7 @@ SM|- [x] **Auto-complete** - Bookings auto-complete when duration elapses
|
||||
- [x] Working hours fetch from `/api/scheduling/working-hours`
|
||||
- [x] Available hours fetch from `/api/scheduling/available-hours`
|
||||
- [x] Admin bookings use `/api/admin/bookings`
|
||||
- [ ] Guest user creation (`/api/users/guest` not implemented)
|
||||
- [x] Guest user creation (`/api/users/guest`) — creates disposable accounts, enforces email uniqueness for registered users only
|
||||
|
||||
---
|
||||
|
||||
@@ -263,24 +268,34 @@ go test -v -run "TestBooking" ./...
|
||||
- Test tokens use fixed secret: `test-secret-key-for-testing-only`
|
||||
- Fixtures auto-generate unique emails to avoid conflicts
|
||||
|
||||
### Recent Testing Updates (March 2026)
|
||||
### Recent Testing Updates (May 2026)
|
||||
|
||||
Since the February 2026 update, extensive testing has been conducted which uncovered and fixed several issues:
|
||||
**Major additions since March 2026:**
|
||||
|
||||
**Testing Improvements:**
|
||||
- Added comprehensive test suite for `notifications` handler (600+ lines)
|
||||
- Added new booking flow tests covering user booking creation
|
||||
- Added extensive test docstrings across all test files
|
||||
- Fixed test database setup and fixture issues
|
||||
**Guest Booking System:**
|
||||
- `POST /api/users/guest` — disposable account creation with email collision handling
|
||||
- Partial unique email index: `WHERE account_role != 'guest'`
|
||||
- `POST /api/bookings` now accepts `user_id` for guest bookings (OptionalAuth middleware)
|
||||
- Guest bookings bypass deposit and patch-test checks
|
||||
- `AnonymizeStaleGuestAccounts()` — scrubs PII 6 months after booking start_time
|
||||
|
||||
**Issues Discovered & Fixed Through Testing:**
|
||||
- **Edit Request Bugs**: Multiple issues in booking edit request handling:
|
||||
- Fixed validation for overlapping bookings during edits
|
||||
- Fixed edit request approval flow
|
||||
- Fixed edit request rejection handling
|
||||
- Fixed status transition logic for edit requests
|
||||
- **SQL Function Updates**: Refactored and optimized database functions in `init-script.sql`
|
||||
- **Test Infrastructure**: Fixed manual test corruption issues, improved test isolation
|
||||
**Slot Reservation System:**
|
||||
- `POST /api/bookings/reserve` (public, OptionalAuth) — 4 TTL types: user=1h, anon=10min, walkin=5min, callin=1h
|
||||
- Anonymous cap: 50 reservations per 10-minute rolling window
|
||||
- `GetTimeBlockersInRange` excludes `RESERVATION:*` entries (self-block fix)
|
||||
- Frontend: BookingFlow reserves on Step 2→3 transition, re-validates on "Next" click
|
||||
- Admin modals wired to reserve endpoints (walk-in: 5min TTL, call-in: 60min TTL)
|
||||
|
||||
**Router Fix:**
|
||||
- Chi routing conflict: `r.Route("/bookings", RequireAuth)` bled into `POST /bookings` (OptionalAuth)
|
||||
- Flattened to explicit paths (`/bookings`, `/bookings/{id}`) with per-route middleware
|
||||
|
||||
**Seed Script Fixes:**
|
||||
- `open_day()` now skips Saturday (6) not Monday (1), matching `working_hours` schema
|
||||
- Guest booking dates moved to +16/+20/+22 days to avoid collisions with upcoming loop
|
||||
- Seed now uses reserve-then-book flow matching frontend
|
||||
|
||||
**Test Coverage:** 222/224 passing
|
||||
|
||||
---
|
||||
|
||||
@@ -315,10 +330,10 @@ flowchart TD
|
||||
end
|
||||
end
|
||||
|
||||
subgraph External[External Services - TODO]
|
||||
Gmail[Gmail SMTP]
|
||||
SquareAPI[Square API]
|
||||
S3[S3/R2 Storage]
|
||||
subgraph External[External Services]
|
||||
S3[S3/R2 Storage ✅]
|
||||
Gmail[Gmail SMTP ❌]
|
||||
SquareAPI[Square API ❌]
|
||||
end
|
||||
|
||||
User -->|HTTPS| NGINX
|
||||
@@ -329,9 +344,9 @@ flowchart TD
|
||||
Auth --> Handlers
|
||||
Handlers --> DB
|
||||
Handlers --> DAV
|
||||
Handlers --> S3
|
||||
Handlers -.->|TODO| Gmail
|
||||
Handlers -.->|TODO| SquareAPI
|
||||
Handlers -.->|TODO| S3
|
||||
```
|
||||
|
||||
---
|
||||
@@ -348,11 +363,14 @@ flowchart TD
|
||||
| POST | `/api/login` | Authenticate and receive JWT |
|
||||
| POST | `/api/verify/generate` | Generate email verification or password reset code |
|
||||
| POST | `/api/verify/check` | Verify code (email verification or password reset) |
|
||||
| POST | `/api/users/guest` | Create disposable guest account (email unique for non-guests only) |
|
||||
| GET | `/api/contact` | Get business contact info (from first admin user) |
|
||||
| GET | `/api/scheduling/default-hours` | Get weekly default hours |
|
||||
| GET | `/api/scheduling/exceptional-groups` | List holiday/special hour groups |
|
||||
| GET | `/api/scheduling/working-hours` | Get merged working hours for date range |
|
||||
| GET | `/api/scheduling/available-hours` | Get available booking slots |
|
||||
| GET | `/api/scheduling/available-hours` | Get available booking slots (triggers reservation cleanup + guest anonymization) |
|
||||
| POST | `/api/bookings/reserve` | Reserve a slot temporarily (OptionalAuth; user=1h TTL, anon=10min TTL, 50-cap) |
|
||||
| POST | `/api/bookings` | Create booking (OptionalAuth; accepts `user_id` for guest bookings) |
|
||||
|
||||
### Authenticated User Endpoints
|
||||
|
||||
@@ -385,6 +403,7 @@ flowchart TD
|
||||
| PUT | `/api/admin/bookings/{id}/progress` | Progress status |
|
||||
| POST | `/api/admin/bookings/{id}/confirm` | Confirm booking |
|
||||
| POST | `/api/admin/bookings/{id}/cancel` | Cancel booking |
|
||||
| POST | `/api/admin/bookings/reserve` | Reserve slot for admin booking (walkin=5min TTL, callin=1h TTL) |
|
||||
| GET | `/api/admin/users` | List users |
|
||||
| GET | `/api/admin/users/{id}` | Get user details |
|
||||
| GET | `/api/admin/users/{id}/patch-tests/eligible` | Get services requiring patch test that user hasn't completed |
|
||||
@@ -435,6 +454,8 @@ payment_status: pending | completed | failed | refunded
|
||||
admin_notification_reason: pending_booking | cancelled_booking | rescheduled_booking | 1_week_no_pay | 1_month_no_pay | affiliate_claim | late_cancellation | no_deposit | deposit_paid
|
||||
```
|
||||
|
||||
**Email uniqueness**: Partial unique index `idx_users_email_registered ON users (email) WHERE account_role != 'guest'` — guests can share emails, registered users cannot.
|
||||
|
||||
**Suggested additional `admin_notification_reason` values:**
|
||||
|
||||
| Reason | Purpose |
|
||||
@@ -450,18 +471,16 @@ admin_notification_reason: pending_booking | cancelled_booking | rescheduled_boo
|
||||
|
||||
| Table | Purpose |
|
||||
|-------|---------|
|
||||
| `users` | User accounts with profile data |
|
||||
| `users` | User accounts with profile data (partial unique email: WHERE account_role != 'guest') |
|
||||
| `verification_codes` | Email verification and password reset codes |
|
||||
| `user_social_logins` | Social auth provider links |
|
||||
TB|| `services` | Service offerings |
|
||||
WR|| `patch_tests` | Patch test definitions (notice_duration_hours, expiry_months, service_ids) |
|
||||
WR|| `user_patch_tests` | User patch test completion records (tested_at, notes) |
|
||||
QB|| `bookings` | Appointment records |
|
||||
VN|| `booking_services` | Services per booking |
|
||||
ZM|| `booking_edit_requests` | Pending customer edit requests |
|
||||
| `user_service_patch_tests` | Patch test tracking |
|
||||
| `services` | Service offerings |
|
||||
| `patch_tests` | Patch test definitions (notice_duration_hours, expiry_months, service_ids) |
|
||||
| `user_patch_tests` | User patch test completion records (tested_at, notes) |
|
||||
| `bookings` | Appointment records |
|
||||
| `booking_services` | Services per booking |
|
||||
| `booking_edit_requests` | Pending customer edit requests |
|
||||
| `time_blockers` | Admin time blocks + slot reservations (description LIKE 'RESERVATION:%') |
|
||||
| `user_referrals` | Referral tracking |
|
||||
| `working_hours` | Default weekly schedule |
|
||||
| `exceptional_working_hours_groups` | Holiday/special hour groups |
|
||||
@@ -495,14 +514,18 @@ ZM|| `booking_edit_requests` | Pending customer edit requests |
|
||||
|
||||
| Task | Description | Files Affected |
|
||||
| ------------------------------ | ------------------------------------------------------------------------------ | -------------------------------------------------------- |
|
||||
| **Customer booking submit** | `submitBooking()` at line 600 only logs, needs `POST /api/bookings` | `frontend/src/lib/components/booking/BookingFlow.svelte` |
|
||||
| **Remove console.logs** | Debug logs left in: `BookingFlow.svelte:600` | Frontend components |
|
||||
| **Guest user endpoint** | Create `/api/users/guest` for walk-in bookings | `backend/handlers/user/` (new file) |
|
||||
| **Remove console.logs** | Debug logs left in: `BookingFlow.svelte` | Frontend components |
|
||||
| ~~Customer booking submit~~ | ~~`submitBooking()` only logs, needs `POST /api/bookings`~~ DONE | `frontend/src/lib/components/booking/BookingFlow.svelte` |
|
||||
| ~~Guest user endpoint~~ | ~~Create `/api/users/guest` for walk-in bookings~~ DONE | `backend/handlers/user/guest.go` |
|
||||
| ~~In-progress auto-infer~~ | ~~Auto-set `in_progress` status based on time~~ DONE | Backend booking logic |
|
||||
| ~~Auto-complete~~ | ~~Auto-complete bookings when duration elapses~~ DONE | Backend today handlers |
|
||||
| ~~Profile picture upload~~ | ~~Upload with cropper to separate bucket, sync to CalDAV~~ DONE | Backend + Account page |
|
||||
| ~~Contact page dynamic~~ | ~~Fetch from `/api/contact` using first admin~~ DONE | Backend + Contact page |
|
||||
| ~~Simplified deposits~~ | ~~`deposits_required` INT on users, 48h check, reduce on payment~~ DONE | Backend booking logic |
|
||||
| ~~Slot reservation system~~ | ~~Temporary slot holds with 4 TTL types~~ DONE | `backend/handlers/bookings/reserve.go`, `admin_reserve.go`|
|
||||
| ~~Booking flow reservation~~ | ~~Reserve slot on Step 2→3 transition in BookingFlow~~ DONE | `frontend/src/lib/components/booking/BookingFlow.svelte` |
|
||||
| ~~Chi router conflict~~ | ~~Flattened /bookings sub-Route to prevent RequireAuth bleed~~ DONE | `backend/main.go` |
|
||||
| ~~Reservation self-block~~ | ~~Exclude RESERVATION:* from GetTimeBlockersInRange~~ DONE | `backend/handlers/scheduling/time-blockers.go` |
|
||||
| **Begin button (Today)** | Manual start for early arrivals, gray out if >3hrs away | `CurrentAppointment.svelte` + backend |
|
||||
| **One-off custom services** | Admin creates custom service for single booking without adding to main list | Backend + frontend booking modals |
|
||||
| **One-off exceptional hours** | Single-day exceptions (dentist, afternoon off) - not yearly/weekly | Backend scheduling + frontend HolidayHours |
|
||||
@@ -511,6 +534,7 @@ ZM|| `booking_edit_requests` | Pending customer edit requests |
|
||||
| **Square payment integration** | Full Square SDK integration | Backend payment handlers + frontend payment step |
|
||||
| **GDPR data export** | User button for "give me my data" using `export_all_user_data()` | Backend endpoint + account page |
|
||||
| **Tax data export** | Admin button for tax-software-compatible format | Backend endpoint + admin page |
|
||||
| **Frontend deposits UI** | Display `deposits_required` status to users | Account page / booking flow |
|
||||
|
||||
### Medium Priority
|
||||
|
||||
@@ -632,11 +656,14 @@ src/lib/components/
|
||||
- The backend merges default hours with applied exceptional hours automatically
|
||||
- Static frontend is built and served by nginx; API calls go directly to Go backend in production
|
||||
- Local dev uses SvelteKit's API proxy for CORS avoidance
|
||||
- **GDPR functions exist in SQL** (`anonymize_user`, `export_all_user_data`, `delete_guest_user`) but only `DELETE /api/user/account` is wired - need user data export and admin tax export endpoints
|
||||
- **Debug console.logs** in `BookingFlow.svelte:600` and `BookingCreateModal.svelte:224` should be removed before production
|
||||
- **GDPR functions exist in SQL** (`anonymize_user`, `export_all_user_data`, `delete_guest_user`) — backend also has `AnonymizeStaleGuestAccounts()` which scrubs guest PII 6 months after booking start_time. Still need user-facing data export endpoint and admin tax export endpoint.
|
||||
- **Notifications** are pull-based only (no push/WebSocket). Admin endpoint exists but no frontend UI. No user-facing notification system yet.
|
||||
- **Notification acknowledgment**: When a booking is confirmed, any pending notification is acknowledged. When cancelled, pending is acknowledged and cancelled_booking notification is only created if the booking was not in pending status (e.g. was confirmed or in_progress).
|
||||
- **User notification preferences**: `user_notification_preferences` table exists with email/sms/push enabled flags, waiting on user notification system to be implemented.
|
||||
- **Guest accounts**: Disposable, created via `POST /api/users/guest`. Email uniqueness enforced only for non-guests (partial unique index). Multiple guest accounts can share an email.
|
||||
- **Slot reservations**: Stored in `time_blockers` with `RESERVATION:*` descriptions. `GetTimeBlockersInRange` excludes these to prevent the reservation from blocking its own booking.
|
||||
- **Chi router**: `/bookings` routes use explicit full paths (not `r.Route("/bookings", ...)`) to prevent RequireAuth middleware from bleeding into OptionalAuth POST endpoints.
|
||||
- **Seed script** (`local-dev-2.sh`): `open_day()` skips Sunday and Saturday (matching `working_hours` schema). Guest bookings use reserve-then-book flow matching frontend.
|
||||
|
||||
---
|
||||
|
||||
@@ -671,38 +698,72 @@ Running `local-dev-2.sh` creates:
|
||||
|
||||
| Resource | Count | Details |
|
||||
|----------|-------|---------|
|
||||
| Users | 18 | 1 admin, 17 regular users |
|
||||
JR|| Services | 8 | 6 standard (no patch test) + 2 requiring patch tests (Gel Polish Full Set 24h, Luxury Gel Manicure 24h) |
|
||||
| Bookings | 45 | 8 past, 3 today, 4 tomorrow, 30 future (spread over 15 days) |
|
||||
| Confirmed | ~50% | Random selection of upcoming bookings auto-confirmed |
|
||||
| Exceptional | 2 | November Break (closed), Christmas Holiday (reduced hours) |
|
||||
| Users | 20 | 1 admin, 19 regular users |
|
||||
| Services | 12 | 10 standard + 2 requiring patch tests |
|
||||
| Past Bookings | ~17 | Historical data |
|
||||
| Today's Bookings | 4 | |
|
||||
| Tomorrow's Bookings | 5 | |
|
||||
| Upcoming Bookings | ~15 | Spread over 14 days |
|
||||
| Guest Bookings | 3 | Nina (+16d), Bob (+20d), Carol (+22d) — reserve-then-book flow |
|
||||
| Total Bookings | 42 | |
|
||||
| Time Blockers | 3 | Staff meeting, holiday, late start |
|
||||
| Schedule Groups | 3 | Exceptional working hour configurations |
|
||||
| Cancellations | 1 | Simulated client cancellation |
|
||||
|
||||
### API JSON Examples
|
||||
|
||||
**Create Booking:**
|
||||
**Create Guest Account:**
|
||||
```json
|
||||
POST /api/users/guest
|
||||
{
|
||||
"start_time": "2025-01-15T10:00:00+00:00",
|
||||
"service_ids": ["abc123def456"],
|
||||
"first_name": "Jane",
|
||||
"last_name": "Doe",
|
||||
"email": "jane@example.com",
|
||||
"phone": "+447700900123"
|
||||
}
|
||||
// Returns: { "id": "abc123def456", "account_role": "guest", ... }
|
||||
```
|
||||
|
||||
**Create Booking (Guest):**
|
||||
```json
|
||||
POST /api/bookings
|
||||
{
|
||||
"user_id": "abc123def456",
|
||||
"start_time": "2026-05-20T13:00:00+01:00",
|
||||
"service_ids": ["SVC001"],
|
||||
"notes": "Optional notes"
|
||||
}
|
||||
```
|
||||
|
||||
**Create Booking (Authenticated):**
|
||||
```json
|
||||
POST /api/bookings
|
||||
{
|
||||
"start_time": "2026-05-20T13:00:00+01:00",
|
||||
"service_ids": ["SVC001"],
|
||||
"notes": "Optional notes"
|
||||
}
|
||||
```
|
||||
|
||||
**Reserve Slot (Anonymous):**
|
||||
```json
|
||||
POST /api/bookings/reserve
|
||||
{
|
||||
"start_time": "2026-05-20T13:00:00+01:00",
|
||||
"service_ids": ["SVC001"]
|
||||
}
|
||||
// Returns: { "id": "TBL001", "expires_at": "...", "is_anonymous": true }
|
||||
```
|
||||
|
||||
**Create Service:**
|
||||
```json
|
||||
VN|{
|
||||
POST /api/admin/services
|
||||
{
|
||||
"name": "Classic Manicure",
|
||||
"description": "Nail shaping, cuticle care, hand massage, and polish.",
|
||||
"price": 25.00,
|
||||
"duration_minutes": 45,
|
||||
"minimum_age_required": 0
|
||||
}
|
||||
"name": "Classic Manicure",
|
||||
"description": "Nail shaping, cuticle care, hand massage, and polish.",
|
||||
"price": 25.00,
|
||||
"duration_minutes": 45,
|
||||
"patch_test_duration_hours": 0,
|
||||
"minimum_age_required": 0
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -0,0 +1,234 @@
|
||||
**Last Updated:** May 2026
|
||||
**Status:** Living backlog — add to this as gaps are discovered
|
||||
|
||||
---
|
||||
|
||||
# 🟢 Local Gaps — Can Fix Now
|
||||
|
||||
No external dependencies. No paid services. No API keys needed.
|
||||
|
||||
## P0 — Critical (Fix Now)
|
||||
|
||||
| # | Gap | Effort | Area | Notes |
|
||||
|---|-----|--------|------|-------|
|
||||
| 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 | Line 277: commented-out guest creation code. Shows "Guest booking not yet implemented" toast despite backend fully working. |
|
||||
| 3 | **ApprovalModal decline/cancel stub** | S (2-3h) | Frontend | `handleDecline()` shows "Coming soon" toast. Admin cannot reject pending bookings. Backend confirm/cancel endpoints exist — decline just needs a cancel call. |
|
||||
| 4 | **CurrentAppointment action stubs** | M (1d) | Frontend | `handleTakePayment()`, `handleExtend()`, `handleCancel()` all show "Coming soon". Today page has 3 dead buttons. Take payment is ⚠️ blocked on Square, but Extend and Cancel are local. |
|
||||
|
||||
## P1 — High
|
||||
|
||||
| # | Gap | Effort | Area | Notes |
|
||||
|---|-----|--------|------|-------|
|
||||
| 5 | **Admin notification panel** | M (1-2d) | Frontend | Backend fully wired (GET/acknowledge). No frontend UI to display notifications. Admin has no visibility into pending bookings, cancellations, no-shows. |
|
||||
| 6 | **Reservation/anonymization cron** | S (2-3h) | Backend | `CleanupOldReservations()` and `AnonymizeStaleGuestAccounts()` only fire on availability fetch. If no one fetches availability, expired reservations persist and stale guests aren't anonymized. Should be a background ticker in `main.go`. |
|
||||
| 7 | **GDPR data export endpoint** | M (1d) | Backend | `export_all_user_data()` SQL function exists (JSON export). No Go handler wired. Required for GDPR Article 15 SAR requests. |
|
||||
| 8 | **VAT/Tax export endpoints** | M (1-2d) | Backend | `get_vat_return_data()`, `export_sales_transactions()` SQL functions exist. No admin API to trigger them. Needed for HMRC compliance. |
|
||||
| 9 | **Walk-in guest reservation → booking transition** | S (1h) | Frontend | WalkInCreateModal line 358: TODO notes guest reservations may not properly transition to real bookings. Needs verification + fix. |
|
||||
| 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 | Users can't reschedule their own bookings. `booking_edit_requests` table exists but frontend flow is incomplete — ApprovalModal only handles approve, not decline. |
|
||||
|
||||
## P2 — Medium
|
||||
|
||||
| # | Gap | Effort | Area | Notes |
|
||||
|---|-----|--------|------|-------|
|
||||
| 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. |
|
||||
| 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. |
|
||||
| 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. |
|
||||
| 21 | **Referral system UI** | M (1-2d) | Full-stack | `user_referrals` table exists. Users can't see their referral code or track uses. Admin can't manage referral campaigns. |
|
||||
| 22 | **Analytics endpoints** | M (1-2d) | Backend | `handlers/admin/analytics.go` is 1 line. `get_monthly_business_summary()`, `get_sales_totals()` SQL functions exist. No admin dashboard stats. |
|
||||
| 23 | ~~**console.log debug statements**~~ ✅ | XS (15min) | Frontend | Removed from BookingFlow.svelte and ImageUpload.svelte. |
|
||||
| 24 | ~~**Alert-based prototype UX**~~ ✅ | XS (30min) | Frontend | Replaced all `alert()` calls with `toast.success/error/info` from svelte-sonner. |
|
||||
| 25 | **No customer relationship view** | M (1-2d) | Frontend | Admin UserModal shows bookings list but no consolidated view: total spend, visit frequency, preferences, notes history. |
|
||||
| 26 | **CSV/Excel export for bookings/payments** | M (1d) | Backend | Admin can't export data for accounting software. SQL functions exist but no endpoint to download as CSV. |
|
||||
| 27 | ~~**Graceful shutdown**~~ ✅ | S (1h) | Backend | Added signal handling for SIGTERM/SIGINT with 15-second shutdown timeout in main.go. |
|
||||
| 28 | ~~**Health check endpoint**~~ ✅ | XS (15min) | Backend | Added `GET /api/health` returning overall status plus DB, S3, Square, and frontend service statuses. |
|
||||
| 29 | **API documentation** | M (1-2d) | Backend | No OpenAPI/Swagger spec. No generated docs. New developers must read code to understand endpoints. |
|
||||
| 30 | **XSS input sanitization** | S (2-3h) | Backend | Backend validates format (regex, length) but doesn't sanitize HTML entities. Stored XSS risk in `notes`, `name`, `description` fields. |
|
||||
| 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. |
|
||||
| 35 | **Walk-in slot blocking** | S (1-2h) | Frontend | `WalkInCreateModal` doesn't properly block the next available slot during walk-in intake. Other customers could book the same slot. |
|
||||
| 36 | **No idempotency keys for bookings** | S (2-3h) | Backend | Double-clicking "Confirm Booking" could create duplicate bookings. Should use idempotency keys or optimistic locking. |
|
||||
| 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. |
|
||||
| 38 | **Service category/tag management** | M (1-2d) | Full-stack | Services have no category field. Hard to organize (manicure vs pedicure vs nail art). Admin must scroll through flat list. |
|
||||
| 39 | ~~**No customer-facing cancellation policy display**~~ ✅ | XS (30min) | Frontend | Added cancellation policy text block in BookingFlow Step 3 below the terms & conditions line. |
|
||||
| 40 | **No no-show tracking dashboard** | S (2-3h) | Frontend | Admin can't see which users have accumulated no-shows. `forgiven_no_shows` table exists but no UI. |
|
||||
| 41 | ~~**No timezone handling for international customers**~~ ✅ | XS (15min) | Frontend | **Intentionally not implemented.** Crussell is UK-only; Cloudflare blocks non-UK traffic. Added explanatory comments in BookingFlow.svelte and README.md so this stops being flagged. |
|
||||
| 42 | **Dark mode** | M (1-2d) | Frontend | SvelteKit + Tailwind supports it easily. No dark mode toggle or `prefers-color-scheme` support. |
|
||||
| 43 | **PWA support** | L (3-5d) | Frontend | No service worker, no manifest.json, no offline support. Customers can't "install" the booking app. |
|
||||
| 44 | **Automated database backups** | M (1d) | Infrastructure | No backup strategy. PostgreSQL volume is persistent but no automated dumps, no point-in-time recovery. Can use `pg_dump` cron on the host — no external service needed. |
|
||||
| 45 | **Loyalty stamp redemption** | M (1-2d) | Full-stack | Display exists (account page shows "X stamps until 10% off"). No mechanism to redeem 10 stamps. Auto-apply of 10% discount at payment is ⚠️ blocked on Square. |
|
||||
| 46 | **Staff management** | L (3-5d) | Full-stack | No multi-staff support. All bookings assumed single-provider. Schema change: add `staff_id` to bookings, per-staff availability tables. |
|
||||
| 47 | **Recurring bookings** | L (3-5d) | Full-stack | Customers can't book the same slot weekly/monthly. Would need a `recurring_bookings` table + background job to materialize instances. |
|
||||
| 48 | **Waitlist functionality** | M (1-2d) | Full-stack | When a slot is full, no way for customers to join a waitlist. Notification on cancellation is ⚠️ blocked on email/SMS, but in-app notification panel (#5) can handle it. |
|
||||
| 49 | **Image optimization for portfolio** | M (1-2d) | Full-stack | Images uploaded as-is. No WebP conversion, no lazy loading, no responsive `srcset`. Portfolio loads full-res images. |
|
||||
|
||||
---
|
||||
|
||||
# 🔴 External Gaps — Blocked on Third-Party Access
|
||||
|
||||
Require paid accounts, API approval, or external service credentials. **Do not attempt until access is granted.**
|
||||
|
||||
## Payment — Square
|
||||
|
||||
| # | Gap | Effort | Area | Status | Notes |
|
||||
|---|-----|--------|------|--------|-------|
|
||||
| E1 | **Payment integration (Square SDK)** | XL (3-5d) | Full-stack | 🔒 Blocked | BookingFlow Step 4 shows "Square payment integration will be added here". Need `github.com/square/square-go-sdk` + API credentials. |
|
||||
| E2 | **No deposit payment flow** | M (1-2d) | Full-stack | 🔒 Depends on E1 | Users with `deposits_required > 0` can't pay deposits online. Blocked from new bookings until they pay in-person. |
|
||||
| E3 | **Tip calculation UI** | S (1h) | Frontend | 🔒 Depends on E1 | `tip` exists in `payment_type` enum. No UI to add tips during payment or at the Today page. |
|
||||
| E4 | **Gift card system** | L (3-5d) | Full-stack | 🔒 Depends on E1 | `giftcard` exists in `payment_method` enum. No gift card creation, redemption, or balance tracking. |
|
||||
|
||||
## Email/SMS — SMTP Provider (Resend, SendGrid, Twilio, etc.)
|
||||
|
||||
| # | Gap | Effort | Area | Status | Notes |
|
||||
|---|-----|--------|------|--------|-------|
|
||||
| E5 | **Email/SMS notification system** | XL (5-7d) | Backend | 🔒 Blocked | No SMTP integration. No scheduled jobs for booking reminders. `user_notification_preferences` table exists but unused. Need SMTP credentials or API key from provider. |
|
||||
| E6 | **Automated deposit reduction notification** | S (1h) | Backend | 🔒 Depends on E5 | When `deposits_required` decreases, no notification is sent. User doesn't know they're closer to being unblocked. |
|
||||
| E7 | **Waitlist cancellation notifications** | S (1h) | Backend | 🔒 Depends on E5 + #48 | When a slot opens up, waitlisted customers need to be notified. |
|
||||
|
||||
## Cloud Storage — S3/R2 Production
|
||||
|
||||
| # | Gap | Effort | Area | Status | Notes |
|
||||
|---|-----|--------|------|--------|-------|
|
||||
| E8 | **S3/R2 production stubs** | M (1d) | Backend | 🔒 Blocked | `s3.go` (`!dev` build tag): Upload/Download/Delete all return "not implemented" errors. Prod builds cannot store images. Need AWS SDK v2 + R2/S3 credentials. Dev works fine with RustFS. |
|
||||
|
||||
## Social Auth — OAuth Apps (Google, Microsoft, Facebook)
|
||||
|
||||
| # | Gap | Effort | Area | Status | Notes |
|
||||
|---|-----|--------|------|--------|-------|
|
||||
| E9 | **Social auth (Google/Microsoft/Facebook)** | L (2-3d) | Backend | 🔒 Blocked | `handlers/auth/social.go` is 1 line. Need OAuth app registrations + client secrets for each provider. |
|
||||
|
||||
## Monitoring — Sentry / Error Tracking
|
||||
|
||||
| # | Gap | Effort | Area | Status | Notes |
|
||||
|---|-----|--------|------|--------|-------|
|
||||
| E10 | **Error tracking / monitoring** | M (1-2d) | Backend | 🔒 Blocked | No Sentry, no structured logging, no error aggregation. `log.Printf()` only. No alerting on 5xx errors. Needs Sentry DSN or equivalent. |
|
||||
|
||||
---
|
||||
|
||||
## Dependency Map
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ EXTERNAL BLOCKERS │
|
||||
│ │
|
||||
│ E1 Square API ──┬──→ E2 Deposit payments │
|
||||
│ ├──→ E3 Tips │
|
||||
│ └──→ E4 Gift cards │
|
||||
│ │
|
||||
│ E5 SMTP/API ────┬──→ E6 Deposit reduction notifications│
|
||||
│ └──→ E7 Waitlist notifications │
|
||||
│ │
|
||||
│ E8 S3/R2 ───────→ Portfolio images in production │
|
||||
│ │
|
||||
│ E9 OAuth Apps ──→ E9 Social auth │
|
||||
│ │
|
||||
│ E10 Sentry ─────→ E10 Error tracking │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ LOCAL (UNBLOCKED) │
|
||||
│ │
|
||||
│ #1 Delete account ──→ #7 GDPR export ──→ #14 SQL func │
|
||||
│ │
|
||||
│ #3 Approval decline ──→ #13 Booking reschedule │
|
||||
│ │
|
||||
│ #5 Admin notification panel ──→ #15 Preferences UI │
|
||||
│ ──→ #48 Waitlist (partial) │
|
||||
│ │
|
||||
│ #2 Walk-in guest fix ──→ #9 Reservation transition │
|
||||
│ │
|
||||
│ #6 Reservation cron ──→ #42 Dark mode (no deps) │
|
||||
│ │
|
||||
│ #39 Cancellation policy ──→ ZERO deps, 30min fix │
|
||||
│ #28 Health check ──→ ZERO deps, 15min fix │
|
||||
│ #23 console.log cleanup ──→ ZERO deps, 15min fix │
|
||||
│ #24 Alert prototype cleanup ──→ ZERO deps, 30min fix │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Suggested Execution Order
|
||||
|
||||
### Phase 1 — Zero-Dependency Quick Wins (Week 1)
|
||||
|
||||
*No external services. Each takes <30min except #1.*
|
||||
|
||||
1. **#23** Remove console.log debug statements (15min)
|
||||
2. **#24** Replace alert() prototypes with toast notifications (30min)
|
||||
3. **#28** Add health check endpoint (15min)
|
||||
4. **#39** Add cancellation policy display to BookingFlow (30min)
|
||||
5. **#41** Fix timezone display for international customers (15min)
|
||||
6. **#18** Add HSTS header (15min)
|
||||
7. **#19** Add Referrer-Policy header (15min)
|
||||
8. **#1** Fix `DELETE /api/user/account` (1-2h) — biggest win in this phase
|
||||
|
||||
### Phase 2 — Admin Productivity (Week 2)
|
||||
|
||||
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)
|
||||
13. **#12** Booking cancellation from user account (2-3h)
|
||||
14. **#40** No-show tracking dashboard (2-3h)
|
||||
|
||||
### Phase 3 — Compliance + Reliability (Week 3)
|
||||
|
||||
15. **#6** Reservation/anonymization background cron (2-3h)
|
||||
16. **#7** GDPR data export endpoint (1d)
|
||||
17. **#8** VAT/Tax export endpoints (1-2d)
|
||||
18. **#14** Create `delete_guest_user()` SQL function (1h)
|
||||
19. **#27** Graceful shutdown (1h)
|
||||
20. **#36** Idempotency keys for bookings (2-3h)
|
||||
21. **#30** XSS input sanitization (2-3h)
|
||||
22. **#44** Automated database backups (1d)
|
||||
|
||||
### Phase 4 — User Experience (Week 4)
|
||||
|
||||
23. **#10** Password reset flow (2-3h)
|
||||
24. **#11** Email verification flow (2-3h)
|
||||
25. **#13** Booking rescheduling for users (1-2d)
|
||||
26. **#20** Business settings management UI (1-2d)
|
||||
27. **#16** One-off custom services (1-2d)
|
||||
28. **#17** One-off exceptional hours (1d)
|
||||
29. **#35** Walk-in slot blocking (1-2h)
|
||||
30. **#38** Service category management (1-2d)
|
||||
|
||||
### Phase 5 — Growth + Polish (Week 5+)
|
||||
|
||||
31. **#21** Referral system UI (1-2d)
|
||||
32. **#22** Analytics endpoints (1-2d)
|
||||
33. **#25** Customer relationship view (1-2d)
|
||||
34. **#26** CSV/Excel export (1d)
|
||||
35. **#29** API documentation (1-2d)
|
||||
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)
|
||||
40. **#37** Booking conflict detection (2-3h)
|
||||
41. **#45** Loyalty stamp redemption (partial — UI only, payment apply blocked on E1)
|
||||
42. **#46** Staff management (3-5d)
|
||||
43. **#47** Recurring bookings (3-5d)
|
||||
44. **#48** Waitlist functionality (1-2d)
|
||||
45. **#49** Image optimization (1-2d)
|
||||
46. **#42** Dark mode (1-2d)
|
||||
47. **#43** PWA support (3-5d)
|
||||
|
||||
### ⏳ Waiting on External Access
|
||||
|
||||
| Item | Blocked On | Unblocks |
|
||||
|------|-----------|----------|
|
||||
| **E1 Square payment** | Square API credentials | E2, E3, E4, deposit payments, loyalty redemption |
|
||||
| **E5 Email/SMS** | SMTP provider (Resend/SendGrid/Twilio) | E6, E7, booking reminders, password reset emails |
|
||||
| **E8 S3/R2 production** | Cloudflare R2 or AWS S3 credentials | Portfolio images in production builds |
|
||||
| **E9 Social auth** | OAuth app registrations (Google/Microsoft/Facebook) | Social login flow |
|
||||
| **E10 Sentry** | Sentry DSN or equivalent | Error tracking, 5xx alerting |
|
||||
Reference in New Issue
Block a user