@code-collective/booking-widget 1.0.3 → 1.0.4

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/src/lib/config.ts CHANGED
@@ -4,14 +4,78 @@ export type CartOverviewDisplay = 'bar' | 'button';
4
4
 
5
5
  export type WizardWidgetType = 'option' | 'age-category' | 'date' | 'time' | 'pickup' | 'addon';
6
6
 
7
- export type WizardPages = WizardWidgetType[][];
7
+ export interface WizardPageConfig {
8
+ title: string;
9
+ widgets: WizardWidgetType[];
10
+ }
11
+
12
+ export type WizardPages = WizardPageConfig[];
8
13
 
9
14
  export const DEFAULT_WIZARD_PAGES: WizardPages = [
10
- ['option', 'age-category'],
11
- ['date', 'time'],
12
- ['pickup'],
15
+ { title: 'Option', widgets: ['option', 'age-category'] },
16
+ { title: 'Schedule', widgets: ['date', 'time'] },
17
+ { title: 'Pickup', widgets: ['pickup'] },
13
18
  ];
14
19
 
20
+ const WIZARD_PRESETS: Record<string, WizardPages> = {
21
+ 'option-first': DEFAULT_WIZARD_PAGES,
22
+ 'date-first': [
23
+ { title: 'Age & capacity', widgets: ['age-category'] },
24
+ { title: 'Schedule', widgets: ['date', 'time'] },
25
+ { title: 'Option', widgets: ['option'] },
26
+ { title: 'Pickup', widgets: ['pickup'] },
27
+ ],
28
+ };
29
+
30
+ // Accepts a preset name ("option-first", "date-first") or a JSON array of { title, widgets } objects.
31
+ export function resolveWizardPages(value: string | null | undefined): WizardPages {
32
+ if (!value) return DEFAULT_WIZARD_PAGES;
33
+ if (value in WIZARD_PRESETS) return WIZARD_PRESETS[value];
34
+ try {
35
+ const parsed = JSON.parse(value);
36
+ if (Array.isArray(parsed) && parsed.length > 0 && parsed[0].widgets) return parsed;
37
+ } catch { /* use default */ }
38
+ return DEFAULT_WIZARD_PAGES;
39
+ }
40
+
41
+ // Grouping for the checkout edit-item accordion - deliberately different from the step wizard's own page
42
+ // breakdown, since a step-by-step flow and a "review everything at once" accordion don't need the same
43
+ // shape. Age & capacity, date and time always live together in one section here, regardless of which
44
+ // preset is in play - only where that section falls (first vs. second) changes with option-first vs.
45
+ // date-first.
46
+ const EDIT_PRESETS: Record<string, WizardPages> = {
47
+ 'option-first': [
48
+ { title: 'Option', widgets: ['option'] },
49
+ { title: 'Age, Date & Time', widgets: ['age-category', 'date', 'time'] },
50
+ { title: 'Pickup', widgets: ['pickup'] },
51
+ ],
52
+ 'date-first': [
53
+ { title: 'Age, Date & Time', widgets: ['age-category', 'date', 'time'] },
54
+ { title: 'Option', widgets: ['option'] },
55
+ { title: 'Pickup', widgets: ['pickup'] },
56
+ ],
57
+ };
58
+
59
+ // Resolves the grouping for the checkout edit-item accordion. If editValue is set, it's resolved the same
60
+ // way as wizardPages (a preset name or a custom JSON array). Otherwise, defaults to the grouped layout for
61
+ // whichever named wizard preset wizardValue resolves to (including the "no value given" case, which is
62
+ // option-first) - but if wizardValue is itself a custom, unnamed page layout, there's no sensible grouping
63
+ // to infer, so editPages just mirrors it directly.
64
+ export function resolveEditPages(wizardValue: string | null | undefined, editValue: string | null | undefined): WizardPages {
65
+ if (editValue) {
66
+ if (editValue in EDIT_PRESETS) return EDIT_PRESETS[editValue];
67
+ try {
68
+ const parsed = JSON.parse(editValue);
69
+ if (Array.isArray(parsed) && parsed.length > 0 && parsed[0].widgets) return parsed;
70
+ } catch { /* fall through to the wizardPages-derived default */ }
71
+ }
72
+
73
+ const wizardKey = wizardValue ? (wizardValue in WIZARD_PRESETS ? wizardValue : null) : 'option-first';
74
+ if (wizardKey) return EDIT_PRESETS[wizardKey];
75
+
76
+ return resolveWizardPages(wizardValue);
77
+ }
78
+
15
79
  export interface AppConfig {
16
80
  apiBaseUrl: string;
17
81
  productId: string;
@@ -22,6 +86,7 @@ export interface AppConfig {
22
86
  cartDisplay: CartOverviewDisplay;
23
87
  orderId: string;
24
88
  wizardPages: WizardPages;
89
+ editPages: WizardPages;
25
90
  autoSelectSingleTimeSlot: boolean;
26
91
  name: string;
27
92
  cancelable: boolean;
@@ -42,7 +107,8 @@ export function readConfig(): AppConfig {
42
107
  mode: parseMode(params.get('mode')),
43
108
  cartDisplay: parseCartDisplay(params.get('cartDisplay')),
44
109
  orderId: params.get('orderId') ?? '',
45
- wizardPages: parseWizardPages(params.get('wizard')),
110
+ wizardPages: resolveWizardPages(params.get('wizard')),
111
+ editPages: resolveEditPages(params.get('wizard'), params.get('edit')),
46
112
  autoSelectSingleTimeSlot: params.get('autoSelectSingleTimeSlot') === 'true',
47
113
  name: params.get('name') ?? '',
48
114
  cancelable: params.get('cancelable') === 'true',
@@ -59,6 +125,7 @@ export function readConfig(): AppConfig {
59
125
  cartDisplay: 'bar',
60
126
  orderId: '',
61
127
  wizardPages: DEFAULT_WIZARD_PAGES,
128
+ editPages: EDIT_PRESETS['option-first'],
62
129
  autoSelectSingleTimeSlot: false,
63
130
  name: '',
64
131
  cancelable: false,
@@ -81,12 +148,3 @@ function deriveCheckoutKey(productId: string): string {
81
148
  const first = productId[0]?.toLowerCase() ?? '';
82
149
  return first >= 'a' && first <= 'j' ? 'morii' : 'csbs';
83
150
  }
84
-
85
- function parseWizardPages(value: string | null): WizardPages {
86
- if (!value) return DEFAULT_WIZARD_PAGES;
87
- try {
88
- const parsed = JSON.parse(value);
89
- if (Array.isArray(parsed) && parsed.every(Array.isArray)) return parsed;
90
- } catch { /* use default */ }
91
- return DEFAULT_WIZARD_PAGES;
92
- }
@@ -2,14 +2,18 @@
2
2
 
3
3
  <script lang="ts">
4
4
  import Checkout from '../Checkout.svelte';
5
- import { DEFAULT_WIZARD_PAGES } from '../config';
5
+ import { resolveWizardPages, resolveEditPages } from '../config';
6
6
  import { getSharedServices } from './shared';
7
7
 
8
8
  let {
9
9
  'wizard-pages': wizardPagesJson = '',
10
+ 'edit-pages': editPagesJson = '',
10
11
  'is-modal': isModalAttr = undefined,
12
+ 'auto-select-single-time-slot': autoSelectSingleTimeSlotAttr = undefined,
11
13
  } = $props();
12
14
 
15
+ let autoSelectSingleTimeSlot = $derived(autoSelectSingleTimeSlotAttr !== undefined);
16
+
13
17
  let isModal = $derived(isModalAttr !== undefined);
14
18
  let showModal = $state(false);
15
19
 
@@ -18,7 +22,8 @@
18
22
  let ready = $state(false);
19
23
  readyPromise.then(() => { ready = true; });
20
24
 
21
- let wizardPages = $derived(wizardPagesJson ? JSON.parse(wizardPagesJson) : DEFAULT_WIZARD_PAGES);
25
+ let wizardPages = $derived(resolveWizardPages(wizardPagesJson));
26
+ let editPages = $derived(resolveEditPages(wizardPagesJson, editPagesJson));
22
27
 
23
28
  function dispatch(name: string, detail: unknown) {
24
29
  const host = (document.querySelector('bw-checkout') as HTMLElement) ?? document.body;
@@ -69,12 +74,12 @@
69
74
  <div class="bw-modal-overlay" onclick={() => close()}>
70
75
  <!-- svelte-ignore a11y_click_events_have_key_events a11y_no_static_element_interactions -->
71
76
  <div class="bw-modal-inner" onclick={(e) => e.stopPropagation()}>
72
- <Checkout {api} {wizardPages} />
77
+ <Checkout {api} {wizardPages} {editPages} {autoSelectSingleTimeSlot} />
73
78
  </div>
74
79
  </div>
75
80
  {/if}
76
81
  {:else if ready}
77
- <Checkout {api} {wizardPages} />
82
+ <Checkout {api} {wizardPages} {editPages} {autoSelectSingleTimeSlot} />
78
83
  {:else}
79
84
  <div class="loading-center"><div class="spinner"></div></div>
80
85
  {/if}
@@ -2,7 +2,7 @@
2
2
 
3
3
  <script lang="ts">
4
4
  import TicketConfigurator from '../TicketConfigurator.svelte';
5
- import { DEFAULT_WIZARD_PAGES } from '../config';
5
+ import { resolveWizardPages } from '../config';
6
6
  import { postMessage } from '../messages';
7
7
  import { getSharedServices } from './shared';
8
8
 
@@ -18,7 +18,7 @@
18
18
  let ready = $state(false);
19
19
  readyPromise.then(() => { ready = true; });
20
20
 
21
- let wizardPages = $derived(wizardPagesJson ? JSON.parse(wizardPagesJson) : DEFAULT_WIZARD_PAGES);
21
+ let wizardPages = $derived(resolveWizardPages(wizardPagesJson));
22
22
 
23
23
  function dispatch(name: string, detail: unknown) {
24
24
  const host = (document.querySelector('bw-configurator') as HTMLElement) ?? document.body;
@@ -26,12 +26,14 @@ interface BwOptions {
26
26
  shouldBottomCloseOnModal: boolean;
27
27
  autoSelectSingleTimeSlot: boolean;
28
28
  wizardPages: string;
29
+ editPages: string;
29
30
  }
30
31
 
31
32
  const defaultOptions: BwOptions = {
32
33
  shouldBottomCloseOnModal: true,
33
34
  autoSelectSingleTimeSlot: false,
34
35
  wizardPages: '',
36
+ editPages: '',
35
37
  };
36
38
 
37
39
  const options: BwOptions = {
@@ -61,7 +63,16 @@ function autoWire() {
61
63
  });
62
64
  }
63
65
 
64
- configurators.forEach((el) => {
66
+ // edit-pages only affects the checkout edit-item accordion, so it's only meaningful on bw-checkout.
67
+ if (options.editPages) {
68
+ checkouts.forEach((el) => {
69
+ if (!el.hasAttribute('edit-pages')) {
70
+ el.setAttribute('edit-pages', options.editPages);
71
+ }
72
+ });
73
+ }
74
+
75
+ [...configurators, ...checkouts].forEach((el) => {
65
76
  if (options.autoSelectSingleTimeSlot && !el.hasAttribute('auto-select-single-time-slot')) {
66
77
  el.setAttribute('auto-select-single-time-slot', '');
67
78
  }
package/src/lib/index.ts CHANGED
@@ -44,6 +44,8 @@ export interface ConfiguratorConfig extends BaseConfig {
44
44
 
45
45
  export interface CheckoutConfig extends BaseConfig {
46
46
  wizardPages?: WizardPages;
47
+ editPages?: WizardPages;
48
+ autoSelectSingleTimeSlot?: boolean;
47
49
  onClose?: () => void;
48
50
  onOrderConfirmed?: (detail: { cartToken: string; value: number; currency: string }) => void;
49
51
  }
@@ -126,6 +128,8 @@ export async function mountCheckout(target: HTMLElement, config: CheckoutConfig)
126
128
  props: {
127
129
  api,
128
130
  wizardPages: config.wizardPages,
131
+ editPages: config.editPages,
132
+ autoSelectSingleTimeSlot: config.autoSelectSingleTimeSlot ?? false,
129
133
  },
130
134
  });
131
135