@getmicdrop/svelte-components 2.0.13 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__LIB_STORES__.js +30 -2
- package/dist/components/AboutShow/AboutShow.svelte +278 -0
- package/dist/components/AboutShow/AboutShow.svelte.d.ts +43 -0
- package/dist/components/AboutShow/AboutShow.svelte.d.ts.map +1 -0
- package/dist/components/Calendar/MiniMonthCalendar.svelte +1446 -0
- package/dist/components/Calendar/{Calendar.svelte.d.ts → MiniMonthCalendar.svelte.d.ts} +20 -21
- package/dist/components/Calendar/MiniMonthCalendar.svelte.d.ts.map +1 -0
- package/dist/components/DarkModeToggle.svelte +3 -1
- package/dist/components/DarkModeToggle.svelte.d.ts.map +1 -1
- package/dist/components/FAQs/FAQs.svelte +49 -0
- package/dist/components/{Calendar/QuarterView.svelte.d.ts → FAQs/FAQs.svelte.d.ts} +10 -10
- package/dist/components/FAQs/FAQs.svelte.d.ts.map +1 -0
- package/dist/components/Input/Input.svelte +100 -12
- package/dist/components/Input/Input.svelte.d.ts +12 -0
- package/dist/components/Input/Input.svelte.d.ts.map +1 -1
- package/dist/components/Input/OTPInput.svelte +1 -1
- package/dist/components/MonthSwitcher/MonthSwitcher.svelte +206 -0
- package/dist/components/MonthSwitcher/MonthSwitcher.svelte.d.ts +37 -0
- package/dist/components/MonthSwitcher/MonthSwitcher.svelte.d.ts.map +1 -0
- package/dist/components/OrderSummary/OrderSummary.svelte +553 -0
- package/dist/components/OrderSummary/OrderSummary.svelte.d.ts +65 -0
- package/dist/components/OrderSummary/OrderSummary.svelte.d.ts.map +1 -0
- package/dist/components/PublicCard/PublicCard.svelte +267 -0
- package/dist/components/{pages/performers/AvailabilityCalendarModal.svelte.d.ts → PublicCard/PublicCard.svelte.d.ts} +12 -14
- package/dist/components/PublicCard/PublicCard.svelte.d.ts.map +1 -0
- package/dist/components/ShowCard/ShowCard.svelte +240 -0
- package/dist/components/ShowCard/ShowCard.svelte.d.ts +39 -0
- package/dist/components/ShowCard/ShowCard.svelte.d.ts.map +1 -0
- package/dist/components/ShowTimeCard/ShowTimeCard.svelte +92 -0
- package/dist/components/{Calendar/QuarterView.stories.svelte.d.ts → ShowTimeCard/ShowTimeCard.svelte.d.ts} +17 -21
- package/dist/components/ShowTimeCard/ShowTimeCard.svelte.d.ts.map +1 -0
- package/dist/components/Spinner/Spinner.svelte +73 -17
- package/dist/components/Spinner/Spinner.svelte.d.ts +5 -3
- package/dist/components/Spinner/Spinner.svelte.d.ts.map +1 -1
- package/dist/components/pages/performers/ShowDetails.svelte.d.ts +2 -2
- package/dist/components/pages/performers/ShowItemCard.svelte.d.ts +6 -6
- package/dist/components/pages/performers/VenueItemCard.svelte.d.ts +2 -2
- package/dist/components/pages/shows/TabNavigation.svelte +7 -8
- package/dist/index.d.ts +8 -3
- package/dist/index.js +12 -3
- package/dist/services/EventService.js +75 -75
- package/dist/services/EventService.spec.js +217 -217
- package/dist/services/ShowService.spec.js +342 -342
- package/package.json +160 -160
- package/dist/components/Calendar/Calendar.spec.d.ts +0 -2
- package/dist/components/Calendar/Calendar.spec.d.ts.map +0 -1
- package/dist/components/Calendar/Calendar.spec.js +0 -131
- package/dist/components/Calendar/Calendar.svelte +0 -1115
- package/dist/components/Calendar/Calendar.svelte.d.ts.map +0 -1
- package/dist/components/Calendar/QuarterView.spec.d.ts +0 -2
- package/dist/components/Calendar/QuarterView.spec.d.ts.map +0 -1
- package/dist/components/Calendar/QuarterView.spec.js +0 -394
- package/dist/components/Calendar/QuarterView.stories.svelte +0 -134
- package/dist/components/Calendar/QuarterView.stories.svelte.d.ts.map +0 -1
- package/dist/components/Calendar/QuarterView.svelte +0 -736
- package/dist/components/Calendar/QuarterView.svelte.d.ts.map +0 -1
- package/dist/components/pages/performers/AvailabilityCalendarModal.svelte +0 -632
- package/dist/components/pages/performers/AvailabilityCalendarModal.svelte.d.ts.map +0 -1
|
@@ -0,0 +1,553 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { fly, fade } from 'svelte/transition';
|
|
3
|
+
import { cubicOut } from 'svelte/easing';
|
|
4
|
+
import { createEventDispatcher } from 'svelte';
|
|
5
|
+
import ChevronDown from 'carbon-icons-svelte/lib/ChevronDown.svelte';
|
|
6
|
+
import Close from 'carbon-icons-svelte/lib/Close.svelte';
|
|
7
|
+
import Spinner from '../Spinner/Spinner.svelte';
|
|
8
|
+
|
|
9
|
+
export let loading = false;
|
|
10
|
+
export let quantities = {};
|
|
11
|
+
export let eventTickets = [];
|
|
12
|
+
|
|
13
|
+
export let checkoutTicket = null;
|
|
14
|
+
export let isAgreed = true;
|
|
15
|
+
export let btnText = 'Checkout';
|
|
16
|
+
export let promoApplied = false;
|
|
17
|
+
export let promoDiscount = 0;
|
|
18
|
+
export let currentPromoRule = null;
|
|
19
|
+
|
|
20
|
+
export let executePurchase = null;
|
|
21
|
+
export let elements = null;
|
|
22
|
+
|
|
23
|
+
export let venueServiceCharge = {
|
|
24
|
+
serviceFeeCents: 0,
|
|
25
|
+
serviceFeePercentage: 0,
|
|
26
|
+
serviceFeeChargeType: 'both', // 'both', 'percent', or 'flat'
|
|
27
|
+
maxServiceFeeCents: 0, // 0 means no cap
|
|
28
|
+
taxPercentage: 0, // Default 0% - only charge tax if venue has set it
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
$: feeFor = price => {
|
|
32
|
+
const chargeType = venueServiceCharge.serviceFeeChargeType || 'both';
|
|
33
|
+
let fee = 0;
|
|
34
|
+
|
|
35
|
+
// Calculate fee based on charge type
|
|
36
|
+
if (chargeType === 'flat' || chargeType === 'both') {
|
|
37
|
+
fee += (venueServiceCharge.serviceFeeCents || 0) / 100;
|
|
38
|
+
}
|
|
39
|
+
if (chargeType === 'percent' || chargeType === 'both') {
|
|
40
|
+
fee += ((venueServiceCharge.serviceFeePercentage || 0) / 100) * price;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Apply max cap if set (convert from cents to dollars for comparison)
|
|
44
|
+
const maxFee = venueServiceCharge.maxServiceFeeCents || 0;
|
|
45
|
+
if (maxFee > 0 && fee > maxFee / 100) {
|
|
46
|
+
fee = maxFee / 100;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return fee;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const dispatch = createEventDispatcher();
|
|
53
|
+
|
|
54
|
+
let showFooter = false;
|
|
55
|
+
let subtotal = 0;
|
|
56
|
+
let fees = 0;
|
|
57
|
+
let taxes = 0;
|
|
58
|
+
let total = 0;
|
|
59
|
+
let showOrderSummaryOnMobile = false;
|
|
60
|
+
|
|
61
|
+
function makeOrderSummaryVisible() {
|
|
62
|
+
showOrderSummaryOnMobile = !showOrderSummaryOnMobile;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Calculate discounted price for a ticket
|
|
66
|
+
$: getDiscountedPrice = (ticket) => {
|
|
67
|
+
if (!currentPromoRule?.provideDiscount) return null;
|
|
68
|
+
|
|
69
|
+
// Check if this discount applies to this specific ticket
|
|
70
|
+
// If discountTicketIds is empty or not provided, discount applies to all tickets
|
|
71
|
+
// If discountTicketIds has values, only apply to those specific ticket IDs
|
|
72
|
+
const discountTicketIds = currentPromoRule.discountTicketIds || [];
|
|
73
|
+
if (discountTicketIds.length > 0 && !discountTicketIds.includes(ticket.ID)) {
|
|
74
|
+
return null; // This ticket is not eligible for the discount
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const basePrice = parseFloat(ticket.price) || 0;
|
|
78
|
+
const discountAmount = parseFloat(currentPromoRule?.amount || '0') || 0;
|
|
79
|
+
if (currentPromoRule.discountType === '%') {
|
|
80
|
+
return (basePrice * (1 - discountAmount / 100)).toFixed(2);
|
|
81
|
+
} else if (currentPromoRule.discountType === '$') {
|
|
82
|
+
return Math.max(0, basePrice - discountAmount).toFixed(2);
|
|
83
|
+
}
|
|
84
|
+
return null;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
$: showFooter = totalQuantity > 0;
|
|
88
|
+
|
|
89
|
+
// Calculate subtotal without any discounts (for calculating savings)
|
|
90
|
+
$: subtotalWithoutDiscount = Object.keys(quantities).reduce((acc, key) => {
|
|
91
|
+
const ticket = eventTickets.find(t => t.ID == key);
|
|
92
|
+
if (!ticket) return acc;
|
|
93
|
+
return acc + quantities[key] * ticket.price;
|
|
94
|
+
}, 0);
|
|
95
|
+
|
|
96
|
+
// Calculate subtotal with discounts applied
|
|
97
|
+
$: subtotal = Object.keys(quantities).reduce((acc, key) => {
|
|
98
|
+
const ticket = eventTickets.find(t => t.ID == key);
|
|
99
|
+
if (!ticket) return acc;
|
|
100
|
+
const discountedPrice = getDiscountedPrice(ticket);
|
|
101
|
+
const priceToUse = discountedPrice !== null ? parseFloat(discountedPrice) : ticket.price;
|
|
102
|
+
return acc + quantities[key] * priceToUse;
|
|
103
|
+
}, 0);
|
|
104
|
+
|
|
105
|
+
// Calculate actual savings from promo code
|
|
106
|
+
$: promoSavings = currentPromoRule?.provideDiscount ? (subtotalWithoutDiscount - subtotal) : 0;
|
|
107
|
+
|
|
108
|
+
$: fees = Object.keys(quantities).reduce((acc, key) => {
|
|
109
|
+
const ticket = eventTickets.find(t => t.ID == key);
|
|
110
|
+
if (!ticket || ticket.price == 0) return acc;
|
|
111
|
+
const discountedPrice = getDiscountedPrice(ticket);
|
|
112
|
+
const priceToUse = discountedPrice !== null ? parseFloat(discountedPrice) : ticket.price;
|
|
113
|
+
return acc + quantities[key] * feeFor(priceToUse);
|
|
114
|
+
}, 0);
|
|
115
|
+
$: taxRate = (venueServiceCharge.taxPercentage || 0) / 100;
|
|
116
|
+
$: taxes = subtotal > 0 ? subtotal * taxRate : 0;
|
|
117
|
+
// When currentPromoRule is used, discounts are already applied per-ticket in subtotal calculation
|
|
118
|
+
// Only subtract promoDiscount separately when NOT using currentPromoRule (legacy behavior)
|
|
119
|
+
// Use Math.max(0, ...) to prevent negative totals in edge cases
|
|
120
|
+
$: total = Math.max(0, subtotal + fees + taxes - (promoApplied && !currentPromoRule?.provideDiscount ? promoDiscount : 0));
|
|
121
|
+
$: dispatch('priceUpdate', { subtotal, fees, taxes, total, promoSavings });
|
|
122
|
+
$: totalQuantity = Object.values(quantities).reduce((sum, q) => sum + q, 0);
|
|
123
|
+
</script>
|
|
124
|
+
|
|
125
|
+
<div
|
|
126
|
+
id="orderSummary"
|
|
127
|
+
class="order-summary desktop-only h-fit rounded-lg"
|
|
128
|
+
>
|
|
129
|
+
<div class="summary-header px-5 py-4">
|
|
130
|
+
<h2 class="text-lg font-semibold text-primary">
|
|
131
|
+
Order summary
|
|
132
|
+
</h2>
|
|
133
|
+
</div>
|
|
134
|
+
|
|
135
|
+
<div class="px-5 pb-5">
|
|
136
|
+
{#if totalQuantity === 0}
|
|
137
|
+
<!-- Empty state -->
|
|
138
|
+
<div class="py-8 text-center">
|
|
139
|
+
<p class="text-tertiary text-sm">Select tickets to continue</p>
|
|
140
|
+
</div>
|
|
141
|
+
{:else}
|
|
142
|
+
<!-- Selected tickets - always show original prices -->
|
|
143
|
+
{#each Object.keys(quantities) as key}
|
|
144
|
+
{#if quantities[key] > 0}
|
|
145
|
+
{#each eventTickets as ticket}
|
|
146
|
+
{#if ticket.ID == key}
|
|
147
|
+
<div class="ticket-line flex justify-between py-3">
|
|
148
|
+
<div>
|
|
149
|
+
<p class="font-medium text-primary">{ticket.name}</p>
|
|
150
|
+
<p class="text-sm text-tertiary">
|
|
151
|
+
{#if ticket.price === 0}
|
|
152
|
+
Free × {quantities[key]}
|
|
153
|
+
{:else}
|
|
154
|
+
${ticket.price.toFixed(2)} × {quantities[key]}
|
|
155
|
+
{/if}
|
|
156
|
+
</p>
|
|
157
|
+
</div>
|
|
158
|
+
<div class="font-medium text-primary">
|
|
159
|
+
{ticket.price === 0
|
|
160
|
+
? 'Free'
|
|
161
|
+
: `$${(ticket.price * quantities[key]).toFixed(2)}`}
|
|
162
|
+
</div>
|
|
163
|
+
</div>
|
|
164
|
+
{/if}
|
|
165
|
+
{/each}
|
|
166
|
+
{/if}
|
|
167
|
+
{/each}
|
|
168
|
+
|
|
169
|
+
<!-- Price breakdown -->
|
|
170
|
+
<div class="flex flex-col py-3 gap-2 text-sm">
|
|
171
|
+
{#if promoSavings > 0 || (promoDiscount > 0 && !currentPromoRule?.provideDiscount)}
|
|
172
|
+
<div class="flex justify-between text-secondary">
|
|
173
|
+
<span>Full Price</span><span>${subtotalWithoutDiscount.toFixed(2)}</span>
|
|
174
|
+
</div>
|
|
175
|
+
{/if}
|
|
176
|
+
{#if promoSavings > 0}
|
|
177
|
+
<div class="flex justify-between promo-discount-line">
|
|
178
|
+
<span>Discount</span><span>-${promoSavings.toFixed(2)}</span>
|
|
179
|
+
</div>
|
|
180
|
+
{:else if promoDiscount > 0 && !currentPromoRule?.provideDiscount}
|
|
181
|
+
<div class="flex justify-between promo-discount-line">
|
|
182
|
+
<span>Discount</span><span>-${promoDiscount.toFixed(2)}</span>
|
|
183
|
+
</div>
|
|
184
|
+
{/if}
|
|
185
|
+
<div class="flex justify-between text-secondary">
|
|
186
|
+
<span>Subtotal</span><span>${subtotal.toFixed(2)}</span>
|
|
187
|
+
</div>
|
|
188
|
+
<div class="flex justify-between text-secondary">
|
|
189
|
+
<span>Fees</span><span>${fees.toFixed(2)}</span>
|
|
190
|
+
</div>
|
|
191
|
+
<div class="flex justify-between text-secondary">
|
|
192
|
+
<span>Taxes</span><span>${taxes.toFixed(2)}</span>
|
|
193
|
+
</div>
|
|
194
|
+
</div>
|
|
195
|
+
|
|
196
|
+
<!-- Total -->
|
|
197
|
+
<div class="total-line flex justify-between font-semibold text-primary py-4 text-lg">
|
|
198
|
+
<span>Total</span><span>${total.toFixed(2)}</span>
|
|
199
|
+
</div>
|
|
200
|
+
{/if}
|
|
201
|
+
|
|
202
|
+
<!-- Terms of service -->
|
|
203
|
+
{#if totalQuantity > 0 && btnText === 'Place order'}
|
|
204
|
+
<p class="terms-text">
|
|
205
|
+
By selecting Place order, I agree to the <a href="https://get-micdrop.com/tos" class="terms-link" target="_blank" rel="noopener noreferrer">terms of service</a>
|
|
206
|
+
</p>
|
|
207
|
+
{/if}
|
|
208
|
+
|
|
209
|
+
<!-- Checkout button -->
|
|
210
|
+
<button
|
|
211
|
+
class="checkout-btn w-full h-12 font-semibold rounded-lg flex items-center justify-center transition-colors"
|
|
212
|
+
class:disabled={totalQuantity === 0}
|
|
213
|
+
on:click={() => {
|
|
214
|
+
if (totalQuantity === 0) return;
|
|
215
|
+
if (executePurchase) {
|
|
216
|
+
executePurchase(elements);
|
|
217
|
+
} else if (checkoutTicket) {
|
|
218
|
+
checkoutTicket();
|
|
219
|
+
}
|
|
220
|
+
}}
|
|
221
|
+
disabled={totalQuantity === 0}
|
|
222
|
+
>
|
|
223
|
+
{#if loading}
|
|
224
|
+
<Spinner size="sm" color="white" />
|
|
225
|
+
{:else}
|
|
226
|
+
{btnText}
|
|
227
|
+
{/if}
|
|
228
|
+
</button>
|
|
229
|
+
</div>
|
|
230
|
+
</div>
|
|
231
|
+
|
|
232
|
+
{#if showOrderSummaryOnMobile}
|
|
233
|
+
<!-- Backdrop overlay - dims background -->
|
|
234
|
+
<button
|
|
235
|
+
class="mobile-backdrop mobile-only"
|
|
236
|
+
on:click={() => (showOrderSummaryOnMobile = false)}
|
|
237
|
+
aria-label="Close order summary"
|
|
238
|
+
transition:fade={{ duration: 200 }}
|
|
239
|
+
></button>
|
|
240
|
+
<!-- Drawer - stays bright -->
|
|
241
|
+
<div
|
|
242
|
+
in:fly={{ y: 800, duration: 300, easing: cubicOut }}
|
|
243
|
+
out:fly={{ y: 800, duration: 300, easing: cubicOut }}
|
|
244
|
+
class="mobile-summary mobile-only fixed bottom-0 left-0 w-full overflow-x-hidden overflow-y-auto z-[99]"
|
|
245
|
+
>
|
|
246
|
+
<div class="mobile-summary-header flex flex-row justify-between items-center px-5 py-4">
|
|
247
|
+
<h2 class="text-xl font-semibold text-primary">
|
|
248
|
+
Order summary
|
|
249
|
+
</h2>
|
|
250
|
+
<button
|
|
251
|
+
on:click={() => (showOrderSummaryOnMobile = false)}
|
|
252
|
+
class="close-btn transition-colors p-2 rounded-lg"
|
|
253
|
+
aria-label="Close order summary"
|
|
254
|
+
>
|
|
255
|
+
<Close size={28} />
|
|
256
|
+
</button>
|
|
257
|
+
</div>
|
|
258
|
+
|
|
259
|
+
<div class="px-5 pb-5">
|
|
260
|
+
<!-- Mobile ticket list - always show original prices -->
|
|
261
|
+
{#each Object.keys(quantities) as key}
|
|
262
|
+
{#if quantities[key] > 0}
|
|
263
|
+
{#each eventTickets as ticket}
|
|
264
|
+
{#if ticket.ID == key}
|
|
265
|
+
<div class="mobile-ticket-line flex justify-between py-4">
|
|
266
|
+
<div>
|
|
267
|
+
<p class="font-semibold text-primary">{ticket.name}</p>
|
|
268
|
+
<p class="text-sm text-tertiary">
|
|
269
|
+
{#if ticket.price === 0}
|
|
270
|
+
Free × {quantities[key]}
|
|
271
|
+
{:else}
|
|
272
|
+
${ticket.price.toFixed(2)} × {quantities[key]}
|
|
273
|
+
{/if}
|
|
274
|
+
</p>
|
|
275
|
+
</div>
|
|
276
|
+
<div class="font-semibold text-primary">
|
|
277
|
+
{ticket.price === 0
|
|
278
|
+
? 'Free'
|
|
279
|
+
: `$${(ticket.price * quantities[key]).toFixed(2)}`}
|
|
280
|
+
</div>
|
|
281
|
+
</div>
|
|
282
|
+
{/if}
|
|
283
|
+
{/each}
|
|
284
|
+
{/if}
|
|
285
|
+
{/each}
|
|
286
|
+
|
|
287
|
+
<div class="flex flex-col py-4 gap-3 text-secondary">
|
|
288
|
+
{#if promoSavings > 0 || (promoDiscount > 0 && !currentPromoRule?.provideDiscount)}
|
|
289
|
+
<div class="flex justify-between">
|
|
290
|
+
<span>Full Price</span><span>${subtotalWithoutDiscount.toFixed(2)}</span>
|
|
291
|
+
</div>
|
|
292
|
+
{/if}
|
|
293
|
+
{#if promoSavings > 0}
|
|
294
|
+
<div class="flex justify-between promo-discount-line">
|
|
295
|
+
<span>Discount</span><span>-${promoSavings.toFixed(2)}</span>
|
|
296
|
+
</div>
|
|
297
|
+
{:else if promoDiscount > 0 && !currentPromoRule?.provideDiscount}
|
|
298
|
+
<div class="flex justify-between promo-discount-line">
|
|
299
|
+
<span>Discount</span><span>-${promoDiscount.toFixed(2)}</span>
|
|
300
|
+
</div>
|
|
301
|
+
{/if}
|
|
302
|
+
<div class="flex justify-between">
|
|
303
|
+
<span>Subtotal</span><span>${subtotal.toFixed(2)}</span>
|
|
304
|
+
</div>
|
|
305
|
+
<div class="flex justify-between">
|
|
306
|
+
<span>Fees</span><span>${fees.toFixed(2)}</span>
|
|
307
|
+
</div>
|
|
308
|
+
<div class="flex justify-between">
|
|
309
|
+
<span>Taxes</span><span>${taxes.toFixed(2)}</span>
|
|
310
|
+
</div>
|
|
311
|
+
</div>
|
|
312
|
+
|
|
313
|
+
<div class="mobile-total flex justify-between font-bold text-primary py-5">
|
|
314
|
+
<span>Total</span><span>${total.toFixed(2)}</span>
|
|
315
|
+
</div>
|
|
316
|
+
</div>
|
|
317
|
+
</div>
|
|
318
|
+
{/if}
|
|
319
|
+
|
|
320
|
+
{#if showFooter}
|
|
321
|
+
<div
|
|
322
|
+
transition:fly={{ y: 100, duration: 200 }}
|
|
323
|
+
class="mobile-footer mobile-only fixed bottom-0 left-0 right-0 z-[500]"
|
|
324
|
+
>
|
|
325
|
+
<div class="mobile-footer-content">
|
|
326
|
+
<button class="mobile-footer-left" on:click={makeOrderSummaryVisible}>
|
|
327
|
+
<span class="mobile-footer-tickets">
|
|
328
|
+
{totalQuantity} {totalQuantity > 1 ? 'tickets' : 'ticket'}
|
|
329
|
+
<ChevronDown size={16} class="chevron-icon transition-transform duration-200 {showOrderSummaryOnMobile ? 'rotate-180' : ''}" />
|
|
330
|
+
</span>
|
|
331
|
+
<span class="mobile-footer-price">${total.toFixed(2)}</span>
|
|
332
|
+
</button>
|
|
333
|
+
|
|
334
|
+
<button
|
|
335
|
+
class="checkout-btn mobile-footer-btn"
|
|
336
|
+
on:click={() => {
|
|
337
|
+
if (executePurchase) {
|
|
338
|
+
executePurchase(elements);
|
|
339
|
+
} else if (checkoutTicket) {
|
|
340
|
+
checkoutTicket();
|
|
341
|
+
}
|
|
342
|
+
}}
|
|
343
|
+
disabled={totalQuantity === 0 || !isAgreed}
|
|
344
|
+
>
|
|
345
|
+
{#if loading}
|
|
346
|
+
<Spinner size="sm" color="white" />
|
|
347
|
+
{:else}
|
|
348
|
+
Checkout
|
|
349
|
+
{/if}
|
|
350
|
+
</button>
|
|
351
|
+
</div>
|
|
352
|
+
</div>
|
|
353
|
+
{/if}
|
|
354
|
+
|
|
355
|
+
<style>
|
|
356
|
+
/* Custom breakpoint at 872px for desktop/mobile switch */
|
|
357
|
+
.desktop-only {
|
|
358
|
+
display: none;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
.mobile-only {
|
|
362
|
+
display: block;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
@media (min-width: 872px) {
|
|
366
|
+
.desktop-only {
|
|
367
|
+
display: block;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
.mobile-only {
|
|
371
|
+
display: none !important;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/* Theme-responsive colors using CSS variables */
|
|
376
|
+
.order-summary {
|
|
377
|
+
background-color: hsl(var(--BG-Primary));
|
|
378
|
+
border: 1px solid hsl(var(--Stroke-Secondary));
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
.summary-header {
|
|
382
|
+
border-bottom: 1px solid hsl(var(--Stroke-Secondary));
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
.text-primary {
|
|
386
|
+
color: hsl(var(--Text-Primary));
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
.text-secondary {
|
|
390
|
+
color: hsl(var(--Text-Secondary));
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
.text-tertiary {
|
|
394
|
+
color: hsl(var(--Text-Tartiary));
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
.ticket-line {
|
|
398
|
+
border-bottom: 1px solid hsl(var(--Stroke-Secondary) / 0.5);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
.promo-discount-line {
|
|
402
|
+
color: hsl(var(--Accent-Success));
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
.total-line {
|
|
406
|
+
border-top: 1px solid hsl(var(--Stroke-Secondary));
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
.terms-text {
|
|
410
|
+
font-size: 12px;
|
|
411
|
+
color: hsl(var(--Text-Tartiary));
|
|
412
|
+
text-align: center;
|
|
413
|
+
margin: 0 0 12px 0;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
.terms-link {
|
|
417
|
+
color: hsl(var(--Brand-Primary));
|
|
418
|
+
text-decoration: underline;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
.terms-link:hover {
|
|
422
|
+
opacity: 0.8;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
.checkout-btn {
|
|
426
|
+
background-color: hsl(var(--Brand-Primary));
|
|
427
|
+
color: white;
|
|
428
|
+
-moz-user-select: none;
|
|
429
|
+
user-select: none;
|
|
430
|
+
-webkit-user-select: none;
|
|
431
|
+
touch-action: manipulation;
|
|
432
|
+
-webkit-tap-highlight-color: transparent;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
.checkout-btn:hover:not(:disabled) {
|
|
436
|
+
background-color: hsl(var(--Brand-Primary) / 0.9);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
.checkout-btn.disabled,
|
|
440
|
+
.checkout-btn:disabled {
|
|
441
|
+
background-color: hsl(var(--BG-Quaternary));
|
|
442
|
+
color: hsl(var(--Text-Tartiary));
|
|
443
|
+
cursor: not-allowed;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/* Mobile backdrop - dims background behind drawer */
|
|
447
|
+
.mobile-backdrop {
|
|
448
|
+
position: fixed;
|
|
449
|
+
inset: 0;
|
|
450
|
+
background-color: rgba(0, 0, 0, 0.5);
|
|
451
|
+
z-index: 98;
|
|
452
|
+
border: none;
|
|
453
|
+
cursor: pointer;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/* Mobile summary drawer - stays bright above backdrop */
|
|
457
|
+
.mobile-summary {
|
|
458
|
+
background-color: hsl(var(--BG-Primary));
|
|
459
|
+
max-height: 80vh;
|
|
460
|
+
border-radius: 16px 16px 0 0;
|
|
461
|
+
box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.15);
|
|
462
|
+
z-index: 99;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
.mobile-summary-header {
|
|
466
|
+
border-bottom: 1px solid hsl(var(--Stroke-Secondary));
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
.close-btn {
|
|
470
|
+
color: hsl(var(--Text-Tartiary));
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
.close-btn:hover {
|
|
474
|
+
color: hsl(var(--Text-Primary));
|
|
475
|
+
background-color: hsl(var(--BG-Secondary));
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
.mobile-ticket-line {
|
|
479
|
+
border-bottom: 1px solid hsl(var(--Stroke-Secondary));
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
.mobile-total {
|
|
483
|
+
border-top: 1px solid hsl(var(--Stroke-Secondary));
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/* Mobile footer */
|
|
487
|
+
.mobile-footer {
|
|
488
|
+
background-color: hsl(var(--BG-Primary));
|
|
489
|
+
border-top: 1px solid hsl(var(--Stroke-Secondary));
|
|
490
|
+
box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.1);
|
|
491
|
+
padding: 16px;
|
|
492
|
+
padding-bottom: max(16px, calc(env(safe-area-inset-bottom) + 8px));
|
|
493
|
+
-moz-user-select: none;
|
|
494
|
+
user-select: none;
|
|
495
|
+
-webkit-user-select: none;
|
|
496
|
+
touch-action: manipulation;
|
|
497
|
+
-webkit-tap-highlight-color: transparent;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
.mobile-footer-content {
|
|
501
|
+
display: flex;
|
|
502
|
+
align-items: stretch;
|
|
503
|
+
gap: 12px;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
.mobile-footer-left {
|
|
507
|
+
display: flex;
|
|
508
|
+
flex-direction: column;
|
|
509
|
+
justify-content: space-between;
|
|
510
|
+
align-items: flex-start;
|
|
511
|
+
flex-shrink: 0;
|
|
512
|
+
white-space: nowrap;
|
|
513
|
+
background: none;
|
|
514
|
+
border: none;
|
|
515
|
+
padding: 0;
|
|
516
|
+
cursor: pointer;
|
|
517
|
+
touch-action: manipulation;
|
|
518
|
+
-webkit-tap-highlight-color: transparent;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
.mobile-footer-tickets {
|
|
522
|
+
display: flex;
|
|
523
|
+
align-items: center;
|
|
524
|
+
gap: 4px;
|
|
525
|
+
font-size: 14px;
|
|
526
|
+
font-weight: 400;
|
|
527
|
+
color: hsl(var(--Text-Secondary));
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
.mobile-footer-price {
|
|
531
|
+
font-size: 20px;
|
|
532
|
+
font-weight: 700;
|
|
533
|
+
color: hsl(var(--Text-Primary));
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
.mobile-footer-btn {
|
|
537
|
+
flex: 1;
|
|
538
|
+
min-width: 140px;
|
|
539
|
+
height: 48px;
|
|
540
|
+
border-radius: 10px;
|
|
541
|
+
font-size: 14px;
|
|
542
|
+
font-weight: 600;
|
|
543
|
+
touch-action: manipulation;
|
|
544
|
+
-webkit-tap-highlight-color: transparent;
|
|
545
|
+
display: flex;
|
|
546
|
+
align-items: center;
|
|
547
|
+
justify-content: center;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
:global(.chevron-icon) {
|
|
551
|
+
color: hsl(var(--Text-Tartiary));
|
|
552
|
+
}
|
|
553
|
+
</style>
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
export default OrderSummary;
|
|
2
|
+
type OrderSummary = SvelteComponent<{
|
|
3
|
+
loading?: boolean | undefined;
|
|
4
|
+
quantities?: {} | undefined;
|
|
5
|
+
eventTickets?: any[] | undefined;
|
|
6
|
+
checkoutTicket?: null | undefined;
|
|
7
|
+
isAgreed?: boolean | undefined;
|
|
8
|
+
btnText?: string | undefined;
|
|
9
|
+
promoApplied?: boolean | undefined;
|
|
10
|
+
promoDiscount?: number | undefined;
|
|
11
|
+
currentPromoRule?: null | undefined;
|
|
12
|
+
executePurchase?: null | undefined;
|
|
13
|
+
elements?: null | undefined;
|
|
14
|
+
venueServiceCharge?: {
|
|
15
|
+
serviceFeeCents: number;
|
|
16
|
+
serviceFeePercentage: number;
|
|
17
|
+
serviceFeeChargeType: string;
|
|
18
|
+
maxServiceFeeCents: number;
|
|
19
|
+
taxPercentage: number;
|
|
20
|
+
} | undefined;
|
|
21
|
+
}, {
|
|
22
|
+
priceUpdate: CustomEvent<any>;
|
|
23
|
+
} & {
|
|
24
|
+
[evt: string]: CustomEvent<any>;
|
|
25
|
+
}, {}> & {
|
|
26
|
+
$$bindings?: string | undefined;
|
|
27
|
+
};
|
|
28
|
+
declare const OrderSummary: $$__sveltets_2_IsomorphicComponent<{
|
|
29
|
+
loading?: boolean | undefined;
|
|
30
|
+
quantities?: {} | undefined;
|
|
31
|
+
eventTickets?: any[] | undefined;
|
|
32
|
+
checkoutTicket?: null | undefined;
|
|
33
|
+
isAgreed?: boolean | undefined;
|
|
34
|
+
btnText?: string | undefined;
|
|
35
|
+
promoApplied?: boolean | undefined;
|
|
36
|
+
promoDiscount?: number | undefined;
|
|
37
|
+
currentPromoRule?: null | undefined;
|
|
38
|
+
executePurchase?: null | undefined;
|
|
39
|
+
elements?: null | undefined;
|
|
40
|
+
venueServiceCharge?: {
|
|
41
|
+
serviceFeeCents: number;
|
|
42
|
+
serviceFeePercentage: number;
|
|
43
|
+
serviceFeeChargeType: string;
|
|
44
|
+
maxServiceFeeCents: number;
|
|
45
|
+
taxPercentage: number;
|
|
46
|
+
} | undefined;
|
|
47
|
+
}, {
|
|
48
|
+
priceUpdate: CustomEvent<any>;
|
|
49
|
+
} & {
|
|
50
|
+
[evt: string]: CustomEvent<any>;
|
|
51
|
+
}, {}, {}, string>;
|
|
52
|
+
interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
|
|
53
|
+
new (options: import("svelte").ComponentConstructorOptions<Props>): import("svelte").SvelteComponent<Props, Events, Slots> & {
|
|
54
|
+
$$bindings?: Bindings;
|
|
55
|
+
} & Exports;
|
|
56
|
+
(internal: unknown, props: Props & {
|
|
57
|
+
$$events?: Events;
|
|
58
|
+
$$slots?: Slots;
|
|
59
|
+
}): Exports & {
|
|
60
|
+
$set?: any;
|
|
61
|
+
$on?: any;
|
|
62
|
+
};
|
|
63
|
+
z_$$bindings?: Bindings;
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=OrderSummary.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OrderSummary.svelte.d.ts","sourceRoot":"","sources":["../../../src/lib/components/OrderSummary/OrderSummary.svelte.js"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAuVA;;;;;;;;;;;;;;;;;;;;;;;mBAA2S;6CAT9P,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,QAAQ,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,QAAQ,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,QAAQ,OAAO,OAAO,QAAQ;IAC3L,cAAc,OAAO,QAAQ,EAAE,2BAA2B,CAAC,KAAK,CAAC,GAAG,OAAO,QAAQ,EAAE,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,GAAG;QAAE,UAAU,CAAC,EAAE,QAAQ,CAAA;KAAE,GAAG,OAAO,CAAC;IACjK,WAAW,OAAO,SAAS,KAAK,GAAG;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,KAAK,CAAA;KAAC,GAAG,OAAO,GAAG;QAAE,IAAI,CAAC,EAAE,GAAG,CAAC;QAAC,GAAG,CAAC,EAAE,GAAG,CAAA;KAAE,CAAC;IAC9G,eAAe,QAAQ,CAAC"}
|