From 1f3d834f0c8fb7934dcfad29de2d2064ce40722c Mon Sep 17 00:00:00 2001 From: Stephen Adamson Date: Thu, 30 Jul 2026 19:52:09 +0100 Subject: [PATCH] docs: add feature catalog and update gap backlog Feature Catalog: comprehensive audit of all 15 feature areas with cross-references, verified against source code (18 parallel deep-dive agents). Gap Backlog: rewritten with dev-to-prod integration framing, 40 items across Pre-Launch/MVP/Stretch/Tech Debt. Gitleaks: whitelist obsidian/ docs containing curl examples. --- .gitleaks.toml | 2 + obsidian/Crussell/Feature Catalog.md | 1000 +++++++++++++++++ .../Crussell/Future Work - Gap Backlog.md | 150 ++- 3 files changed, 1096 insertions(+), 56 deletions(-) create mode 100644 obsidian/Crussell/Feature Catalog.md diff --git a/.gitleaks.toml b/.gitleaks.toml index 07c122b..f14480b 100644 --- a/.gitleaks.toml +++ b/.gitleaks.toml @@ -18,4 +18,6 @@ paths = [ "sabredav/composer.json", # Frontend env examples "frontend/.env.production", + # Obsidian docs — contain API curl examples with Authorization headers + "obsidian/", ] diff --git a/obsidian/Crussell/Feature Catalog.md b/obsidian/Crussell/Feature Catalog.md new file mode 100644 index 0000000..8b2321c --- /dev/null +++ b/obsidian/Crussell/Feature Catalog.md @@ -0,0 +1,1000 @@ +# Feature Catalog + +A comprehensive catalog of every feature in the Crussell nail salon booking platform. Each feature area includes a layman description, sub-features, and cross-references to related areas. Built from a full codebase audit (Go 1.26.5 backend + SvelteKit 5 SPA + PostgreSQL 17). + +**Last Updated:** July 2026 + +--- + +## 1. Booking System + +Three booking flows for creating appointments, each with its own entry point and reservation TTL. The entire system revolves around slots — 15-minute time windows that are blocked by reservations, bookings, time blockers, holidays, and lunch breaks. + +**Related:** [[Payments|2. Payments]] (deposits), [[Availability & Scheduling|3. Availability & Scheduling]] (availability), [[Notifications|11. Notifications]] (booking alerts) + +### 1.1 Self-Service Booking (Customer-Facing) +**What it does:** Customers book online through a 5-step wizard on the website. Select services, pick a date/time, fill in details, and optionally pay a deposit. The slot is temporarily held (reserved) while they complete the flow so nobody else can take it. + +**How it works:** +- Step 0 (guest only): Welcome — login or continue as guest +- Step 1: Service selection — choose from the catalog; services requiring patch tests are grayed out if ineligible +- Step 2: Date & Time — calendar date picker + 15-min time slot grid; slot reserved on backend +- Step 3: Details — guest fills name/email/phone; authenticated users skip +- Step 4: Payment/Confirmation — if deposit required → card payment form; if not → booking confirmed immediately + +**Layman summary:** "Walk through the online booking form — choose what you want, pick a time, tell us who you are, and you're booked." + +**Related:** [[Services Catalog|1.5 Services Catalog]], [[Patch Tests|1.6 Patch Tests]], [[Deposit System|1.2 Deposit System]], [[Slot Reservation System|1.8 Slot Reservation System]] + +### 1.2 Deposit System +**What it does:** Some customers need to pay a 20% deposit upfront to secure a booking (enforced after accumulating no-shows). The deposit must be paid 24 hours before the appointment, or the slot becomes vulnerable to eviction. + +**Key rules:** +- Deposit required = 20% of booking total +- Deadline = 24 hours before start time +- If unpaid by deadline → booking enters `pending_release` (slot vulnerable) +- If another booking claims the slot → evicted to `deposit_lapsed` +- Paying ≥20% at any point promotes back to `confirmed` + +**Layman summary:** "A security deposit system. Some bookings need 20% up front. If you don't pay in time, someone else can take your slot." + +**Related:** [[Payments|2. Payments]], [[Booking Statuses & Transition Rules|1.13 Booking Statuses]], [[No-Show Tracking|1.11 Cancellation]] + +### 1.3 Walk-In Booking (Admin) +**What it does:** Admin creates a booking for a customer who walks in without an appointment. The slot is held for 15 minutes while the customer is at the counter. + +**Layman summary:** "Customer walks in off the street. Staff quickly book them in with a 15-minute hold." + +**Related:** [[Admin Dashboard|5. Admin Dashboard]], [[Today Page|7. Today Page]] (Walk-In Wizard) + +### 1.4 Call-In Booking (Admin) +**What it does:** Admin creates a booking over the phone while the customer is on the line. Same as walk-in but service durations are calculated from the services selected. + +**Layman summary:** "Customer calls up. Staff book them in while on the phone." + +**Related:** [[Admin Dashboard|5. Admin Dashboard]], [[Today Page|7. Today Page]] (Call-In Wizard) + +### 1.5 Services Catalog +**What it does:** The salon's list of services (e.g., "Gel Manicure", "Acrylic Extensions"). Each has a price, duration, minimum age requirement, and optional patch test requirement. + +**Layman summary:** "The price list. Every service has a set duration and may need a patch test." + +**Related:** [[Custom Services|1.7 Custom Services]], [[Patch Tests|1.6 Patch Tests]] + +### 1.6 Patch Tests +**What it does:** Some services (e.g., acrylics) require a patch test 24-48 hours before the appointment. The system tracks which customers have taken which tests and when they expire (6 months). + +**Layman summary:** "You need an allergy test before some treatments. The system remembers when you had it and nags if it's expired." + +**Related:** [[Services Catalog|1.5 Services Catalog]], [[Admin Dashboard|5. Admin Dashboard]] (Patch Test Management) + +### 1.7 Custom Services +**What it does:** One-off or special-request services not in the permanent catalog (e.g., "Bridal Party Special"). Admin can create them on the fly and optionally promote them to permanent services later. + +**Layman summary:** "Special requests. If a customer wants something not on the menu, staff can create a custom service just for that booking." + +**Related:** [[Services Catalog|1.5 Services Catalog]], [[Admin Dashboard|5. Admin Dashboard]] (Services & Custom Services Management) + +### 1.8 Slot Reservation System +**What it does:** When a customer picks a time in the booking wizard, the slot is temporarily blocked so nobody else can take it. Reservations are stored as time blocker entries with type-specific TTLs. + +**TTLs by type:** +- Logged-in user: 1 hour +- Anonymous user: 10 minutes +- Admin walk-in/call-in: 15 minutes +- Edit requests: 24 hours + +**Layman summary:** "While you're filling out the booking form, that time slot is 'held' so nobody else grabs it." + +**Related:** [[Availability & Scheduling|3. Availability & Scheduling]], [[Slot Reservation TTLs|1.14 Slot Reservation TTLs]] + +### 1.9 Overlap Prevention +**What it does:** Ensures no two bookings occupy the same time slot. Uses database-level `FOR UPDATE` row locks inside transactions. Also checks against time blockers (admin-created blocks, holidays, recurring blocks). + +**Layman summary:** "The system makes sure nobody is double-booked. If a slot is taken, it's taken." + +**Related:** [[Availability & Scheduling|3. Availability & Scheduling]] (Time Blockers, Holidays), [[Time Blockers|3.4 Time Blockers]] + +### 1.10 Booking Edit Requests +**What it does:** Customers can request to change their booking time or services without admin involvement. If no payments exist and the booking is >48 hours away, changes are auto-approved. Otherwise, admin reviews and approves/denies. + +**Layman summary:** "Need to change your appointment? You can request it online. Simple changes happen automatically; complex ones need staff approval." + +**Related:** [[Admin Dashboard|5. Admin Dashboard]], [[Notifications|11. Notifications]] (edit_requested), [[Availability & Scheduling|3. Availability & Scheduling]] + +### 1.11 Cancellation +**What it does:** Customers can cancel their own bookings (status goes to `client_cancelled`). Admin can cancel any booking with optional fee forgiveness and refund processing. Refund amount depends on how much notice was given. + +**Refund tiers:** +- >72 hours: Full refund +- 24-72 hours: Keep up to 50% as deposit +- <24 hours: No refund + +**Layman summary:** "Need to cancel? Do it online. You'll get a full refund if you cancel more than 3 days before." + +**Related:** [[Payments|2. Payments]] (Refunds), [[Notifications|11. Notifications]], [[Deposit System|1.2 Deposit System]] + +### 1.12 Idempotency Keys +**What it does:** Prevents duplicate bookings if the customer accidentally submits the form twice. Each submission has a unique key; if the same key is used again, the existing booking is returned instead of creating a new one. + +**Layman summary:** "If you accidentally click 'Book Now' twice, you won't end up with two appointments." + +**Related:** [[Double-Payment Prevention|2.9 Double-Payment Prevention]], [[Background Jobs|13. Background Jobs]] (idempotency key cleanup) + +### 1.13 Booking Statuses & Transition Rules +**What it does:** Nine booking statuses govern the lifecycle of each appointment. Each has valid transitions enforced by the `ProgressBookingHandler`. + +**Statuses:** `pending` (needs admin approval) → `confirmed` → `in_progress` → `completed` (terminal). `client_cancelled`, `we_cancelled`, `no_show` (terminal). `pending_release` (deposit deadline passed, slot vulnerable) → `deposit_lapsed` (terminal, slot given away). + +**Valid transitions:** +- `pending` → confirmed, completed, client_cancelled, we_cancelled +- `confirmed` → in_progress, completed, client_cancelled, we_cancelled +- `in_progress` → completed +- `pending_release` → pending, confirmed, client_cancelled, we_cancelled +- `no_show`, `deposit_lapsed` — terminal, no transitions + +**Layman summary:** "Each booking has a status that moves forward: waiting approval, confirmed, in progress, done. Some statuses (cancelled, no-show) are final." + +**Related:** [[Deposit System|1.2 Deposit System]] (pending_release/deposit_lapsed), [[Today Page|7. Today Page]] (ProgressBooking) + +### 1.14 Slot Reservation TTLs +**What it does:** When a time slot is "held" during the booking process, it's stored as a special time blocker entry with a type-specific time-to-live. + +| Reservation Type | TTL | DB Pattern | +|-----------------|-----|------------| +| Logged-in user | 1 hour | `RESERVATION:user::` | +| Anonymous user | 10 minutes | `RESERVATION:anon::` | +| Admin walk-in | 15 minutes | `RESERVATION:admin:walkin::` | +| Admin call-in | 15 minutes | `RESERVATION:admin:callin::` | +| Edit request | 24 hours | `RESERVATION:edit_request:` | + +Anonymous rate cap: max 50 reservations per IP in 10 minutes. + +**Layman summary:** "Different types of holds have different time limits — logged-in users get an hour, walk-in customers get 15 minutes." + +**Related:** [[Slot Reservation System|1.8 Slot Reservation System]], [[Background Jobs|13. Background Jobs]] (reservation cleanup) + +--- + +## 2. Payments + +Multi-method payment system accepting Square (card terminal & online), cash, gift cards, and saved cards. Supports deposits, full/partial payments, tips on completed bookings, and refunds with notice-period tiers. + +**Related:** [[Booking System|1. Booking System]] (deposits), [[Gift Cards|4. Gift Cards]] (pay by gift card), [[Admin Dashboard|5. Admin Dashboard]] (till purchases) + +### 2.1 Online Card Payment (Square Web Payments SDK) +**What it does:** Customers pay online using a credit/debit card via Square's Web Payments SDK. Used for deposits, full payments, balance payments, and tips. + +**Layman summary:** "Pay online with your card — just like any online shop." + +**Related:** [[Saved Cards|2.5 Saved Cards]], [[Double-Payment Prevention|2.9 Double-Payment Prevention]] + +### 2.2 Square Terminal (In-Person Card) +**What it does:** Admin initiates a card payment on the Square Terminal. Customer taps or inserts their card at the terminal. Admin polls for completion. + +**Layman summary:** "Tap your card on the reader at the counter." + +**Related:** [[Till Purchases (POS)|5.9 Till Purchases]], [[Infrastructure & DevOps|12. Infrastructure & DevOps]] (build tags for dev mock) + +### 2.3 Cash Payment +**What it does:** Admin records a cash payment. The system calculates change due. Optionally "keep the change as tip." + +**Layman summary:** "Pay with cash. Staff enter the amount; the system tells them how much change to give." + +**Related:** [[Tips|2.6 Tips]], [[Till Purchases (POS)|5.9 Till Purchases]] + +### 2.4 Gift Card Payment +**What it does:** Customers pay using a 12-digit gift card code or their account balance (balance from redeemed gift cards). VAT treatment depends on whether the card is SPV or MPV. + +**Layman summary:** "Use a gift card to pay — either enter the code or use your online balance." + +**Related:** [[Gift Cards|4. Gift Cards]], [[VAT Calculation|2.10 VAT Calculation]] + +### 2.5 Saved Cards +**What it does:** Customers can save their card details for faster checkout next time. Cards are tokenized via Square (raw card numbers never touch the server). Soft-deleted with 7-year UK retention. + +**Layman summary:** "Save your card for next time — one-click payment." + +**Related:** [[GDPR & Compliance|9. GDPR & Compliance]] (financial data retention), [[Frontend Architecture|15. Frontend Architecture]] (Cards tab) + +### 2.6 Tips +**What it does:** Customers can add a tip to a completed booking. Available as percentage presets (10%/15%/20%) or custom amount. Cash tip via "keep change as change" checkbox. + +**Layman summary:** "Add a tip after your appointment — either by card or by leaving the change." + +**Related:** [[Booking System|1. Booking System]] (completed bookings trigger tip eligibility), [[Today Page|7. Today Page]] (daily tips summary) + +### 2.7 Refunds +**What it does:** Admin processes refunds with automatic routing by payment method. Square card payments are refunded via Square API (post-DB-commit). Cash refunds are credited as gift card balance. Gift card payments are returned to the card's balance. + +**Layman summary:** "If a booking is cancelled, the system automatically refunds the right amount to the right place." + +**Related:** [[Cancellation|1.11 Cancellation]], [[Refund to Gift Card|4.11 Refund to Gift Card]] + +### 2.8 Payment Split Logic (`buildSplitRecords`) +**What it does:** When a customer pays online, a single Square charge is automatically split into up to 3 records: deposit portion (first 50%), balance portion (remaining owed), and tip portion (overflow beyond the total). This protects the deposit accounting. + +**Layman summary:** "The system smartly divides your payment — some goes to the deposit, the rest to the balance, and anything extra becomes a tip." + +**Related:** [[Deposit System|1.2 Deposit System]], [[Tips|2.6 Tips]] + +### 2.9 Double-Payment Prevention +**What it does:** Two mechanisms prevent accidental double-charges: PostgreSQL advisory locks (serialize payment attempts per-booking at the database level) and idempotency keys (client-generated unique IDs). + +**Layman summary:** "If you open two tabs and try to pay twice, only one payment goes through." + +**Related:** [[Idempotency Keys|1.12 Idempotency Keys]], [[Idempotent Purchases|4.10 Idempotent Purchases]] + +### 2.10 VAT Calculation +**What it does:** Applies VAT to payments and till sales based on business settings (rate, registration status, SPV/MPV voucher type). Discount, on-the-house, and tip payments are exempt. + +**Layman summary:** "VAT is calculated automatically on every payment based on your settings." + +**Related:** [[VAT Treatment (SPV vs MPV)|4.7 VAT Treatment]], [[Business Settings|5.6 Business Settings]] + +--- + +## 3. Availability & Scheduling + +Manages when the salon is open, handles exceptions (holidays, time off), and computes available time slots for booking. + +**Related:** [[Booking System|1. Booking System]] (availability calculation), [[Background Jobs|13. Background Jobs]] (staged hours, cleanup) + +### 3.1 Default Weekly Hours +**What it does:** The salon's regular opening hours for each day of the week. + +**Default schedule (from seed data):** +| Day | Hours | +|-----|-------| +| Mon | Closed | +| Tue | 09:00-17:00 | +| Wed | 09:00-17:00 | +| Thu | 12:00-20:00 | +| Fri | 09:00-17:00 | +| Sat | 09:00-17:00 | +| Sun | Closed | + +**Layman summary:** "The salon's regular opening hours." + +**Related:** [[Staged Default Hours Changes|3.3 Staged Default Hours Changes]], [[Admin Dashboard|5. Admin Dashboard]] (Business Settings) + +### 3.2 Exceptional Hours (Holidays & Special Days) +**What it does:** One-off schedule changes for holidays, early closing, or special openings. Uses a three-table design: group metadata (e.g., "Christmas"), 7-day hours per group, and date-range applications. + +**Layman summary:** "Holiday hours or special openings — like 'Open Sunday 2-5pm for Mother's Day'." + +**Related:** [[Default Weekly Hours|3.1 Default Weekly Hours]] + +### 3.3 Staged Default Hours Changes +**What it does:** Admin can schedule future changes to opening hours with an effective date picker. Conflict detection checks all active bookings in a 90-day window. Changes auto-apply at midnight via a daily cron job. + +**Layman summary:** "Planning to change your hours next month? Set it up now and it'll switch automatically on the right day." + +**Related:** [[Background Jobs|13. Background Jobs]] (apply-default-hours), [[Notifications|11. Notifications]] (default_hours_changed) + +### 3.4 Time Blockers +**What it does:** Admin can block specific time slots for any reason (dentist appointment, staff training, personal time). Supports one-off blocks and recurring blocks (cron expressions like "every Tuesday 2-3pm"). + +**Layman summary:** "Block out time in the calendar — whether it's a one-off or every week." + +**Related:** [[Slot Reservation System|1.8 Slot Reservation System]], [[Admin Schedule (Week View)|3.8 Admin Schedule]] + +### 3.5 Available Hours Calculation +**What it does:** The system computes available time slots by: loading working hours → subtracting existing bookings (with gap logic) → subtracting time blockers → applying lunch protection → applying late-night lock. Self-blocking prevention ensures a user's own reservation doesn't hide the slot from them. + +**Layman summary:** "The system figures out when you're free by looking at your hours, taking out appointments, blocked time, lunch, and late-night restrictions." + +**Related:** [[Overlap Prevention|1.9 Overlap Prevention]], [[Lunch Protection|3.6 Lunch Protection]], [[Late-Night Lock|3.7 Late-Night Lock]] + +### 3.6 Lunch Protection +**What it does:** Prevents bookings that would overlap the artist's lunch break. Shown as a warning or blocked slot on the time picker. Skipped on short days (≤5 hours). + +**Layman summary:** "Nobody can book over your lunch break." + +**Related:** [[Available Hours Calculation|3.5 Available Hours Calculation]] + +### 3.7 Late-Night Lock +**What it does:** After 22:00 (10pm UK time), the system blocks the next calendar day's 00:00-11:00 slots from appearing for non-admin users. This prevents customers from booking early-morning slots late at night when the artist may be asleep and unable to handle schedule conflicts. Admin users bypass this lock automatically. + +**Actual logic:** `if current London time >= 22:00 → subtract 00:00-11:00 from next day's available slots`. Admin can additionally use the `out_of_hours` flag to book outside normal working hours entirely. + +**Layman summary:** "After 10pm, you can't book tomorrow's morning slots online — the system assumes the salon is closed for the night. If you need a late or early appointment, call the salon." + +**Related:** [[Available Hours Calculation|3.5 Available Hours Calculation]] + +### 3.8 Admin Schedule (Week View) +**What it does:** Google Calendar-style weekly view showing all appointments, closed days (striped overlay), and time blockers. Accessible from the navigation bar. + +**Layman summary:** "See the whole week at a glance — appointments, closures, and blocked time." + +**Related:** [[Admin Dashboard|5. Admin Dashboard]], [[Today Page|7. Today Page]] + +--- + +## 4. Gift Cards + +Physical and digital gift cards with multi-method purchase, 24-month rolling expiry, balance pooling, and admin recovery of expired balances. + +**Related:** [[Payments|2. Payments]] (purchase/redeem), [[VAT Calculation|2.10 VAT Calculation]] (SPV/MPV), [[Admin Dashboard|5. Admin Dashboard]] (gift card management) + +### 4.1 Gift Card Types +**What it does:** Three types of gift cards: +- **Standard**: Purchased with a balance >0, 24-month rolling expiry +- **Inventory**: Zero-amount physical cards for stock management, topped up later at the till +- **Redeemed**: Balance moved to user's pooled account + +**Layman summary:** "Gift cards come in three flavours: bought (has money), blank (needs topping up), or redeemed (money moved to your online account)." + +**Related:** [[Purchase Methods|4.2 Purchase Methods]], [[24-Month Rolling Expiry|4.4 24-Month Rolling Expiry]] + +### 4.2 Purchase Methods +**What it does:** Gift cards can be bought four ways: +- **Cash** — record cash received at the till +- **Card Machine** — Square Terminal payment +- **Online Square** — Web Payments SDK (buy from website) +- **On the House** — giveaway (no payment) + +**Layman summary:** "Buy a gift card with cash, card, online, or as a freebie." + +**Related:** [[Till Purchases (POS)|5.9 Till Purchases]], [[Online Gift Card Purchase|4.3 Online Gift Card Purchase]] + +### 4.3 Online Gift Card Purchase +**What it does:** Customers buy gift cards from the website. Payment is processed via Square. If buying for themselves, balance is auto-redeemed. If buying for a friend, a code is generated and the recipient's email is logged for future email delivery (SMTP not yet wired — TODO). + +**Layman summary:** "Buy a gift card online — for yourself (instant) or for a friend (code generated, email delivery pending)." + +**Related:** [[Online Card Payment (Square Web Payments SDK)|2.1 Online Card Payment]] + +### 4.4 24-Month Rolling Expiry +**What it does:** Gift cards expire after 24 months of inactivity. "Last use" includes balance checks, top-ups, redemptions, and payments. Each use resets the timer. Expired balances move to a recovery table. + +**Layman summary:** "Gift cards don't expire if you keep using them. After 2 years without use, the balance moves to a recovery account." + +**Related:** [[Idle Account Cleanup|4.5 Idle Account Cleanup]], [[Expired Balance Recovery|4.6 Expired Balance Recovery]], [[Background Jobs|13. Background Jobs]] (cleanup-expired-gift-cards) + +### 4.5 Idle Account Cleanup +**What it does:** Accounts (gift card holders) with no balance and 2+ years of inactivity are anonymized. Accounts with a balance and 5+ years of inactivity have their balance moved to expired balances, then the account is anonymized. + +**Layman summary:** "Dormant accounts are cleaned up after 2-5 years. Any remaining balance is preserved for recovery." + +**Related:** [[24-Month Rolling Expiry|4.4 24-Month Rolling Expiry]], [[GDPR & Compliance|9. GDPR & Compliance]] (anonymization) + +### 4.6 Expired Balance Recovery +**What it does:** Admin can view all expired/dormant balances and recover them with an audit trail. Only account ID + amount is stored (no PII), so recovery requires the account ID. + +**Layman summary:** "If a customer says 'I had a gift card that expired', staff can find and restore it." + +**Related:** [[Admin Dashboard|5. Admin Dashboard]] (Gift Card Management), [[Admin Audit Trail|4.9 Admin Audit Trail]] + +### 4.7 VAT Treatment (SPV vs MPV) +**What it does:** Gift cards can be configured as Single-Purpose Vouchers (VAT charged at purchase) or Multi-Purpose Vouchers (VAT charged at redemption). Default is SPV. + +**Layman summary:** "VAT is handled differently depending on the gift card type — charged at purchase or at use." + +**Related:** [[VAT Calculation|2.10 VAT Calculation]], [[Business Settings|5.6 Business Settings]] + +### 4.8 Transaction Audit Log (`gift_card_transactions`) +**What it does:** Every action on a gift card is recorded in a dedicated audit table with transaction type, amount, reference, and acting user. Transaction types: `purchase`, `topup`, `redeem_to_balance`, `payment` (spent at checkout), `refund` (credited back), `expire` (background cleanup). + +**Layman summary:** "Every penny that moves on a gift card is logged — who did what, when, and why." + +**Related:** [[Admin Audit Trail|4.9 Admin Audit Trail]], [[GDPR & Compliance|9. GDPR & Compliance]] + +### 4.9 Admin Audit Trail (`admin_audit_log`) +**What it does:** Admin actions on gift card data (e.g., checking a user's balance) are logged to a separate audit table with admin ID, action type, target user, and JSON details. Used for compliance and dispute resolution. + +**Layman summary:** "When staff look up a customer's gift card balance, it's logged with timestamps." + +**Related:** [[Transaction Audit Log (gift_card_transactions)|4.8 Transaction Audit Log]], [[GDPR & Compliance|9. GDPR & Compliance]] + +### 4.10 Idempotent Purchases +**What it does:** Gift card purchases use UUID-v4 idempotency keys to prevent double-charges. The system checks for existing payments with the same key before processing. Old keys are cleaned up after 24 hours by a cron job. + +**Layman summary:** "If you click 'Buy' twice, you only get charged once." + +**Related:** [[Double-Payment Prevention|2.9 Double-Payment Prevention]], [[Idempotency Keys|1.12 Idempotency Keys]] + +### 4.11 Refund to Gift Card +**What it does:** When a booking paid with a gift card is cancelled, the refund is credited back to the gift card's balance. If the card has expired, the money is retained by the salon (per policy). + +**Layman summary:** "If you paid with a gift card and cancel, the money goes back on the card." + +**Related:** [[Refunds|2.7 Refunds]], [[Cancellation|1.11 Cancellation]] + +### 4.12 Friend Purchase Email Delivery (TODO) +**What it does:** When a user buys a gift card for a friend, the recipient's email is logged alongside the gift card code. The intent is to send the code via email automatically. **Not yet implemented** — SMTP integration is pending, and no admin notification is created (the gift code is logged and will be emailed once email delivery is wired). + +**Layman summary:** "Buy a gift card for a friend and we'll email them the code — once email sending is set up." + +**Related:** [[Online Gift Card Purchase|4.3 Online Gift Card Purchase]], [[Notifications|11. Notifications]] + +--- + +## 5. Admin Dashboard + +The central management hub for salon operations — managing users, bookings, services, scheduling, discounts, gift cards, portfolio, and business settings. + +**Related:** [[Booking System|1. Booking System]], [[Payments|2. Payments]], [[Loyalty & Discounts|6. Loyalty & Discounts]], [[Gift Cards|4. Gift Cards]], [[Today Page|7. Today Page]] + +### 5.1 User Management +**What it does:** List all users with search, view detailed user profiles (bookings, spend, visits, top services, patch tests, loyalty, referrals, privacy/consent), and manage customer relationships. + +**Layman summary:** "See everything about a customer — their appointments, spending, favourite services, and more." + +**Related:** [[Authentication & Security|8. Authentication & Security]] (roles), [[GDPR & Compliance|9. GDPR & Compliance]] (data export) + +### 5.2 Booking Management +**What it does:** List all bookings with filters (date range, status, user), view details, edit services/notes/overrides, progress status, confirm pending bookings, cancel with refunds, and reschedule. + +**Layman summary:** "Full control over every appointment — create, edit, reschedule, confirm, cancel." + +**Related:** [[Booking System|1. Booking System]], [[Notifications|11. Notifications]] (pending approvals) + +### 5.3 Services & Custom Services Management +**What it does:** Full CRUD for the service catalog (name, price, duration, patch test settings). Custom services CRUD with promote-to-permanent and delete (with usage guard). + +**Layman summary:** "Manage your price list and create one-off services for special requests." + +**Related:** [[Services Catalog|1.5 Services Catalog]], [[Custom Services|1.7 Custom Services]] + +### 5.4 Discount Campaigns Management +**What it does:** Create and manage discount campaigns (time-based, milestones, anniversary). Lifecycle: draft → active → completed → cancelled. + +**Layman summary:** "Set up promotions like '10% off this week' or '15% off your 5th visit'." + +**Related:** [[Loyalty & Discounts|6. Loyalty & Discounts]], [[Campaign Lifecycle|6.4 Campaign Lifecycle]] + +### 5.5 Gift Card Management +**What it does:** Create gift cards (all payment methods), top-up, transfer between cards, redeem to account balance, view/search cards, manage expired balances. + +**Layman summary:** "Full gift card management — create, top up, transfer, and recover expired balances." + +**Related:** [[Gift Cards|4. Gift Cards]], [[Till Purchases (POS)|5.9 Till Purchases]], [[Expired Balance Recovery|4.6 Expired Balance Recovery]] + +### 5.6 Business Settings +**What it does:** Configure business name, address, VAT rate, gift card expiry months, and voucher type (SPV/MPV). + +**Layman summary:** "Salon settings — VAT, gift cards, and contact info." + +**Related:** [[VAT Calculation|2.10 VAT Calculation]], [[VAT Treatment (SPV vs MPV)|4.7 VAT Treatment]], [[Contact & Location|14. Contact & Location]] + +### 5.7 Portfolio Image Upload +**What it does:** Upload portfolio images with tag management (categories and tags for filtering). Multi-format encoding pipeline. + +**Layman summary:** "Add photos of your work to the gallery." + +**Related:** [[Portfolio Gallery|10. Portfolio Gallery]] + +### 5.8 Patch Test Management +**What it does:** Manage patch test definitions (which services need tests, notice period, expiry) and record customer patch test completions. + +**Layman summary:** "Manage allergy test requirements and record when customers have had them." + +**Related:** [[Patch Tests|1.6 Patch Tests]], [[Services Catalog|1.5 Services Catalog]] + +### 5.9 Till Purchases (POS) +**What it does:** Point-of-sale interface for selling gift cards at the counter (cash, card machine, saved card, online card entry, on the house). + +**Layman summary:** "Sell gift cards at the till — take cash or card." + +**Related:** [[Gift Cards|4. Gift Cards]], [[Purchase Methods|4.2 Purchase Methods]], [[Payments|2. Payments]] + +--- + +## 6. Loyalty & Discounts + +A stamp-based loyalty program and automated discount campaigns that stack additively. + +**Related:** [[Admin Dashboard|5. Admin Dashboard]] (campaign management), [[Payments|2. Payments]] (discount application), [[Booking System|1. Booking System]] (auto-apply on completion) + +### 6.1 Stamp Loyalty +**What it does:** Customers earn 1 stamp per paid appointment (max 1 per calendar day). After 10 stamps, they get 10% off on their next booking. Stamps are refunded if the booking is cancelled. + +**Layman summary:** "Buy 9 get 1 10% off. One stamp per appointment. After 10 stamps, the next one is 10% cheaper." + +**Related:** [[Background Jobs|13. Background Jobs]] (expired loyalty redemption cleanup), [[Frontend Architecture|15. Frontend Architecture]] (stamp card UI) + +### 6.2 Discount Campaigns +**What it does:** Automatic discount application at payment or completion time. Four campaign types: +- **Time-based**: Active during a date range (e.g., "10% off this week") +- **Per-user milestone**: Discount at the customer's Nth visit +- **Global milestone**: Discount when the salon reaches a total booking count (in-person only) +- **Anniversary**: Discount on the anniversary of the customer's first visit + +**Layman summary:** "Automatic promotions — time-limited sales, loyalty rewards, and celebration discounts." + +**Related:** [[Campaign Lifecycle|6.4 Campaign Lifecycle]], [[Additive Stacking|6.3 Additive Stacking]] + +### 6.3 Additive Stacking +**What it does:** All eligible discounts stack together against the original booking total. E.g., loyalty 10% + time-based 5% = 15% off. + +**Layman summary:** "Every discount you qualify for adds up. More discounts = more savings." + +**Related:** [[Discount Campaigns|6.2 Discount Campaigns]], [[Stamp Loyalty|6.1 Stamp Loyalty]] + +### 6.4 Campaign Lifecycle +**What it does:** Campaigns progress through states: `draft → active → completed → cancelled`. Status transitions are automated via hourly cron job (start on start_date, end on end_date or max redemptions). + +**Layman summary:** "Set up a campaign now, schedule it to start next week, and it runs itself." + +**Related:** [[Background Jobs|13. Background Jobs]] (transition-discount-campaigns), [[Discount Campaigns Management|5.4 Discount Campaigns Management]] + +--- + +## 7. Today Page (Daily Operations Hub) + +The staff's main daily dashboard for managing appointments in real time. + +**Related:** [[Booking System|1. Booking System]], [[Admin Dashboard|5. Admin Dashboard]], [[Payments|2. Payments]] (tips, refunds) + +### 7.1 Current & Next Appointment +**What it does:** Cards at the top showing the current in-progress appointment and the next one coming up. Auto-updates as time passes. + +**Layman summary:** "See who's here now and who's next — at a glance." + +**Related:** [[Booking Statuses & Transition Rules|1.13 Booking Statuses]] + +### 7.2 Interactive Daily Calendar Grid +**What it does:** A visual timeline of today's appointments with time blockers shown as red/hatched bars. Drag-and-drop style interaction. + +**Layman summary:** "Your day laid out on a timeline — appointments and blocked time." + +**Related:** [[Time Blockers|3.4 Time Blockers]], [[Admin Schedule (Week View)|3.8 Admin Schedule]] + +### 7.3 Pending Approvals Queue +**What it does:** List of bookings waiting for admin approval (new bookings, edit requests) with accept/decline buttons and side-by-side snapshots for edit requests. + +**Layman summary:** "Approve or decline new bookings and change requests — all in one place." + +**Related:** [[Booking Edit Requests|1.10 Booking Edit Requests]], [[Notifications|11. Notifications]] + +### 7.4 Daily Summary / End of Day +**What it does:** When all appointments are done, replaces the current/next cards with a summary: customers served, total taken, tips, bookings made. Also shows weekly summaries on closed days. + +**Layman summary:** "End-of-day report — customers seen, money taken, tips received." + +**Related:** [[Tips|2.6 Tips]], [[DoneForDay State|7.6 DoneForDay State]] + +### 7.5 Walk-In & Call-In Wizards +**What it does:** Step-by-step wizards for creating walk-in (3-step) and call-in (4-step) bookings directly from the Today page. + +**Layman summary:** "Quickly book someone in who walked in or called — step by step." + +**Related:** [[Walk-In Booking (Admin)|1.3 Walk-In Booking]], [[Call-In Booking (Admin)|1.4 Call-In Booking]] + +### 7.6 `DoneForDay` State +**What it does:** When all appointments are completed/admin marks the day done, the system shows summary stats. Works across closed days and weekends with automatic week-range aggregation. + +**Layman summary:** "Mark the day as done. The system shows you how it went." + +**Related:** [[Daily Summary / End of Day|7.4 Daily Summary]] + +--- + +## 8. Authentication & Security + +JWT-based authentication with refresh token rotation, role-based access control, progressive rate limiting, and account lockout. + +**Related:** [[Frontend Architecture|15. Frontend Architecture]] (auth store), [[GDPR & Compliance|9. GDPR & Compliance]] (data export includes session data) + +### 8.1 Registration +**What it does:** New users create an account with email/password. Optional referral code. Password strength checked via zxcvbn (same algorithm client and server-side). + +**Layman summary:** "Sign up with your email. Optionally enter a referral code." + +**Related:** [[Guest Accounts|8.8 Guest Accounts]], [[Referral System|5.1 User Management]] (referral code) + +### 8.2 Login & JWT Tokens +**What it does:** Users log in with email/password. Receive a JWT access token (1-hour expiry) and a refresh token (90-day, single-use with rotation). The refresh token is consumed on each use — old token invalidated. + +**Layman summary:** "Log in and stay logged in. Your session refreshes automatically." + +**Related:** [[Auth Store & Token Management|15.4 Auth Store & Token Management]], [[Background Jobs|13. Background Jobs]] (refresh token cleanup) + +### 8.3 Role-Based Access Control +**What it does:** Five roles control what users can see and do: +- `unverified_email` — registered but email not verified (limited access) +- `verified_email` — email verified (full customer access) +- `admin` — full salon management +- `guest` — disposable booking-only account +- `affiliate` — referral tracking + +**Layman summary:** "Different levels of access — customers see their own bookings, admins see everything." + +**Related:** [[Guest Accounts|8.8 Guest Accounts]], [[Admin Dashboard|5. Admin Dashboard]] + +### 8.4 Rate Limiting +**What it does:** Progressive dual-window rate limiting on login/register endpoints. Burst: 30 requests per 5 seconds. Sustained: 120 requests per 60 seconds. Violators get progressive delays (500ms → 10s). + +**Layman summary:** "The system slows down rapid-fire requests to prevent abuse." + +**Related:** [[Account Lockout|8.5 Account Lockout]] + +### 8.5 Account Lockout +**What it does:** After 5 failed login attempts, the account is locked for a progressively longer period (15min → 30min → 1h → 2h). + +**Layman summary:** "Too many wrong passwords? You'll be locked out for a while." + +**Related:** [[Rate Limiting|8.4 Rate Limiting]] + +### 8.6 Email Verification & Password Reset +**What it does:** Backend supports sending verification codes for email verification and password reset. The frontend verification/reset UI is not yet wired (notable gap). + +**Layman summary:** "The system can send verification codes, but the 'forgot password' link isn't on the website yet." + +**Related:** [[Background Jobs|13. Background Jobs]] (verification code cleanup) + +### 8.7 Security Headers +**What it does:** Content-Security-Policy (`default-src 'none'; frame-ancestors 'none'`), CORS headers, and security middleware applied globally. + +**Layman summary:** "Security headers protect against common web attacks." + +### 8.8 Guest Accounts +**What it does:** Disposable accounts created on-the-fly for booking without registration. No identity tracking across bookings. PII scrubbed 6 months after the appointment. + +**Layman summary:** "Book without signing up. Your details are automatically deleted after 6 months." + +**Related:** [[Guest PII Anonymization|9.3 Guest PII Anonymization]], [[Self-Service Booking (Customer-Facing)|1.1 Self-Service Booking]] + +--- + +## 9. GDPR & Compliance + +Full compliance with UK GDPR, including Article 15 data export, right to erasure, and data retention policies. + +**Related:** [[Authentication & Security|8. Authentication & Security]], [[Background Jobs|13. Background Jobs]] (anonymization, financial cleanup), [[Notifications|11. Notifications]] (user_id nullification) + +### 9.1 GDPR Data Export (Article 15) +**What it does:** Customers can download all their personal data as a 23-section JSON file and PDF. Export runs in the background with 12-hour caching. Includes: profile, bookings, payments, patch tests, referrals (referred by + referred users), referral discounts, notification preferences, saved cards, refunds, social logins, loyalty redemptions, booking discounts, edit requests, affiliate payouts, forgiven no-shows, gift card balance, gift card transactions, gift cards, admin audit log, login history, name history, plus export metadata. Verification codes are explicitly excluded as authentication tokens. + +**Layman summary:** "Download everything we know about you — in one click." + +**Related:** [[Background Jobs|13. Background Jobs]] (gdpr export cache cleanup), [[Frontend Architecture|15. Frontend Architecture]] (GDPR page) + +### 9.2 Account Deletion (Right to Erasure) +**What it does:** Customers can delete their account. The system scrubs PII from all child tables (social logins deleted, saved cards soft-deleted, verification codes expired, notes nulled, notification prefs deleted). Also scrubs external systems (S3 profile pictures, Square saved cards). + +**Layman summary:** "Delete your account and we wipe your data — everywhere." + +**Related:** [[Saved Cards|2.5 Saved Cards]], [[SabreDAV (CardDAV)|12.7 SabreDAV]] (contact deletion) + +### 9.3 Guest PII Anonymization +**What it does:** Guest accounts with bookings older than 6 months are automatically anonymized: name → "Guest Anonymized", email → "anon-{id}@anon.invalid", phone zeroed, DOB reset. + +**Layman summary:** "Guest details are automatically wiped after 6 months." + +**Related:** [[Guest Accounts|8.8 Guest Accounts]], [[Background Jobs|13. Background Jobs]] (anonymize-stale-guest-accounts) + +### 9.4 Financial Data Retention +**What it does:** Payment records retained for 7 years (UK HMRC + Limitation Act), then aggregated into monthly totals (no PII) and deleted. + +**Layman summary:** "Payment records kept for 7 years for tax purposes, then anonymized." + +**Related:** [[Background Jobs|13. Background Jobs]] (cleanup-expired-financial-records) + +### 9.5 Privacy Policy & Terms +**What it does:** Full privacy policy, terms of service, cancellation policy, and gift card terms & conditions available as user-facing pages. + +**Layman summary:** "Legal documents — privacy, terms, and policies." + +--- + +## 10. Portfolio Gallery + +A visual gallery showcasing the salon's work with multi-format image support, tag-based filtering, and cursor-based pagination. + +**Related:** [[Admin Dashboard|5. Admin Dashboard]] (portfolio upload), [[S3/R2 Storage Abstraction|12.8 S3/R2 Storage]], [[Multi-Format Image Pipeline|10.2 Multi-Format Image Pipeline]] + +### 10.1 Image Upload +**What it does:** Admin uploads portfolio images with tags and categories. The system auto-generates multiple formats (AVIF, WebP, JPEG, JXL) for optimal browser delivery. + +**Layman summary:** "Upload photos of your work with tags so customers can find them." + +**Related:** [[Portfolio Image Upload|5.7 Portfolio Image Upload]] + +### 10.2 Multi-Format Image Pipeline +**What it does:** Client-side WASM encoding generates AVIF, WebP, JPEG, and JPEG XL variants. The `` element serves the best format the browser supports. + +**Layman summary:** "Photos look great and load fast — the system picks the best format for your device." + +**Related:** [[Frontend Architecture|15. Frontend Architecture]] (ImageVariant component, workers) + +### 10.3 Tag & Category Filtering +**What it does:** Fuzzy tag search (relevance-sorted results) and exact category filters (date-sorted results). Tag autocomplete for admin. Category counts show how many images in each. + +**Layman summary:** "Find photos by tag or category — searching 'gel' shows all gel nail photos." + +**Related:** [[Portfolio Image Upload|5.7 Portfolio Image Upload]] + +### 10.4 Cursor-Based Pagination +**What it does:** Images load in pages using cursor pagination (not page numbers). Infinite scroll style with a `next_cursor` field in responses. + +**Layman summary:** "Scroll through the gallery — it loads more as you go." + +--- + +## 11. Notifications + +A pull-based admin notification queue with priority ordering. Note: email/SMS delivery is not yet implemented — notifications are UI-only. + +**Related:** [[Booking System|1. Booking System]] (creation/cancellation notifications), [[Payments|2. Payments]] (deposit notifications), [[Scheduling|3. Scheduling]] (hours change notifications), [[Gift Cards|4. Gift Cards]] (friend purchase) + +### 11.1 Admin Notification Queue +**What it does:** Internal notifications for the admin about important events: new bookings, cancellations, edit requests, deposit deadlines, unpaid reminders, and schedule changes. + +**All 14 notification reasons (DB enum):** + +| Reason | Trigger | Created By | +|--------|---------|------------| +| `new_booking` | Every customer-created booking unconditionally | `bookings.go:2328` | +| `pending_booking` | Booking needs admin approval (has notes or is today) | `bookings.go:2349`, `manage.go:1693` | +| `cancelled_booking` | Booking cancelled by customer or admin | `bookings.go:3330`, `manage.go:101,242` | +| `edit_requested` | User requests booking edit/reschedule | `manage.go:1673` | +| `deposit_not_paid_by_deadline` | Deposit deadline passed in `pending_release` | `time-blockers.go:760` (cron every 5min) | +| `1_week_no_pay` | Booking ended 7-30 days ago with no payment | `scheduled-cleanup.go:71` (cron daily 07:00) | +| `1_month_no_pay` | Booking ended 30+ days ago with no payment | `scheduled-cleanup.go:139` (cron daily 07:30) | +| `gift_card_purchased_for_friend` | *(defined in DB enum — code removed, email delivery pending)* | Removed — email TODO | +| `default_hours_changed` | Staged hours change auto-applied at midnight | `scheduled-cleanup.go:342` (cron daily 00:05) | +| `late_cancellation` | *(defined in DB enum — NOT yet created by any code)* | Planned | +| `rescheduled_booking` | *(defined in DB enum — NOT yet created by any code)* | Planned | +| `affiliate_claim` | *(defined in DB enum — NOT yet created by any code)* | Planned | +| `deposit_paid` | *(defined in DB enum — NOT yet created by any code)* | Planned | +| `edit_request` | *(defined in DB enum — only `edit_requested` is used in code)* | Unused | + +**Layman summary:** "Staff get notified when things happen — new bookings, cancellations, unpaid reminders." + +### 11.2 Notification Bell & Unread Count +**What it does:** A bell icon in the navigation bar shows the number of unread notifications. Clicking it opens the notifications page. + +**Layman summary:** "A bell icon shows how many new things need your attention." + +**Related:** [[Frontend Architecture|15. Frontend Architecture]] (NavBar component, auth store) + +### 11.3 Acknowledge Flow +**What it does:** Notifications can be acknowledged (marked as read). Pagination with "show acknowledged" toggle lets admin review historical notifications. + +**Layman summary:** "Mark notifications as read. You can still see old ones if needed." + +**Related:** [[Pending Approvals Queue|7.3 Pending Approvals Queue]] + +### 11.4 User Notification Preferences +**What it does:** Users can set their notification preferences (email, SMS, push). **Note:** The table exists but no delivery system is wired — SMTP/SMS integration is not yet implemented. + +**Layman summary:** "You can choose how you want to be notified (but notifications aren't being sent yet)." + +**Related:** [[Frontend Architecture|15. Frontend Architecture]] (account page preferences UI) + +--- + +## 12. Infrastructure & DevOps + +The platform infrastructure — Docker Compose stack, CI/CD, local development environment, and deployment configuration. + +**Related:** [[Background Jobs|13. Background Jobs]] (cron scheduler), [[S3/R2 Storage Abstraction|12.8 S3/R2 Storage]] + +### 12.1 Docker Compose Stack +**What it does:** Five services orchestrated in Docker Compose: +- **postgres:17** — Primary database +- **backend** — Custom Go API server (chi router) +- **sabredav** — PHP CardDAV/CalDAV server (profile photo sync) +- **nginx** — Reverse proxy (static frontend, API proxy, DAV proxy) +- **rustfs** — Local S3-compatible storage for dev + +**Layman summary:** "Everything runs in Docker — database, API, web server, and file storage." + +**Related:** [[Nginx Configuration|12.2 Nginx Configuration]], [[Local Development Setup|12.5 Local Development Setup]] + +### 12.2 Nginx Configuration +**What it does:** Routes traffic: +- `/` → Static frontend files +- `/api/*` → Backend Go server +- `/dav/*` → SabreDAV server + +**Layman summary:** "Nginx directs traffic to the right place — website, API, or contact sync." + +**Related:** [[Docker Compose Stack|12.1 Docker Compose Stack]] + +### 12.3 CI/CD Pipeline (Gitea) +**What it does:** Gitea CI pipeline that runs on every push: lints backend (go vet, golangci-lint, staticcheck, gosec), lints frontend (prettier, eslint), runs all tests, builds both binaries. Uses cached dependencies for speed. + +**Layman summary:** "Every code change is automatically checked, tested, and built." + +**Related:** [[Pre-commit Hooks|12.4 Pre-commit Hooks]] + +### 12.4 Pre-commit Hooks +**What it does:** Before every commit: auto-formats frontend with Prettier, lints frontend with ESLint, vet + lint the backend (if changed), runs secret scan (gitleaks). + +**Layman summary:** "Code is cleaned and checked before it's saved." + +**Related:** [[CI/CD Pipeline (Gitea)|12.3 CI/CD Pipeline]] + +### 12.5 Local Development Setup +**What it does:** A single `./local-dev-2.sh` script launches a 4-pane tmux session with: psql console, Go dev server (hot-reload), Svelte dev server (HMR), and RustFS (local file storage). Seeds 20 users, 12 services, 43 bookings, and more. + +**Layman summary:** "One command starts everything you need for local development." + +**Related:** [[Docker Compose Stack|12.1 Docker Compose Stack]], [[Build Tags (Dev vs Prod)|12.6 Build Tags]] + +### 12.6 Build Tags (Dev vs Prod) +**What it does:** Go build tags switch between development mocks and production implementations: +- `dev` → Square mock client, RustFS storage, rate limiter disabled, local DB +- `!dev` (default) → Live Square API, Cloudflare R2 storage, production rate limiting + +**Layman summary:** "Automatically switches between test-mode and real services depending on how you build." + +**Related:** [[Square Terminal (In-Person Card)|2.2 Square Terminal]] (dev mock) + +### 12.7 SabreDAV (CardDAV) +**What it does:** CardDAV server for syncing customer contact info (profile photos) with compatible apps. Integrated at user registration (create contact), booking confirmation (calendar event), and account deletion (delete contact). + +**Layman summary:** "Syncs customer contacts with your phone or address book." + +**Related:** [[Account Deletion (Right to Erasure)|9.2 Account Deletion]] + +### 12.8 S3/R2 Storage Abstraction +**What it does:** Abstracted storage layer for images and profile pictures. Uses RustFS (local filesystem via S3-compatible API) in development, Cloudflare R2 in production. + +**Layman summary:** "Photo storage that works locally and in the cloud." + +**Related:** [[Portfolio Gallery|10. Portfolio Gallery]], [[Profile Picture Upload (Admin)|5.1 User Management]] + +--- + +## 13. Background Jobs (Cron Scheduler) + +A centralized cron scheduler that runs 20 maintenance jobs for cleanup, transitions, and data management. + +**Related:** [[Availability & Scheduling|3. Scheduling]] (hours apply), [[GDPR & Compliance|9. GDPR & Compliance]] (cleanup), [[Gift Cards|4. Gift Cards]] (expiry/cleanup), [[Payments|2. Payments]] (idempotency cleanup) + +### 13.1 Every 5 Minutes +- **cleanup-reservations**: Delete expired slot reservations by TTL type +- **cleanup-expired-deposits**: Move unpaid bookings to `pending_release` state +- **cleanup-rate-limiters**: Purge stale rate limiter entries +- **cleanup-gdpr-export-cache**: Expire old GDPR export caches + +**Related:** [[Slot Reservation TTLs|1.14 Slot Reservation TTLs]], [[Deposit System|1.2 Deposit System]] + +### 13.2 Every Minute +- **cleanup-progressive-rate-limiter**: Clean progressive rate limiter state + +**Related:** [[Rate Limiting|8.4 Rate Limiting]] + +### 13.3 Hourly +- **cleanup-expired-loyalty-redemptions**: Expire unused loyalty discount windows +- **cleanup-old-idempotency-keys**: Purge old booking/payment dedup keys +- **cleanup-revoked-jtis**: Clean expired revoked JWT entries +- **cleanup-stale-login-entries**: Clean old login attempt records +- **transition-discount-campaigns**: Advance campaign lifecycle (draft→active, active→completed) + +**Related:** [[Idempotency Keys|1.12 Idempotency Keys]], [[Campaign Lifecycle|6.4 Campaign Lifecycle]], [[Account Lockout|8.5 Account Lockout]] + +### 13.4 Daily (Staggered) +- **00:05** — `apply-default-hours`: Apply pending scheduled hours changes +- **02:00** — `cleanup-verification-codes`: Purge expired verification codes +- **02:00** — `cleanup-refresh-tokens`: Purge expired refresh tokens +- **03:00** — `anonymize-stale-guest-accounts`: GDPR anonymize guests >6mo inactive +- **03:30** — `cleanup-idle-accounts`: Clean idle gift card accounts (2yr/5yr) +- **04:00** — `cleanup-expired-financial-records`: Purge records beyond 7yr retention +- **04:30** — `cleanup-old-name-history`: Prune old name change history +- **05:00** — `cleanup-expired-gift-cards`: Expire gift cards after 24 months +- **07:00** — `notify-unpaid-1-week`: Create notification for bookings unpaid >1 week +- **07:30** — `notify-unpaid-1-month`: Create notification for bookings unpaid >1 month + +**Related:** [[Staged Default Hours Changes|3.3 Staged Default Hours Changes]], [[Guest PII Anonymization|9.3 Guest PII Anonymization]], [[24-Month Rolling Expiry|4.4 24-Month Rolling Expiry]], [[Idle Account Cleanup|4.5 Idle Account Cleanup]], [[Financial Data Retention|9.4 Financial Data Retention]] + +**Layman summary:** "The system takes care of itself — cleaning up old data, applying schedule changes, anonymizing guest info, and sending reminders — all on a timer." + +--- + +## 14. Contact & Location + +Dynamic business contact information and map display. + +**Related:** [[Admin Dashboard|5. Admin Dashboard]] (Business Settings), [[Frontend Architecture|15. Frontend Architecture]] (Map component) + +### 14.1 Contact Page +**What it does:** Displays the salon's contact info (phone, email, address) from the first admin user's profile. Includes a MapLibre GL interactive map. + +**Layman summary:** "Find the salon — address, phone, and an interactive map." + +### 14.2 Admin Contact / Business Info +**What it does:** The salon can update contact info through business settings. The contact page reflects changes automatically. + +**Layman summary:** "Change your contact details and they update on the website." + +**Related:** [[Business Settings|5.6 Business Settings]] + +--- + +## 15. Frontend Architecture + +The SvelteKit 5 static SPA that powers the entire user interface. + +**Related:** [[Authentication & Security|8. Authentication & Security]] (auth store), [[Portfolio Gallery|10. Portfolio Gallery]] (ImageVariant component) + +### 15.1 SvelteKit Routes +**What it does:** All frontend pages and their purposes: +- `/` — Home page (welcome, services overview, portfolio carousel) +- `/book` — 5-step booking wizard +- `/login` — Login/Register +- `/account` — User profile, bookings, loyalty, gift cards, GDPR export +- `/admin` — Admin dashboard +- `/today` — Daily operations hub +- `/portfolio` — Image gallery +- `/prices` — Service price list +- `/schedule` — User's upcoming appointments +- `/admin/schedule` — Weekly calendar (Google Calendar-style) +- `/contact` — Business info + MapLibre map +- `/gdpr` — GDPR data export +- `/admin/notifications` — Notification queue +- `/book/confirmed/[id]` — Booking confirmation +- `/pay-tip/[id]` — Tip payment page + +**Related:** [[Self-Service Booking (Customer-Facing)|1.1 Self-Service Booking]], [[Admin Dashboard|5. Admin Dashboard]], [[Today Page|7. Today Page]], [[Contact Page|14.1 Contact Page]] + +### 15.2 Shared UI Components +**What it does:** A library of reusable UI components built with shadcn-svelte: +- `PhoneInput` — UK phone validation with auto-formatting +- `CharCounter` — Grapheme counter using `Intl.Segmenter` (colour-coded at thresholds) +- `ImageVariant` — Multi-format `` element for responsive images +- `NavBar` — Responsive navigation with notification badge +- `Map` — MapLibre GL interactive map +- Various shadcn components: buttons, cards, dialogs, inputs, badges, separators, skeletons + +**Related:** [[Multi-Format Image Pipeline|10.2 Multi-Format Image Pipeline]], [[Notification Bell & Unread Count|11.2 Notification Bell]] + +### 15.3 Timezone Handling +**What it does:** All timestamps are stored in UTC. The backend uses `clock.Now()` (returns UTC). The frontend converts between UTC and the browser's local time (always Europe/London since the app is UK-only) using `formatLocalDateTime()` and `parseWallClockDate()`. + +**Layman summary:** "Times are stored in UTC and shown in UK time — no confusion about timezones." + +**Related:** [[Available Hours Calculation|3.5 Available Hours Calculation]] + +### 15.4 Auth Store & Token Management +**What it does:** JWT tokens stored in localStorage with automatic hourly refresh. Refresh token rotates each use (old token invalidated). Role-based UI via `hasRole()`, `isAdmin()`, `isVerified()`. + +**Layman summary:** "Stays logged in. Automatically refreshes your session. Shows different options based on who you are." + +**Related:** [[Login & JWT Tokens|8.2 Login & JWT Tokens]], [[Role-Based Access Control|8.3 Role-Based Access Control]] + +--- + +## Known Limitations + +Documented gaps and missing functionality (from codebase audit): + +- **Single employee** — No multi-staff scheduling +- **No email/SMS** — SMTP not wired; notifications are UI-only +- **No production S3/R2** — Prod storage stubs return "not implemented" +- **No social auth** — OAuth provider registrations pending +- **No dark mode, PWA, recurring bookings, CSV export** +- **Password reset** — Backend exists, no frontend link +- **No error tracking** — Sentry not configured +- **No automated DB backups** — No pg_dump cron +- **No API docs** — No OpenAPI/Swagger spec + +--- + +## Cross-Reference Index + +| Feature | Related Areas | +|---------|---------------| +| **Booking System** | Payments (deposits, refunds), Scheduling (availability), Services Catalog, Patch Tests, Notifications (approvals, cancellations), Background Jobs (cleanup) | +| **Payments** | Gift Cards (purchase/redeem), VAT (SPV/MPV), Booking (deposits), Loyalty (discount application), Admin (POS till), Background Jobs (idempotency cleanup) | +| **Gift Cards** | Payments (purchase/redeem/refund), VAT (SPV/MPV), Admin (management), Notifications (friend purchase), Background Jobs (expiry, idle cleanup) | +| **Scheduling** | Booking (availability calculation), Admin (schedule management), Notifications (hours change), Background Jobs (apply-default-hours) | +| **Admin Dashboard** | Every other feature (central management hub for all operations) | +| **Loyalty & Discounts** | Payments (application at completion), Booking (auto-apply on completion), Admin (campaign management), Background Jobs (campaign transitions, redemption expiry) | +| **Today Page** | Booking (progress status, walk-in/call-in), Payments (tips, refunds), Scheduling (time blockers, daily view) | +| **Authentication** | Everything (gate across all features), Frontend (auth store), Background Jobs (token/login cleanup) | +| **GDPR & Compliance** | Auth (account deletion), User Management (data export), Background Jobs (anonymization, financial cleanup), Notifications (user_id nullification) | +| **Portfolio Gallery** | Admin (image upload), Frontend (ImageVariant, workers), Infrastructure (S3/R2 storage) | +| **Notifications** | Booking (creation/cancellation/edit), Payments (deposits), Scheduling (hours change), Gift Cards (friend purchase), Background Jobs (unpaid reminders) | +| **Infrastructure** | Background Jobs (cron scheduler), Portfolio (S3 storage), SabreDAV (contacts), Auth (CSP headers, CORS) | +| **Background Jobs** | Scheduling (hours apply), GDPR (cleanup/anonymization), Gift Cards (expiry), Payments (idempotency), Loyalty (campaign transitions), Auth (token cleanup) | +| **Contact & Location** | Admin (business settings), Frontend (MapLibre map component) | +| **Frontend Architecture** | Auth (auth store), Portfolio (ImageVariant), Notifications (bell badge), Booking (wizard components), Scheduling (time slot picker) | + +--- + +*This catalog was compiled from a full codebase audit of the Crussell platform. Each feature has been traced through backend handlers (Go), frontend components (SvelteKit), database schema (PostgreSQL), and infrastructure configuration (Docker/nginx).* diff --git a/obsidian/Crussell/Future Work - Gap Backlog.md b/obsidian/Crussell/Future Work - Gap Backlog.md index ecf48c3..63b03d9 100644 --- a/obsidian/Crussell/Future Work - Gap Backlog.md +++ b/obsidian/Crussell/Future Work - Gap Backlog.md @@ -1,37 +1,61 @@ # Future Work — Gap Backlog -**Last Updated:** June 2026 +**Last Updated:** July 2026 **Status:** Living backlog — add to this as gaps are discovered +**Previous version:** OUT OF DATE — this replaces the prior document. Completed items removed, new items added from exhaustive codebase audit. -This document is two lists: **MVP** (must do before launch) and **Stretch** (nice-to-have after launch). Items are numbered sequentially. All done items are removed — not crossed out, not tracked. If you need to know what was done, check the git history. +This document has three lists: **Pre-Launch** (integration tasks), **MVP** (missing features for daily ops), **Stretch** (nice-to-have). Items are numbered sequentially. All done items are removed without tracking. + +### Development Context + +Much of the application was built rapidly in development mode. External integrations (Square payments, S3/R2 storage, SMTP email, OAuth, error monitoring) were stubbed out as the business logic evolved — mocks and placeholders that let us move fast without configuring real services. + +Those dev placeholders have **not kept pace** with the application's feature growth. As we approach production, each needs: +1. A **production-side implementation** wired alongside the dev mock +2. The **dev stub itself** may need updating to better reflect real-world behaviour +3. **End-to-end alignment** between what the application expects and what the integration delivers + +Some items in this backlog are genuine missing features — these sit in MVP/Stretch. But many are simply "the app finished its dev journey and needs its production integrations wired up." + +--- + +## Pre-Launch — Production Integration Tasks + +These are things that work fine in dev (with mocks) but need real implementations hooked in before production goes live. Each is an expected step in the dev→prod journey — the app was built this way deliberately to stay fast. + +| # | Task | Effort | Area | Dev Status | Notes | +|---|---|---|---|---|---| +| P1 | **Square payments: wire prod client alongside dev mock** | XL (5-7d) | Backend | Mock exists (`internal/square/square_dev.go:MockClient`). Prod client (`internal/square/square.go`) returns "not yet configured" for all 8 methods. Dev `devProdClient` (`square_dev.go:38-61`) also returns stubs when `SQUARE_ENVIRONMENT=sandbox` or `production`. Only `SQUARE_ENVIRONMENT=mock` processes payments (in-memory). The health endpoint reports `"not_implemented"`. | The in-memory Square mock was great for development — it let us build the full payment flow, refund logic, split records, VAT calculation, and saved cards without touching Square's API. Now we need the production SDK wired beside it. The mock already has the interface; implement `ProdClient` with real SDK calls. | +| P2 | **S3/R2 storage: implement prod side of the abstraction** | M (2-3d) | Backend | Dev works (`internal/s3/s3_dev.go` — RustFS + in-memory fallback). Prod side (`internal/s3/s3.go:52-62`) returns "not implemented" for Upload/Download/Delete. The prod `S3Client` struct lacks the `*s3.Client` field entirely — it was never populated. | The storage abstraction was defined early and the dev side got a full implementation. The prod side needs the AWS SDK v2 dependency and real S3/R2 calls. Portfolio images and profile pictures will start working in prod once this is done. | +| P3 | **Square webhook event handling: from log-only to action** | S (1d) | Backend | Webhook signature verification works (HMAC-SHA256, references Square docs). Event parsing works. But `handlePaymentUpdated` and `handleRefundUpdated` (`square.go:88-94`) only log the event data — they never update booking/payment state. | The webhook receiver was built first (parse + verify). The handlers that act on events were deferred. Now they need to: update payment status on `payment.updated`, update refund status on `refund.updated`. | +| P4 | **Payment reconciliation: add recovery for split-brain scenarios** | L (3-5d) | Backend | 11 `log.Printf("CRITICAL: ... manual reconciliation required")` calls exist across payment, refund, and till handlers. When Square succeeds but the DB transaction fails afterwards, state diverges with no automated recovery. | This happens when the application correctly processes a Square payment but then hits a DB error on commit. In dev, this was handled by just logging it. For prod, we need a reconciliation job or retry mechanism. | +| P5 | **Till Purchases: wire the backend payment flow** | M (1d) | Frontend + Backend | Frontend (`TillPurchases.svelte:203-208`) has the UI built but the Charge button is disabled with "Payment flow and backend integration coming soon." The till sale submission path was deferred. | The till UI is fully designed — service selection, gift card types, payment method selection. Only the final "submit payment" path was left as a placeholder. Needs the backend `till.go` sale endpoint wired. | +| P6 | **Email/SMS notification delivery** | XL (5-7d) | Backend | `user_notification_preferences` table stores delivery preferences. 8 TODO markers reference this blocker. Notification creation works (admin_notifications table), but no delivery channel exists. No SMTP configuration, no SMS provider. 2 tests skipped as "WIP handler." | The notification queue works (reasons, priorities, acknowledging). What's missing is the delivery backend. Affects: slot eviction alerts, edit request approvals/denials, gift card codes, unpaid booking reminders, idle account warnings. | +| P7 | **Production security headers** | S (1h) | Backend | HSTS and Referrer-Policy headers are commented out in `main.go:231-233` with TODO markers. They were left disabled for dev HTTP convenience. | Uncomment and configure for production. | +| P8 | **Social auth stubs (Google/Microsoft/Facebook)** | L (2-3d) | Backend + Frontend | `handlers/auth/social.go` is 1 line (`package auth`). Frontend login page has 3 social buttons that show `toast.info("${provider} login coming soon")`. The `user_social_logins` table and `account_type` enum values exist from early schema design. | The schema was designed for social auth from the start (table + enum values). The OAuth flow itself was never implemented. Buttons exist as UI placeholders. | +| P9 | **Tip payments: replace placeholder card tokens** | S (1d) | Frontend | 3 tip endpoints send `card_token: 'placeholder'` — a literal string. (`UserBookingModal:190`, `tip/+page.svelte:141`, `pay-tip/[id]/+page.svelte:170`). The Square Web Payments token from the frontend card form was never wired. | The tip UI flow works (select amount, confirm). The actual card token from Square's payment form was never plumbed through. Needs to capture the real nonce/token and pass it to the backend. | +| P10 | **No automated database backups** | M (1d) | Infrastructure | PostgreSQL volume is persistent in Docker but no `pg_dump` cron, no point-in-time recovery. | Standard production DB setup task. | --- ## MVP — Must Do Before Launch -These are blockers: missing functionality that prevents daily operations, legal compliance, or basic security. No external dependencies. Each is local, can be implemented today. +These are missing functionality that prevents daily operations, legal compliance, or basic UX. Unlike the Pre-Launch tasks above, these aren't about wiring production counterparts — they're features that were genuinely deferred or overlooked. | # | Gap | Effort | Area | Notes | |---|---|---|---|---| -| 1 | **CurrentAppointment action stubs** | M (1d) | Frontend | `Extend` and `Cancel` buttons on Today page are dead. Staff cannot cancel or extend an in-progress appointment from the Today page. Edit, Take Payment, and Reschedule are already wired. | -| 2 | **~~Reservation/cleanup background cron~~** | S (3h) | Backend | **DONE**: All cleanup functions migrated to `backend/internal/jobs/` — centralised cron scheduler. 21 jobs registered with staggered schedules: cleanup-reservations (5min), cleanup-expired-deposits (5min), cleanup-rate-limiters (5min), cleanup-gdpr-export-cache (5min), cleanup-progressive-rate-limiter (1min), cleanup-expired-loyalty-redemptions (hourly), cleanup-old-idempotency-keys (hourly), cleanup-revoked-jtis (hourly), cleanup-stale-login-entries (hourly), transition-discount-campaigns (hourly), notify-unpaid-1-week (7am daily), notify-unpaid-1-month (7am daily), cleanup-verification-codes (2am daily), cleanup-refresh-tokens (2am daily), anonymize-stale-guest-accounts (3am daily), cleanup-idle-accounts (3:30am daily), cleanup-expired-financial-records (4am daily), cleanup-old-name-history (4:30am daily), cleanup-expired-gift-cards (5am daily), apply-default-hours (00:05 daily). | -| 3 | **VAT/Tax export endpoints** | M (1-2d) | Backend | `get_vat_return_data()` and `export_sales_transactions()` SQL functions exist. No admin API to trigger them. Needed for HMRC Making Tax Digital compliance. | -| 4 | **Password reset flow** | S (2-3h) | Frontend | Backend has `/api/verify/generate` and `/api/verify/check`. Login page has no "forgot password" link or form. Customers who forget their password must call the salon. | -| 5 | **Email verification flow** | S (2-3h) | Frontend | Users register with `unverified_email` role. No UI to enter verification code or resend. `+layout.svelte` has an alert-based prototype that needs to be wired properly. | -| 6 | **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 `DELETE /api/bookings/{id}` exists. | -| 7 | **Business settings management UI** | M (1-2d) | Frontend | `GET/PUT /api/admin/settings` backend endpoints exist (VAT, business name, gift card expiry months, voucher type SPV/MPV). No admin page. Staff must use `curl` or direct SQL. | -| 8 | **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 CSV. | -| 9 | **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. **Partially addressed June 2026:** toast.error() messages now escape HTML. `{@html statusBadge()` replaced with proper component. ICS injection sanitized. Error messages no longer reflect user input. CSP header added. Remaining: backend-level HTML sanitization of stored fields. | -| 10 | **CSRF protection** | S (2-3h) | Backend | SvelteKit handles CSRF for its own forms, but direct API calls to `/api/*` bypass it. Consider double-submit cookie or SameSite cookies. | -| 11 | **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. | -| 12 | **Square webhook production verification** | S (2-3h) | Backend | Current handler skips signature check if `SQUARE_WEBHOOK_SIGNATURE_KEY` is empty. Production must ALWAYS verify. See `TODO(PROD)` in `handlers/webhooks/square.go` for detailed implementation guide including Go SDK usage, base64 HMAC-SHA256, and notification URL matching. | - -### External MVP (Requires Third-Party Access) - -| # | Gap | Effort | Area | Blocked On | Notes | -|---|---|---|---|---|---| -| E5 | **Email/SMS notification system** | XL (5-7d) | Backend | SMTP provider (Resend, SendGrid, Twilio) | `user_notification_preferences` table exists but no delivery system. Blocks: booking reminders, password reset emails, deposit reduction notifications. | -| E8 | **S3/R2 production storage** | M (1d) | Backend | Cloudflare R2 or AWS S3 credentials | `s3.go` (`!dev` build tag) returns "not implemented". Prod builds cannot store portfolio images. Need AWS SDK v2 + credentials. | +| M1 | **VAT/Tax export endpoints** | M (1-2d) | Backend | 6 SQL functions exist (`get_vat_return_data`, `export_sales_transactions`, `get_monthly_business_summary`, `get_sales_totals`, `calculate_vat`, `get_receipt_data`) but no Go handler calls them. Needed for HMRC MTD compliance. | +| M2 | **Password reset — no frontend route** | S (2-3h) | Frontend | Backend has verification codes with `verification_purpose = 'password_reset'` and `/verify/generate` + `/verify/check` endpoints. No "forgot password" link or reset form exists. | +| M3 | **Email verification calls wrong API endpoint** | S (2h) | Frontend | `+layout.svelte:33` calls `/api/verify-email` which 404s. Correct endpoints: `POST /api/verify/generate` and `POST /api/verify/check`. Every login triggers a silent failure. | +| M4 | **CSRF protection** | S (2-3h) | Backend | SvelteKit handles CSRF for its own forms, but direct API calls to `/api/*` bypass it. | +| M5 | **XSS input sanitization** | S (2-3h) | Backend | Backend validates format (regex, length) but doesn't sanitize HTML entities in stored fields. CSP mitigates but doesn't eliminate risk. | +| M6 | **Square webhook signature verification — enforce always** | S (1-2h) | Backend | Currently skips if `SQUARE_WEBHOOK_SIGNATURE_KEY` is empty. Prod must always verify. Implementation exists (HMAC-SHA256), just needs enforcement. | +| M7 | **Booking cancellation UI from user account** | S (2-3h) | Frontend | `UserBookingModal` shows details but has no cancel button. Backend `DELETE /api/bookings/{id}` exists. | +| M8 | **Business settings management UI** | M (1-2d) | Frontend | `GET/PUT /api/admin/settings` endpoints exist. No admin page — staff use curl or SQL. | +| M9 | **CSV/Excel export for bookings/payments** | M (1d) | Backend | No endpoint for accounting software export. SQL functions exist but not wired. | +| M10 | **CurrentAppointment action stubs** | M (1d) | Frontend | Extend and Cancel buttons on Today page are dead. Edit/TakePayment/Reschedule are already wired. | +| M11 | **`/terms` and `/privacy` routes don't exist** | S (1h) | Frontend | Login and account pages link to these — both 404. | +| M12 | **Admin notifications list/acknowledge untested** | S (2-3h) | Backend | 2 tests skipped as WIP (`today_test.go:940,946`). Notification endpoints have zero coverage. | --- @@ -39,43 +63,57 @@ These are blockers: missing functionality that prevents daily operations, legal These improve the experience or add features, but the business can operate without them. -| # | Gap | Effort | Area | Notes | -| --- | ---------------------------------------- | -------- | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| 12 | **One-off custom services** | M (1-2d) | Full-stack | Admin can't create single-use services. Every custom job (bridal party, special request) must be added to the permanent catalog. | -| 13 | **One-off exceptional hours** | M (1d) | Full-stack | Single-day overrides (dentist appointment, afternoon off) require creating a full exceptional group. Time blockers handle unavailable periods; one-off *open* hours (e.g., "open Sunday 2pm-5pm") still need simplification. | -| 14 | **Referral system UI** | M (1-2d) | Full-stack | Backend complete — registration validates codes, relationships recorded. Users can't see their referral code or track uses. Admin can't manage referral campaigns. | -| 15 | **Analytics endpoints** | M (1-2d) | Backend | `handlers/admin/analytics.go` is 1 line. `get_monthly_business_summary()`, `get_sales_totals()` SQL functions exist but not wired. | -| 16 | **API documentation** | M (1-2d) | Backend | No OpenAPI/Swagger spec. No generated docs. New developers must read code. | -| 17 | **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. **Partially addressed June 2026:** L3 ProgressiveRateLimit added for login/register (per-IP dual-window: 30 req/5s burst + 120 req/60s sustained). Account lockout added (5+ failures → progressive 15min→2h). Remaining: user-ID tracking for authenticated endpoints. | -| 18 | **Begin button (Today page)** | S (2-3h) | Full-stack | Manual start for early arrivals. Currently auto-inferred only. Gray out if >3 hours away. | -| 19 | **Booking conflict detection for users** | S (2-3h) | Backend | Users can theoretically double-book themselves in two tabs. Reservation system helps but doesn't fully prevent. | -| 20 | **Service category/tag management** | M (1-2d) | Full-stack | Services have no category field. Admin scrolls through a flat list. No way to group (manicure vs pedicure vs nail art). | -| 21 | **No-show tracking dashboard** | S (2-3h) | Frontend | `forgiven_no_shows` table exists but no UI. Admin can't see which users have accumulated no-shows. | -| 22 | **Dark mode** | M (1-2d) | Frontend | SvelteKit + Tailwind supports it. No toggle or `prefers-color-scheme` support. | -| 23 | **PWA support** | L (3-5d) | Frontend | No service worker, no manifest.json, no offline support. Customers can't "install" the app. | -| 24 | **Recurring bookings** | L (3-5d) | Full-stack | Customers can't book the same slot weekly/monthly. Would need `recurring_bookings` table + background job. | -| 25 | **Idempotency key cleanup** | S (1h) | Backend | `idempotency_key` columns added to `bookings`, `payments`, `till_sales` with unique constraints. No retention policy — keys accumulate indefinitely. | -| 26 | **Gift card self-service portal** | M (1d) | Frontend | Users can see gift card balance on Account page but can't independently redeem to balance without admin. | - -### External Stretch (Requires Third-Party Access) - -| # | Gap | Effort | Area | Blocked On | Notes | -|---|---|---|---|---|---| -| E9 | **Social auth (Google/Microsoft/Facebook)** | L (2-3d) | Backend | OAuth app registrations + client secrets | `handlers/auth/social.go` is 1 line. | -| E10 | **Error tracking / monitoring** | M (1-2d) | Backend | Sentry DSN or equivalent | `log.Printf()` only. No alerting on 5xx. | +| # | Gap | Effort | Area | Notes | +|---|---|---|---|---| +| S1 | **One-off custom services** | M (1-2d) | Full-stack | Single-use services must be added to permanent catalog then deleted. | +| S2 | **One-off exceptional hours** | M (1d) | Full-stack | Single-day open hours require a full exceptional group. Time blockers handle closures but not openings. | +| S3 | **Referral system UI** | M (1-2d) | Full-stack | Backend complete. Users can't see their code or track uses. No admin campaign management. `affiliate` role and `affiliate_payouts` table exist but unused. | +| S4 | **Analytics endpoints** | M (1-2d) | Backend | `analytics.go` is 1 line. 4 SQL summary functions exist but no Go handler. | +| S5 | **API documentation** | M (1-2d) | Backend | No OpenAPI/Swagger spec. | +| S6 | **Per-user rate limiting** | M (1d) | Backend | Currently IP-based only. | +| S7 | **Begin button (Today page)** | S (2-3h) | Full-stack | Manual appointment start for early arrivals. | +| S8 | **Booking conflict detection for users** | S (2-3h) | Backend | Users can double-book themselves in two tabs. | +| S9 | **Service category/tag management** | M (1-2d) | Full-stack | Flat service list, no grouping. | +| S10 | **No-show tracking dashboard** | S (2-3h) | Frontend | `forgiven_no_shows` table exists but no UI. | +| S11 | **Dark mode** | M (1-2d) | Frontend | Tailwind supports it. No toggle. | +| S12 | **PWA support** | L (3-5d) | Frontend | No service worker, manifest, or offline support. | +| S13 | **Recurring bookings** | L (3-5d) | Full-stack | No weekly/monthly booking support. | +| S14 | **Gift card self-service portal** | M (1d) | Frontend | Users see balance but can't redeem without admin. | +| S15 | **Admin audit trail for account anonymization** | S (1h) | Backend | No `user_anonymized` notification when admin deletes a user. | +| S16 | **User notification of slot eviction** | S (2h) | Backend | When a booking is evicted (deposit not paid, slot reclaimed), the user is never notified. 3 TODO sites reference this. | +| S17 | **Password reset should clear lockout state** | S (1h) | Backend | TODO in `auth/local.go:427` — currently `failed_attempts`/`locked_until` aren't cleared on password reset. | --- -## Dependency Map +## Technical Debt — Cleanup -``` -E5 Email/SMS → E6 deposit reduction notifications - → booking reminders - → password reset emails +These don't add features but reduce maintenance cost and risk. -E8 S3/R2 → portfolio images in production -E9 OAuth → social login flow -E10 Sentry → error tracking, 5xx alerting -``` +| # | Task | Effort | Area | Notes | +|---|---|---|---|---| +| T1 | **Drop orphaned `square_deposits` table + function** | S (1h) | DB Schema | Full table + `generate_square_deposit_id()` function. Zero Go code references it. Square bank reconciliation was planned but never built. | +| T2 | **Remove 6 unused DB enum values** | S (2h) | DB Schema | `account_role: 'affiliate'`, `account_type: 'google'/'microsoft'/'facebook'`, `payment_status: 'failed'/'refunded'`, `discount_campaign_scope: 'first_booking_only'/'new_customers_only'`, `till_item_type: 'retail_product'` — defined but never referenced in Go code. | +| T3 | **Remove 4 unused admin_notification_reason values** | S (1h) | DB Schema | `'rescheduled_booking'`, `'gift_card_purchased_for_friend'`, `'edit_request'` (code uses `'edit_requested'`), `'deposit_paid'` (in priority ordering but never inserted). | +| T4 | **Create or remove documented `update_data_consent()` function** | S (1h) | DB Schema | Listed in FUNCTION USAGE SUMMARY comment (~line 2401) but no `CREATE FUNCTION` exists. | +| T5 | **Resolve 2 route-conflicted lint-ignored handlers** | S (1h) | Backend | `manage.go:27,314` — handlers exist only for tests but routes conflict. | +| T6 | **Resolve portfolio lint-ignored handler** | S (1h) | Backend | `images.go:53` — handler referenced from tests only, never routed. | +| T7 | **Fix README job count: 20 not 21** | S (5min) | Docs | README says "21 maintenance jobs", code registers 20. | +| T8 | **Audit 18 silent catch blocks** | M (1d) | Frontend | 1 `catch (e) {}`, 17 `catch (_err)` — errors swallowed silently. Many should show user-facing toasts. | +| T9 | **33 `svelte/no-navigation-without-resolve` suppressions** | M (1d) | Frontend | Create a project-wide `goto` wrapper instead of suppressing per-file. | +| T10 | **Replace `as any` in HolidayHours** | S (30min) | Frontend | `HolidayHours.svelte:234` — `(group.hours as any[])?.map(…)`. Hours array has known shape. | +| T11 | **Replace `e: any` in button onclick** | S (30min) | Frontend | `button.svelte:101` — click handler typed as `e: any`. | +| T12 | **Former name display (4 TODO sites)** | S (1d) | Frontend + Backend | 4 TODOs across GiftCards + notifications needing `previousFirstName`/`previousLastName` from backend. | +| T13 | **Fix `devProdClient` rune-arithmetic in test** | S (30min) | Backend | `square_dev_test.go:410` — `rune('0'+idx)` breaks for indices >= 10 (`:` not a digit). | +| T14 | **Error tracking / monitoring (Sentry)** | M (1-2d) | Backend | `log.Printf()` only. No alerting on 5xx. 39 ALERT + 11 CRITICAL logs will never be seen. | -All local (MVP + Stretch) items have zero external dependencies. +--- + +## Previously Completed Items (June 2026 backlog) + +- ~~Reservation/cleanup background cron~~ — All 20 jobs migrated to centralized scheduler +- ~~XSS input sanitization (partial)~~ — CSP added, error messages sanitized, ICS injection fixed +- ~~Per-user rate limiting (partial)~~ — L3 ProgressiveRateLimit added for login/register + +--- + +*This catalog was compiled from a full codebase audit (July 2026): 4 parallel deep-dive agents covering backend TODOs, frontend gaps, implied stubs, and DB schema analysis. Every item traces to specific file paths and line numbers.*