docs: update documentation for custom services feature

Update Technical Manual (handlers, API endpoints, component hierarchy, tables), Admin Manual (new Custom Services section, updated FAQ, booking creation), Overview (test count, admin features), and README (new feature line, admin updates).

Ultraworked with Sisyphus (https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
2026-06-15 17:02:57 +01:00
co-authored by Sisyphus
parent 867f86978b
commit 6c27a44302
4 changed files with 34 additions and 8 deletions
+4 -2
View File
@@ -12,7 +12,9 @@ Nail salon booking platform — Go 1.25 backend + SvelteKit 5 SPA + PostgreSQL 1
**Scheduling**: Default weekly hours, holiday/exceptional groups, time blockers (one-off + recurring with cron). Lunch protection. Late-night lock (22:0011:00). Admin schedule page (Google Calendar-style week view). **Scheduling**: Default weekly hours, holiday/exceptional groups, time blockers (one-off + recurring with cron). Lunch protection. Late-night lock (22:0011:00). Admin schedule page (Google Calendar-style week view).
**Admin**: Today page with interactive calendar grid. Booking management (create, edit, reschedule, approve, cancel). User management with customer relationship data (spend, visits, top services). Discount campaigns (time-based and milestone). Time blocker CRUD. Portfolio image upload with tag management. Gift card management. Business settings (VAT, gift card config). Notification queue with priority ordering. **Custom Services**: One-off or special-request services not in the permanent catalog. Admin management with create, edit, promote to permanent service (migrates booking references), and delete. Full CRUD API with search, popular sorting, and pagination. Can be added to any booking alongside regular services.
**Admin**: Today page with interactive calendar grid. Booking management (create, edit, reschedule, approve, cancel). User management with customer relationship data (spend, visits, top services). Custom services (one-off services with create/edit/promote/delete). Discount campaigns (time-based and milestone). Time blocker CRUD. Portfolio image upload with tag management. Gift card management. Business settings (VAT, gift card config). Notification queue with priority ordering.
**Loyalty & Discounts**: 1 stamp per paid appointment (max 1/day). 10 stamps → 10% off next visit. Campaigns: time-based, per-user milestone, global milestone, anniversary. All discounts stack additively against original total. **Loyalty & Discounts**: 1 stamp per paid appointment (max 1/day). 10 stamps → 10% off next visit. Campaigns: time-based, per-user milestone, global milestone, anniversary. All discounts stack additively against original total.
@@ -72,7 +74,7 @@ Default logins (password: `password`):
```bash ```bash
cd backend && go build -o bin/backend ./main.go cd backend && go build -o bin/backend ./main.go
cd frontend && npm ci && npm run build cd frontend && npm ci && npm run build
cd backend && go test -tags "test,dev" -p 1 ./... # 633/636 passing, 3 skipped cd backend && go test -tags "test,dev" -p 1 ./... # 682/685 passing, 3 skipped
``` ```
## Full Documentation ## Full Documentation
+17 -2
View File
@@ -175,6 +175,21 @@ The Services section lets you manage the salon's treatment list:
**Patch test duration:** When you create a service, you can set how many hours a patch test takes. If the duration is more than 0 hours, the system automatically creates a patch test record for that service. **Patch test duration:** When you create a service, you can set how many hours a patch test takes. If the duration is more than 0 hours, the system automatically creates a patch test record for that service.
### Custom Services
Custom services are one-off or special-request treatments that aren't in the permanent catalog. For example, a bridal party wants a specific nail design that you don't normally offer.
You can manage custom services in the **Custom Services** section:
- **Create** — Add a custom service with name, description, price, duration, and minimum age. A custom service will not appear on the public price list.
- **Edit** — Update the name, price, duration, or other details.
- **Promote** — Convert a custom service into a permanent service. This migrates all existing booking references from the custom service to the new permanent service. Use this when a one-off design becomes popular enough to offer regularly.
- **Delete** — Remove a custom service. You can only delete custom services that have never been used in a booking.
- **Search** — Search by name or keyword.
- **Popular** — See which custom services are most frequently used.
Custom services can be added to bookings from the **Booking Create** and **Walk-In** flows, just like regular services.
### Scheduling ### Scheduling
The Scheduling section has three parts: The Scheduling section has three parts:
@@ -282,7 +297,7 @@ When a customer calls to book over the phone:
2. Click **Create Booking** 2. Click **Create Booking**
3. Select the customer (search by name or email) 3. Select the customer (search by name or email)
4. The system shows their patch test records and age — services they're not eligible for are greyed out 4. The system shows their patch test records and age — services they're not eligible for are greyed out
5. Select the services they want 5. Select the services they want — you can also add **custom services** (one-off services not in the public list) alongside regular services
6. Pick a date and time 6. Pick a date and time
7. The system holds the slot for 1 hour (call-in reservation) 7. The system holds the slot for 1 hour (call-in reservation)
8. Fill in notes if needed 8. Fill in notes if needed
@@ -616,4 +631,4 @@ Pending bookings need admin approval. If the booking has notes or is for today,
### "A customer says they want to create a custom service (bridal party, etc.)" ### "A customer says they want to create a custom service (bridal party, etc.)"
Currently, all services must be added to the permanent catalog. There's no way to create a one-off custom service. Add it to the Services list, then delete it after the appointment if needed. You can now create **custom services** — one-off or special-request services that aren't part of the permanent catalog. Go to the **Admin** dashboard → **Custom Services** section. Create the custom service with name, price, and duration — it won't appear in the public service list. Custom services can be added to any booking from the admin booking flows. You can also promote a custom service to a permanent service if you decide to offer it regularly.
+2 -2
View File
@@ -65,7 +65,7 @@ Lunch protection: `findAllLunchGaps()` returns all gap durations in the middle w
**Today page** (`/today`): current + next appointment cards, interactive daily calendar grid, pending approvals queue, today stats summary (total/confirmed/pending/completed). Walk-in booking wizard (3-step) and call-in wizard (4-step) with slot reservation. **Today page** (`/today`): current + next appointment cards, interactive daily calendar grid, pending approvals queue, today stats summary (total/confirmed/pending/completed). Walk-in booking wizard (3-step) and call-in wizard (4-step) with slot reservation.
**Admin dashboard** (`/admin`): users list + detail modal (profile, bookings, relationship data, patch tests, loyalty/referrals, privacy/consent), services CRUD, bookings list with search, scheduling management (default hours, exceptional groups, time blockers), discount campaigns, portfolio image upload with tag management, gift card management, business settings. **Admin dashboard** (`/admin`): users list + detail modal (profile, bookings, relationship data, patch tests, loyalty/referrals, privacy/consent), services CRUD, **custom services management** (one-off services with create/edit/promote/delete), bookings list with search, scheduling management (default hours, exceptional groups, time blockers), discount campaigns, portfolio image upload with tag management, gift card management, business settings.
**Reschedule modal**: search available slots, conflict detection (overlapping bookings), one-click confirm. Side-by-side enriched snapshots in Pending Approvals for edit requests. **Reschedule modal**: search available slots, conflict detection (overlapping bookings), one-click confirm. Side-by-side enriched snapshots in Pending Approvals for edit requests.
@@ -202,7 +202,7 @@ npm run dev # Dev server with HMR
```bash ```bash
cd backend cd backend
go test -tags "test,dev" -p 1 -count=1 ./... # 633/636 passing, 3 skipped go test -tags "test,dev" -p 1 -count=1 ./... # 682/685 passing, 3 skipped
go test -tags "test,dev" -v -run TestName ./... # Single test go test -tags "test,dev" -v -run TestName ./... # Single test
``` ```
+11 -2
View File
@@ -51,7 +51,7 @@ Backend (:8080)
| `handlers/bookings` | bookings.go, reserve.go, manage.go, admin_reserve.go | Booking CRUD, reservations, admin management, edit requests, discounts, closing hours validation, active booking limits, GetBookingsByCreatedRange, created_by_name resolution | | `handlers/bookings` | bookings.go, reserve.go, manage.go, admin_reserve.go | Booking CRUD, reservations, admin management, edit requests, discounts, closing hours validation, active booking limits, GetBookingsByCreatedRange, created_by_name resolution |
| `handlers/payments` | handlers.go, service.go, validators.go, giftcards.go, till.go | Square payments: terminal, online, refunds, tips, saved cards, gift cards (CRUD, topup, transfer, redeem, buy, expired balances, till sales) | | `handlers/payments` | handlers.go, service.go, validators.go, giftcards.go, till.go | Square payments: terminal, online, refunds, tips, saved cards, gift cards (CRUD, topup, transfer, redeem, buy, expired balances, till sales) |
| `handlers/webhooks` | square.go | Square webhook handler for payment status updates | | `handlers/webhooks` | square.go | Square webhook handler for payment status updates |
| `handlers/admin` | users.go, analytics.go, discount_campaigns.go, settings.go | Admin user management, discount campaigns, analytics (stub), business settings (GET/PUT with VAT, gift card config) | | `handlers/admin` | users.go, analytics.go, custom_services.go, discount_campaigns.go, settings.go | Admin user management, custom services CRUD (list/create/get/update/promote/delete), discount campaigns, analytics (stub), business settings (GET/PUT with VAT, gift card config) |
| `handlers/today` | today.go | Current/next appointment, today's grid, pending approvals | | `handlers/today` | today.go | Current/next appointment, today's grid, pending approvals |
| `handlers/user` | profile.go, account.go, guest.go, loyalty.go, customer_relationship.go, gdpr_export.go | User profile, guest creation (with CheckEmailHandler for registered-email detection), loyalty, contact info, GDPR export (async with 12h cache) | | `handlers/user` | profile.go, account.go, guest.go, loyalty.go, customer_relationship.go, gdpr_export.go | User profile, guest creation (with CheckEmailHandler for registered-email detection), loyalty, contact info, GDPR export (async with 12h cache) |
| `handlers/services` | services.go | Service catalog, eligibility filtering, patch_test_duration_hours auto-creates patch test records | | `handlers/services` | services.go | Service catalog, eligibility filtering, patch_test_duration_hours auto-creates patch test records |
@@ -121,6 +121,7 @@ src/lib/components/
│ ├── BookingCreateModal.svelte # Admin booking creation (4-step, uses shared timeSlots utils) │ ├── BookingCreateModal.svelte # Admin booking creation (4-step, uses shared timeSlots utils)
│ ├── BookingModal.svelte # View booking details (shows created_by_name) │ ├── BookingModal.svelte # View booking details (shows created_by_name)
│ ├── BookingsCard.svelte # Bookings list │ ├── BookingsCard.svelte # Bookings list
│ ├── CustomServicesManagement.svelte # Custom services CRUD (list/create/edit/promote/delete)
│ ├── CallInBooking.svelte # Call-in booking flow │ ├── CallInBooking.svelte # Call-in booking flow
│ ├── EditBookingModal.svelte # Edit booking services │ ├── EditBookingModal.svelte # Edit booking services
│ ├── HolidayHours.svelte # Exceptional schedule management │ ├── HolidayHours.svelte # Exceptional schedule management
@@ -323,6 +324,12 @@ src/lib/components/
| POST | `/api/admin/gift-cards/{id}/transfer` | Transfer gift card to another user | | POST | `/api/admin/gift-cards/{id}/transfer` | Transfer gift card to another user |
| POST | `/api/admin/gift-cards/{id}/redeem` | Redeem gift card to account balance | | POST | `/api/admin/gift-cards/{id}/redeem` | Redeem gift card to account balance |
| POST | `/api/gift-cards/buy` | User buys gift card online (with idempotency_key) | | POST | `/api/gift-cards/buy` | User buys gift card online (with idempotency_key) |
| GET | `/api/admin/custom-services` | List custom services (search `q`, popular, pagination `page`/`per_page`) |
| POST | `/api/admin/custom-services` | Create custom service (name, price, duration, minimum age, notes) |
| GET | `/api/admin/custom-services/{id}` | Get single custom service |
| PUT | `/api/admin/custom-services/{id}` | Update custom service fields |
| POST | `/api/admin/custom-services/{id}/promote` | Promote custom service to regular service (migrates booking references) |
| DELETE | `/api/admin/custom-services/{id}` | Delete custom service (409 if usage_count > 0) |
--- ---
@@ -347,7 +354,7 @@ src/lib/components/
| `discount_campaign_status` | `draft`, `active`, `completed`, `cancelled` | | `discount_campaign_status` | `draft`, `active`, `completed`, `cancelled` |
| `till_item_type` | `gift_card`, `merchandise`, `service` | | `till_item_type` | `gift_card`, `merchandise`, `service` |
### Tables (30 total) ### Tables (37 total)
| Table | Purpose | | Table | Purpose |
|-------|---------| |-------|---------|
@@ -357,6 +364,8 @@ src/lib/components/
| `patch_tests` | Patch test definitions (notice_duration_hours, expiry_months, service_ids) | | `patch_tests` | Patch test definitions (notice_duration_hours, expiry_months, service_ids) |
| `user_patch_tests` | User patch test completion records (tested_at, notes) | | `user_patch_tests` | User patch test completion records (tested_at, notes) |
| `services` | Service offerings | | `services` | Service offerings |
| `custom_services` | One-off / special-request services (usage_count, created_by) |
| `booking_custom_services` | Custom services per booking (override_price, override_duration_minutes) |
| `bookings` | Appointment records (idempotency_key, deposit_required, deposit_paid, deposit_amount, deposit_deadline) | | `bookings` | Appointment records (idempotency_key, deposit_required, deposit_paid, deposit_amount, deposit_deadline) |
| `booking_services` | Services per booking (override_price, override_duration_minutes) | | `booking_services` | Services per booking (override_price, override_duration_minutes) |
| `booking_edit_requests` | Pending customer edit requests | | `booking_edit_requests` | Pending customer edit requests |