@code-collective/booking-widget 1.0.4 → 1.0.6
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/README.md +38 -3
- package/dist/booking-widget.css +1 -1
- package/dist/booking-widget.js +330 -226
- package/dist/booking-widget.min.css +1 -1
- package/dist/booking-widget.min.js +9 -5
- package/dist/booking-widget.umd.cjs +2 -2
- package/package.json +1 -1
- package/src/lib/CartBar.svelte +14 -0
- package/src/lib/CartBarView.svelte +1 -1
- package/src/lib/CartOverviewButton.svelte +15 -1
- package/src/lib/Checkout.svelte +38 -28
- package/src/lib/CheckoutModal.svelte +10 -0
- package/src/lib/TicketConfigurator.svelte +31 -16
- package/src/lib/api.ts +2 -1
- package/src/lib/app.css +12 -4
- package/src/lib/cart-manager.ts +1 -0
- package/src/lib/fake-api.ts +1 -1
- package/src/lib/index.ts +2 -1
package/src/lib/Checkout.svelte
CHANGED
|
@@ -30,31 +30,41 @@
|
|
|
30
30
|
load();
|
|
31
31
|
</script>
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
<div class="
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
33
|
+
<div class="bw-widget">
|
|
34
|
+
{#if isLoading}
|
|
35
|
+
<div class="loading-center" style="height:100vh">
|
|
36
|
+
<div class="spinner"></div>
|
|
37
|
+
</div>
|
|
38
|
+
{:else if cart && cart.items.length > 0}
|
|
39
|
+
<CheckoutModal
|
|
40
|
+
{cart}
|
|
41
|
+
{api}
|
|
42
|
+
{wizardPages}
|
|
43
|
+
{editPages}
|
|
44
|
+
{autoSelectSingleTimeSlot}
|
|
45
|
+
onClose={() => postMessage({ type: 'modal:close' })}
|
|
46
|
+
onOrderConfirmed={() => {
|
|
47
|
+
const total = cart!.items.reduce((s, i) => s + i.amount, 0);
|
|
48
|
+
const currency = cart!.items[0]?.currencyCode ?? 'ZAR';
|
|
49
|
+
postMessage({
|
|
50
|
+
type: 'order:complete',
|
|
51
|
+
cartToken: cart!.cartToken,
|
|
52
|
+
value: total,
|
|
53
|
+
currency,
|
|
54
|
+
});
|
|
55
|
+
}}
|
|
56
|
+
/>
|
|
57
|
+
{:else}
|
|
58
|
+
<div class="loading-center" style="height:100vh;color:var(--bw-color-text-secondary)">
|
|
59
|
+
No items in cart.
|
|
60
|
+
</div>
|
|
61
|
+
{/if}
|
|
62
|
+
</div>
|
|
63
|
+
|
|
64
|
+
<style>
|
|
65
|
+
/* display: contents - a plain box here would break CheckoutModal's own .modal{height:100%}, which needs
|
|
66
|
+
to resolve against this component's real parent, not an unsized wrapper inserted in between. */
|
|
67
|
+
.bw-widget {
|
|
68
|
+
display: contents;
|
|
69
|
+
}
|
|
70
|
+
</style>
|
|
@@ -126,15 +126,25 @@
|
|
|
126
126
|
const updated = await api.getCart();
|
|
127
127
|
cart = updated;
|
|
128
128
|
currentView = 'cart';
|
|
129
|
+
notifyCartUpdated();
|
|
129
130
|
}
|
|
130
131
|
|
|
131
132
|
async function removeItem(item: CheckoutCartItemDetailDto) {
|
|
132
133
|
await api.removeCartItem(item.id);
|
|
133
134
|
const updated = await api.getCart();
|
|
134
135
|
cart = updated;
|
|
136
|
+
notifyCartUpdated();
|
|
135
137
|
if (cart.items.length === 0) close();
|
|
136
138
|
}
|
|
137
139
|
|
|
140
|
+
// A standalone postMessage - not the itemCount/cartItemId-shaped cart:change TicketConfigurator posts on
|
|
141
|
+
// add (which bw-configurator.svelte re-dispatches as the public bw:cart-change event and auto-opens
|
|
142
|
+
// checkout for). This is purely internal: CartBar/CartOverviewButton listen for it to refetch their own
|
|
143
|
+
// total after an edit or remove, which otherwise only ever fetch once on mount.
|
|
144
|
+
function notifyCartUpdated(): void {
|
|
145
|
+
postMessage({ type: 'cart:updated' });
|
|
146
|
+
}
|
|
147
|
+
|
|
138
148
|
async function onPayNow() {
|
|
139
149
|
hasAttemptedSubmit = true;
|
|
140
150
|
if (!contactForm?.isValid() || !privacyAccepted) return;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { BookingApi } from './api';
|
|
3
3
|
import type { WizardPages } from './config';
|
|
4
|
+
import { DEFAULT_WIZARD_PAGES } from './config';
|
|
4
5
|
import type { CartItem, CheckoutProductDto } from './client-types';
|
|
5
6
|
import { CartManager } from './cart-manager';
|
|
6
7
|
import { postMessage } from './messages';
|
|
@@ -12,11 +13,11 @@
|
|
|
12
13
|
api: BookingApi;
|
|
13
14
|
cartManager: CartManager;
|
|
14
15
|
productId: string;
|
|
15
|
-
wizardPages
|
|
16
|
+
wizardPages?: WizardPages;
|
|
16
17
|
autoSelectSingleTimeSlot?: boolean;
|
|
17
18
|
onCancel?: () => void;
|
|
18
19
|
}
|
|
19
|
-
let { api, cartManager, productId, wizardPages, autoSelectSingleTimeSlot = false, onCancel }: Props = $props();
|
|
20
|
+
let { api, cartManager, productId, wizardPages = DEFAULT_WIZARD_PAGES, autoSelectSingleTimeSlot = false, onCancel }: Props = $props();
|
|
20
21
|
|
|
21
22
|
let product = $state<CheckoutProductDto | null>(null);
|
|
22
23
|
let isLoading = $state(true);
|
|
@@ -56,23 +57,37 @@
|
|
|
56
57
|
totalFormatted: total,
|
|
57
58
|
openCheckout: true,
|
|
58
59
|
});
|
|
60
|
+
// Separate from cart:change above - that one is per-item-added detail forwarded to consumers as the
|
|
61
|
+
// public bw:cart-change event. This one just tells CartBar/CartOverviewButton to refetch their own
|
|
62
|
+
// total, same signal update/remove in CheckoutModal use - see this event's own doc comment there.
|
|
63
|
+
postMessage({ type: 'cart:updated' });
|
|
59
64
|
} finally {
|
|
60
65
|
isAddingToCart = false;
|
|
61
66
|
}
|
|
62
67
|
}
|
|
63
68
|
</script>
|
|
64
69
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
<div class="
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
70
|
+
<div class="bw-widget">
|
|
71
|
+
{#if isLoading || isAddingToCart}
|
|
72
|
+
<div class="loading-center" style="height:100vh">
|
|
73
|
+
<div class="spinner"></div>
|
|
74
|
+
</div>
|
|
75
|
+
{:else if product}
|
|
76
|
+
<WizardPage
|
|
77
|
+
{product}
|
|
78
|
+
{api}
|
|
79
|
+
{wizardPages}
|
|
80
|
+
{autoSelectSingleTimeSlot}
|
|
81
|
+
{onCancel}
|
|
82
|
+
onComplete={onAddToCart}
|
|
83
|
+
/>
|
|
84
|
+
{/if}
|
|
85
|
+
</div>
|
|
86
|
+
|
|
87
|
+
<style>
|
|
88
|
+
/* display: contents - a plain box here would break WizardPage's own .wizard{height:100%}, which needs
|
|
89
|
+
to resolve against this component's real parent, not an unsized wrapper inserted in between. */
|
|
90
|
+
.bw-widget {
|
|
91
|
+
display: contents;
|
|
92
|
+
}
|
|
93
|
+
</style>
|
package/src/lib/api.ts
CHANGED
|
@@ -17,6 +17,7 @@ import { dateKey } from './utils';
|
|
|
17
17
|
|
|
18
18
|
export interface BookingApi {
|
|
19
19
|
sessionToken: string;
|
|
20
|
+
cartToken: string;
|
|
20
21
|
|
|
21
22
|
// Session
|
|
22
23
|
startSession(checkoutKey: string): Promise<CheckoutSessionDto>;
|
|
@@ -46,7 +47,7 @@ export interface BookingApi {
|
|
|
46
47
|
|
|
47
48
|
export class ApiClient implements BookingApi {
|
|
48
49
|
sessionToken = '';
|
|
49
|
-
|
|
50
|
+
cartToken = '';
|
|
50
51
|
private client;
|
|
51
52
|
|
|
52
53
|
constructor(baseUrl: string) {
|
package/src/lib/app.css
CHANGED
|
@@ -26,13 +26,17 @@
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
/* ── Reset ─────────────────────────────────────────────── */
|
|
29
|
-
|
|
29
|
+
/* Scoped to .bw-widget (present on every mountable component's own root - TicketConfigurator, Checkout,
|
|
30
|
+
CartBar, CartOverviewButton) rather than the bare selectors this used to be, since Option 2/3 consumers
|
|
31
|
+
mount these directly into their own app alongside their own global styles/resets (Tailwind preflight,
|
|
32
|
+
etc.) - an unscoped * or body rule here would leak onto their entire page, not just the widget. */
|
|
33
|
+
.bw-widget, .bw-widget * {
|
|
30
34
|
margin: 0;
|
|
31
35
|
padding: 0;
|
|
32
36
|
box-sizing: border-box;
|
|
33
37
|
}
|
|
34
38
|
|
|
35
|
-
|
|
39
|
+
.bw-widget {
|
|
36
40
|
font-family: var(--bw-font-family);
|
|
37
41
|
font-size: 14px;
|
|
38
42
|
line-height: 1.5;
|
|
@@ -41,8 +45,12 @@ body {
|
|
|
41
45
|
-webkit-font-smoothing: antialiased;
|
|
42
46
|
}
|
|
43
47
|
|
|
44
|
-
button
|
|
45
|
-
|
|
48
|
+
/* button/input/etc form controls don't inherit font-family by default even from an ancestor that sets it
|
|
49
|
+
(a standard browser quirk, not specific to this reset) - covers both a bw-widget div wrapping a button
|
|
50
|
+
(TicketConfigurator, Checkout) and bw-widget being set directly on the control itself (CartOverviewButton's
|
|
51
|
+
own <button>), since a plain descendant selector alone only matches the first case. */
|
|
52
|
+
.bw-widget button, button.bw-widget { cursor: pointer; font-family: inherit; }
|
|
53
|
+
.bw-widget :is(input, select, textarea), :is(input, select, textarea).bw-widget { font-family: inherit; }
|
|
46
54
|
|
|
47
55
|
/* ── Spinner ───────────────────────────────────────────── */
|
|
48
56
|
@keyframes spin { to { transform: rotate(360deg); } }
|
package/src/lib/cart-manager.ts
CHANGED
package/src/lib/fake-api.ts
CHANGED
package/src/lib/index.ts
CHANGED
|
@@ -19,8 +19,9 @@ export { default as CartOverviewButton } from './CartOverviewButton.svelte';
|
|
|
19
19
|
|
|
20
20
|
// Re-export utilities
|
|
21
21
|
export { ApiClient, FakeApiClient, SessionManager, CartManager };
|
|
22
|
+
export { resolveWizardPages, resolveEditPages, DEFAULT_WIZARD_PAGES } from './config';
|
|
22
23
|
export type { BookingApi } from './api';
|
|
23
|
-
export type { WizardPages, CartOverviewDisplay, WidgetMode, WizardWidgetType } from './config';
|
|
24
|
+
export type { WizardPages, WizardPageConfig, CartOverviewDisplay, WidgetMode, WizardWidgetType } from './config';
|
|
24
25
|
export type { components, operations, paths } from './generated-types';
|
|
25
26
|
export type * from './client-types';
|
|
26
27
|
|