feat(loyalty-discount): implement loyalty and discount system

- Add discount campaign management and validation logic
- Update booking handlers with discount application flow
- Add customer relationship endpoints for loyalty tracking
- Update frontend modals (booking, approval, payment, reschedule)
- Add DiscountsManagement and loyalty reference documentation
- Update dev scripts and database init for discount tables
- Clean up completed plan files
This commit is contained in:
2026-06-04 23:13:02 +01:00
parent 79b23a3cf7
commit 6d4bc4d637
28 changed files with 2248 additions and 1461 deletions
@@ -96,7 +96,7 @@
if (form.discount_percent <= 0 || form.discount_percent > 100) return false;
if (form.campaign_type === 'time_based') {
if (!form.start_date || !form.end_date) return false;
if (new SvelteDate(form.end_date) <= new SvelteDate(form.start_date)) return false;
if (new SvelteDate(form.end_date) < new SvelteDate(form.start_date)) return false;
}
if (form.campaign_type === 'milestone') {
if (form.milestone_value <= 0) return false;
@@ -146,8 +146,8 @@
campaign_type: c.campaign_type,
discount_percent: c.discount_percent,
scope: c.scope || 'all_bookings',
start_date: c.start_date ? new Date(c.start_date).toISOString().slice(0, 16) : '',
end_date: c.end_date ? new Date(c.end_date).toISOString().slice(0, 16) : '',
start_date: c.start_date ? new Date(c.start_date).toISOString().slice(0, 10) : '',
end_date: c.end_date ? new Date(c.end_date).toISOString().slice(0, 10) : '',
milestone_type: c.milestone_type || 'per_user_booking_count',
milestone_value: c.milestone_value || 0,
milestone_unit: c.milestone_unit || 'bookings',
@@ -168,7 +168,7 @@
if (
form.start_date &&
form.end_date &&
new Date(form.end_date) <= new Date(form.start_date)
new Date(form.end_date) < new Date(form.start_date)
) {
errors.end_date = 'Must be after start';
}
@@ -188,8 +188,8 @@
};
if (form.campaign_type === 'time_based') {
payload.scope = form.scope;
payload.start_date = new Date(form.start_date).toISOString();
payload.end_date = new Date(form.end_date).toISOString();
payload.start_date = form.start_date + 'T00:00:00.000Z';
payload.end_date = form.end_date + 'T23:59:59.999Z';
} else {
payload.milestone_type = form.milestone_type;
payload.milestone_value = form.milestone_value;
@@ -562,12 +562,12 @@
<div class="grid grid-cols-1 gap-3 sm:grid-cols-2">
<div class="space-y-2">
<Label.Root for="dc-start">Start Date *</Label.Root>
<Input id="dc-start" type="datetime-local" bind:value={form.start_date} />
<Input id="dc-start" type="date" bind:value={form.start_date} />
{#if errors.start_date}<p class="text-xs text-red-500">{errors.start_date}</p>{/if}
</div>
<div class="space-y-2">
<Label.Root for="dc-end">End Date *</Label.Root>
<Input id="dc-end" type="datetime-local" bind:value={form.end_date} />
<Input id="dc-end" type="date" bind:value={form.end_date} />
{#if errors.end_date}<p class="text-xs text-red-500">{errors.end_date}</p>{/if}
</div>
</div>