feat: admin notification system with priority ordering, bell icon, and /notifications page

Two-tier notification system: new_booking (all public bookings) + pending_booking (notes/today).
Priority-sorted queue, unread count polling, enriched responses with user_name/booking_start_time.
Fix critical bug: edit_requested cleanup was broken (wrong reason string in 3 handlers).
Add 15 new tests covering priority ordering, enrichment, and notification creation flows.
Update Admin Manual, Technical Manual, and gap backlog docs.
This commit is contained in:
2026-05-16 23:41:18 +01:00
parent c3501ae89a
commit 7fc58f58d9
19 changed files with 1608 additions and 106 deletions
+2 -2
View File
@@ -171,11 +171,11 @@
},
"active": "0e456d61bc5b6ded",
"lastOpenFiles": [
"Crussell/Overview.md",
"Crussell/Future Work - Gap Backlog.md",
"Crussell/Technical Manual.md",
"Crussell/User Manual.md",
"Crussell/Admin Manual.md",
"Crussell/Overview.md",
"Crussell/Future Work - Gap Backlog.md",
"Crussell/Test Implementation Plan.md",
"Crussell/Crussell Nails.md",
"Crussell/Backend/bookings.md",
+69
View File
@@ -328,6 +328,75 @@ The window shows the full booking details so you can review them before making y
---
## Notifications
The notifications page keeps you informed about everything happening with your bookings and customers. You'll find a bell icon in the top-right corner of the navigation bar — if there's a red dot on it, you have unread notifications.
### How to Access
Click the **bell icon** in the top-right corner of the website to go to the Notifications page. The bell shows a number indicating how many unread notifications you have.
### What You'll See
Notifications are sorted by importance, with the most urgent at the top:
1. **Booking Pending Approval** — A booking needs your review and approval
2. **Booking Cancelled** — A booking was cancelled
3. **Late Cancellation (< 24h)** — A customer cancelled less than 24 hours before their appointment
4. **Deposit Issue** — A deposit-related problem
5. **Deposit Payment Received** — A deposit payment came through
6. **Affiliate Referral Claimed** — Someone used a referral code
7. **Booking Edit Requested** — A customer wants to change their booking
8. **New Booking Received** — A new booking was made online
9. **No Payments in 1 Month** — No payments recorded in the last month
10. **No Payments in 1 Week** — No payments recorded in the last week
Within each priority level, older notifications appear first so you see the ones that have been waiting longest.
### What Each Notification Does
**Booking Pending Approval:**
- Shows **"Approve Booking"** button
- Opens the approval window where you can confirm or decline the booking
- You can also adjust the price, duration, and add notes before confirming
**New Booking Received / Booking Edit Requested:**
- Shows **"See Booking"** button
- Opens a read-only view of the booking details
- No approval needed — just for your awareness
**Late Cancellation / Deposit Issue / No Payments:**
- Shows **"See User"** button
- Opens the customer's full profile so you can review their history, spending, and notes
**All other notifications:**
- Show only the **"Acknowledge"** button
- These are informational — no further action needed
### Acknowledging Notifications
Every notification has an **"Acknowledge"** button. Clicking it marks the notification as seen and removes it from the default view.
When you click **"Approve Booking"**, **"See Booking"**, or **"See User"**, the notification is automatically acknowledged for you — you don't need to click Acknowledge separately.
### Viewing Old Notifications
At the top of the Notifications page, there's a **"Show acknowledged"** toggle. Turn it on to see all notifications you've already acknowledged, sorted newest first. This is useful if you need to look back at something you've already dealt with.
### Pagination
If you have many notifications, they're split into pages of 20. Use the **Previous** and **Next** buttons at the bottom to navigate.
### How Notifications Are Created
- **Every booking** made through the website creates a "New Booking Received" notification
- If a booking has **notes** attached or is for **today**, an additional "Booking Pending Approval" notification is also created so you know it needs your attention
- **Edit requests** from customers create a "Booking Edit Requested" notification
- If the booking being edited is still **pending**, an additional "Booking Pending Approval" notification is also created
- Admin-created bookings (walk-ins, call-ins) do **not** create notifications — you already know about them because you created them
---
## Uploading Portfolio Images
The portfolio is the salon's gallery of nail art photos that customers can browse on the website.
@@ -20,7 +20,7 @@ No external dependencies. No paid services. No API keys needed.
| # | Gap | Effort | Area | Notes |
| ----- | -------------------------------------------------------- | -------- | ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 5 | **Admin notification panel** | M (1-2d) | Frontend | Backend fully wired (GET/acknowledge). No frontend UI to display notifications. Admin has no visibility into pending bookings, cancellations, no-shows. |
| ~~5~~ | ~~**Admin notification panel**~~ | ~~M (1-2d)~~ | ~~Frontend~~ | ~~Backend fully wired (GET/acknowledge). No frontend UI to display notifications. Admin has no visibility into pending bookings, cancellations, no-shows.~~ Two-tier system live: `new_booking` (all) + `pending_booking` (notes/today). Priority ordering, bell icon, `/notifications` page, acknowledge flow, enriched responses.
| 6 | **Reservation/anonymization cron** | S (2-3h) | Backend | `CleanupOldReservations()` and `AnonymizeStaleGuestAccounts()` only fire on availability fetch. If no one fetches availability, expired reservations persist and stale guests aren't anonymized. Should be a background ticker in `main.go`. |
| 7 | **GDPR data export endpoint** | M (1d) | Backend | `export_all_user_data()` SQL function exists (JSON export). No Go handler wired. Required for GDPR Article 15 SAR requests. |
| 8 | **VAT/Tax export endpoints** | M (1-2d) | Backend | `get_vat_return_data()`, `export_sales_transactions()` SQL functions exist. No admin API to trigger them. Needed for HMRC compliance. |
@@ -143,7 +143,7 @@ Require paid accounts, API approval, or external service credentials. **Do not a
│ │
│ #3 Approval decline ✅──→ #13 Booking reschedule │
│ │
│ #5 Admin notification panel ──→ #15 Preferences UI │
~~#5 Admin notification panel~~ ✅ ──→ #15 Preferences UI │
│ ──→ #48 Waitlist (removed) │
│ │
│ #2 Walk-in guest fix ──→ #9 Reservation transition │
@@ -179,7 +179,7 @@ Require paid accounts, API approval, or external service credentials. **Do not a
9. **#2** Wire WalkInCreateModal guest booking (1-2h) ✅
10. **#3** ApprovalModal decline/cancel (2-3h) ✅
11. **#5** Admin notification panel (1-2d)
11. ~~**#5** Admin notification panel (1-2d)~~
12. **#4** CurrentAppointment Extend + Cancel actions (1d) — skip TakePayment (blocked on E1). **Edit** ✅ — now opens `EditBookingModal` for service management.
13. **#12** Booking cancellation from user account (2-3h)
14. **#40** No-show tracking dashboard (2-3h)
+33 -1
View File
@@ -230,6 +230,7 @@ src/lib/components/
| GET | `/api/admin/today/appointments` | Today's appointments |
| GET | `/api/admin/today/pending-approvals` | Pending approval queue |
| GET | `/api/admin/notifications` | List notifications |
| GET | `/api/admin/notifications/unread-count` | Unread count for bell icon |
| POST | `/api/admin/notifications/{id}/acknowledge` | Acknowledge notification |
| GET | `/api/admin/time-blockers` | List time blockers |
| POST | `/api/admin/time-blockers` | Create time blocker |
@@ -261,7 +262,7 @@ src/lib/components/
| `payment_type` | `deposit`, `full`, `tip`, `balance`, `partial` |
| `payment_method` | `online_square`, `in_person_card`, `cash`, `giftcard`, `discount` |
| `payment_status` | `pending`, `completed`, `failed`, `refunded` |
| `admin_notification_reason` | `pending_booking`, `cancelled_booking`, `rescheduled_booking`, `1_week_no_pay`, `1_month_no_pay`, `affiliate_claim`, `late_cancellation`, `no_deposit`, `deposit_paid`, `edit_request` |
| `admin_notification_reason` | `pending_booking`, `cancelled_booking`, `rescheduled_booking`, `1_week_no_pay`, `1_month_no_pay`, `affiliate_claim`, `late_cancellation`, `no_deposit`, `deposit_paid`, `edit_request`, `new_booking`, `edit_requested` |
| `campaign_type` | `time_based`, `milestone` |
| `milestone_type` | `per_user_booking_count`, `global_booking_count`, `anniversary` |
| `milestone_unit` | `bookings`, `months`, `years` |
@@ -452,6 +453,37 @@ src/lib/components/
---
### Notifications
**How it works:** Two-tier notification system. Every public booking creates a `new_booking` notification (low priority, acknowledge-only). If the booking has notes or is for today, an additional `pending_booking` notification is also created (high priority, approve/deny action).
**Endpoints:**
- `GET /api/admin/notifications` — List notifications. Query params: `page`, `per_page`, `include_acknowledged` (bool), `reason` (filter). Default: unacknowledged only, sorted by priority CASE WHEN then oldest-first. With `include_acknowledged=true`: all notifications, newest-first.
- `GET /api/admin/notifications/unread-count` — Returns `{"count": N}` for the bell icon.
- `POST /api/admin/notifications/{id}/acknowledge` — Sets `acknowledged_at = NOW()`. Idempotent (404 if already acknowledged).
**Priority order** (SQL CASE WHEN):
| Priority | Reason | Frontend Action |
|----------|--------|-----------------|
| 1 | `pending_booking` | Approve/Decline (ApprovalModal) |
| 2 | `cancelled_booking` | Acknowledge |
| 3 | `late_cancellation` | Acknowledge + See User |
| 4 | `no_deposit` | Acknowledge + See User |
| 5 | `deposit_paid` | Acknowledge |
| 6 | `affiliate_claim` | Acknowledge |
| 7 | `edit_requested` | See Booking (BookingModal) |
| 8 | `new_booking` | See Booking (BookingModal) |
| 9 | `1_month_no_pay` | Acknowledge + See User |
| 10 | `1_week_no_pay` | Acknowledge + See User |
**Auto-acknowledge behavior:** Clicking "Approve Booking", "See Booking", or "See User" automatically acknowledges the notification before opening the modal. The standalone "Acknowledge" button is for dismissing without action.
**Creation sources:**
- Public bookings (`POST /api/bookings`) → always `new_booking`, plus `pending_booking` if notes or today
- Edit requests (`POST /api/bookings/{id}/edit-request`) → always `edit_requested`, plus `pending_booking` if booking status is pending
- Admin bookings (`POST /api/admin/bookings`) → no notifications (admin already knows)
### Loyalty & Discount System
**Loyalty Stamps:**