feat: loyalty/discount system with milestone campaigns, auto-redemption, and admin UI

- Database: loyalty_redemptions, discount_campaigns, booking_discounts tables
- Backend: auto-create pending redemption at 10 stamps, apply discounts at completion
- Backend: discount_eligible flag on booking creation (user + admin flows)
- Backend: campaign CRUD handlers (GET/POST/PUT/DELETE + stats)
- Backend: milestone campaigns (per-user, global, anniversary)
- Frontend: customer account page shows 'card full' status at 10 stamps
- Frontend: admin discounts page with campaign management UI
- Frontend: TypeScript types for all discount entities
- Tests: 9 integration tests covering loyalty, campaigns, milestones, edge cases
This commit is contained in:
2026-05-08 17:32:20 +01:00
parent bec4100e4d
commit 1f54d8565c
9 changed files with 2864 additions and 8 deletions
+9
View File
@@ -21,6 +21,7 @@ import (
"crussell/mw"
authHandlers "crussell/handlers/auth"
"crussell/handlers/admin"
"crussell/handlers/bookings"
"crussell/handlers/notifications"
"crussell/handlers/portfolio"
@@ -270,6 +271,14 @@ r.Route("/admin/users", func(r chi.Router) {
r.Post("/", scheduling.CreateTimeBlocker)
r.Delete("/{id}", scheduling.DeleteTimeBlocker)
})
r.Route("/admin/discount-campaigns", func(r chi.Router) {
r.Get("/", admin.GetDiscountCampaigns)
r.Post("/", admin.CreateDiscountCampaign)
r.Put("/{id}", admin.UpdateDiscountCampaign)
r.Delete("/{id}", admin.DeleteDiscountCampaign)
r.Get("/{id}/stats", admin.GetCampaignStats)
})
})
})