Extract computeAvailableHours as shared core between GetAvailableHours and GetPreviewAvailableHours, eliminating ~320 lines of duplication. Add GetConflictingBookingsForExceptionHandler (POST) for holiday hours conflict detection with batch service loading, int-based time comparison, and ActiveBookingStatuses constant. Add RESERVATION:placeholder cleanup (24h TTL). Fix scan error propagation in all scheduling row iterations. Add rows.Err() checks.
Restore processImage (images.go) and nonDepositPaymentType (handlers.go) with //nolint:unused — used in test files.
Fix 97 tx.Rollback defers to silently discard expected "tx is closed" error after commit.
Frontend: remove 44 unused shadcn-svelte files, 2 dead components, 9 stale npm deps, prune unused exports.
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
errcheck: add proper error handling with slog.Error for tx.Rollback, key generation, and s3/dav operations. Add nolint comments for intentionally discarded DB scan errors and HTTP write errors.
unused: remove dead code (svcRow type, processImage, nonDepositPaymentType, generateSecureCode, colorBold, nGreen, nRed)
gosimple S1021: merge var declaration with assignment in manage.go
ineffassign: remove dead assignments in settings.go, till.go, images.go
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Add optional excludeUserID parameter to GetTimeBlockersInRange and CheckTimeBlockerOverlap so a user's own RESERVATION entries are excluded from overlap checks. This prevents users from self-blocking on their existing reservation when checking availability or confirming a booking.
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Split multi-day time blockers into per-day segments for subtractTimeSlots. Add normalizeTime helper to strip seconds from HH:MM:SS for consistent comparison. Subtract blockers for all users (not just non-admin) to prevent 409 errors on reserve. Move late-night lock logic to separate block.
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Support out_of_hours query param in GetWorkingHours and GetAvailableHours (admin-only, returns 06:00-22:00 for all days). Replace ukLocation with time.Local for date boundaries. Add OptionalAuth middleware to scheduling routes for admin role detection. Add tests for admin/non-admin/no-auth scenarios, booking respect, and exceptional hours interaction.
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Simplify scheduling handlers by using bookings.total_duration_minutes and bookings.end_time computed columns instead of inline UNION sub-queries for duration calculations. Remove unnecessary ::text casts from end_time columns (using native timestamptz).
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Replace direct *pgxpool.Pool usage with PoolProxy wrapper across the entire backend:
- db.DB renamed to db.Conn (*pgxpool.Pool -> *PoolProxy)
- JWT functions now accept context.Context instead of using context.Background()
- Handler DB calls route through PoolProxy for per-test transaction support
- Fixture/helper/testdb functions accept Querier interface for decoupling
- Query ordering fixed in bookings handlers: COUNT after data query to avoid pgx conn busy
- Time truncation fixed: time.Date instead of Truncate(24*time.Hour) for week start calc
- testmain_test.go files updated with SeedBaseline and NewPoolProxy
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Add name history cleanup to the scheduling pipeline and improve time blocker tests.
- Call CleanupOldNameHistory from GetAvailableHours to periodically purge
old name_history entries (6+ months)
- Add time_blockers_test.go with comprehensive test coverage
- Update time-blockers.go with name_history cleanup logic
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Update booking create/confirm/progress/reserve handlers to support custom_service_ids and custom overrides. Add UNION ALL queries to include custom_services in booking detail, payment summary, scheduling availability, today dashboard, and customer relationship queries.
Ultraworked with Sisyphus (https://github.com/code-yeongyu/oh-my-openagent)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
CleanupOldIdempotencyKeys clears stale idempotency keys from bookings, payments, and till_sales older than 24h (non-pending status). Wired into daily cleanup cycle in default-hours.go.
Tests: clears old bookings/payments/till_sales, preserves recent and pending entries.
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
- Added generic formatAndPreserveCursor() helper on frontend to track
and restore selection caret position during dynamic input sanitization
- Applied to all card inputs, gift card code inputs, and expiry inputs
- Added Luhn validation (isValidLuhn) for saved cards and gift cards
- Rebuilt payments test DB and got 100% green tests
CleanupOldReservations now handles four reservation types:
logged-in (1hr), anonymous (10min), admin-walkin (10min), admin-callin (1hr).
AnonymizeStaleGuestAccounts scrubs PII from guest accounts whose last booking
was 6+ months ago and who have no pending/confirmed bookings. Financial records
remain intact — only personal data is wiped (UK GDPR compliance).
Triggered on every GetAvailableHours call.
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
CHANGES:
Phase 1: Schema
- Change deposits_required default from 3 to 0 for new users
- Add forgiven_no_shows table to track forgiven no-show bookings
Phase 2: No-Show Logic (manage.go)
- CountUnforgivenNoShows(): Count unforgiven no-shows in 6-month period
- ApplyDepositsIfNeeded(): Auto-apply 3 deposits if 2+ no-shows detected
- ForgiveNoShowsForUser(): Clear no-shows and reset deposits on full payment
Phase 3: Slot Reservation System
- Add CleanupOldReservations() to delete 1h+ old reservation blockers
- Call cleanup in GetAvailableHours() on each availability check
- Delete existing user reservation before creating new booking
Phase 4: Minimum Advance Time
- Changed from 48h (deposit-only) to 1h (all users)
- Now universally enforced at booking creation time
Phase 5: Notes-Based Approval Workflow
- If booking has notes (not empty) → status = 'pending' (needs approval)
- If no notes → status = 'confirmed' (auto-approved)
- Uses CASE statement in INSERT for status determination
Phase 6: Late Night Lock
- After 22:00, non-admin users cannot book next morning before 11:00
- Implemented in GetAvailableHours() via artificial blocker subtraction
- Admin users see all times (no restriction)
Phase 7: Admin Notifications
- Notify admin if booking has notes OR is for same day
- All qualifying bookings trigger notification for admin review
VERIFICATION:
✓ Build passes: go build -tags dev ./main.go succeeds
✓ All 7 phases implemented as per dev-approved plan
✓ No breaking changes to existing schemas
✓ Backward compatible with existing booking flow