@ticketlayer/elements 0.2.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/LICENSE +21 -0
- package/README.md +55 -0
- package/dist/cjs/index-DfzcrE9-.js +3598 -0
- package/dist/cjs/index.cjs.js +16 -0
- package/dist/cjs/loader.cjs.js +12 -0
- package/dist/cjs/order-summary-ZMkJ5bez.js +116 -0
- package/dist/cjs/sdk-connector-ClDF3013.js +108 -0
- package/dist/cjs/ticketlayer-elements.cjs.js +24 -0
- package/dist/cjs/tl-buy-modal.cjs.entry.js +127 -0
- package/dist/cjs/tl-buy-tickets-button.cjs.entry.js +131 -0
- package/dist/cjs/tl-buy-tickets-wrapper.cjs.entry.js +58 -0
- package/dist/cjs/tl-cart-badge.cjs.entry.js +109 -0
- package/dist/cjs/tl-cart-drawer.cjs.entry.js +67 -0
- package/dist/cjs/tl-cart.cjs.entry.js +148 -0
- package/dist/cjs/tl-checkout-wrapper.cjs.entry.js +57 -0
- package/dist/cjs/tl-event-card.cjs.entry.js +140 -0
- package/dist/cjs/tl-event-list.cjs.entry.js +80 -0
- package/dist/cjs/tl-login.cjs.entry.js +184 -0
- package/dist/cjs/tl-my-orders.cjs.entry.js +123 -0
- package/dist/cjs/tl-occurrence-selector_2.cjs.entry.js +754 -0
- package/dist/cjs/tl-order-confirmation.cjs.entry.js +146 -0
- package/dist/cjs/tl-order-tickets_2.cjs.entry.js +3198 -0
- package/dist/cjs/tl-promo-code.cjs.entry.js +75 -0
- package/dist/cjs/tl-seat-map.cjs.entry.js +147 -0
- package/dist/collection/collection-manifest.json +29 -0
- package/dist/collection/components/tl-buy-modal/tl-buy-modal.css +264 -0
- package/dist/collection/components/tl-buy-modal/tl-buy-modal.js +249 -0
- package/dist/collection/components/tl-buy-tickets-button/tl-buy-tickets-button.css +120 -0
- package/dist/collection/components/tl-buy-tickets-button/tl-buy-tickets-button.js +412 -0
- package/dist/collection/components/tl-buy-tickets-wrapper/tl-buy-tickets-wrapper.css +23 -0
- package/dist/collection/components/tl-buy-tickets-wrapper/tl-buy-tickets-wrapper.js +192 -0
- package/dist/collection/components/tl-cart/tl-cart.css +251 -0
- package/dist/collection/components/tl-cart/tl-cart.js +314 -0
- package/dist/collection/components/tl-cart-badge/tl-cart-badge.css +138 -0
- package/dist/collection/components/tl-cart-badge/tl-cart-badge.js +217 -0
- package/dist/collection/components/tl-cart-drawer/tl-cart-drawer.css +98 -0
- package/dist/collection/components/tl-cart-drawer/tl-cart-drawer.js +185 -0
- package/dist/collection/components/tl-checkout-wrapper/tl-checkout-wrapper.css +23 -0
- package/dist/collection/components/tl-checkout-wrapper/tl-checkout-wrapper.js +148 -0
- package/dist/collection/components/tl-event-card/event-card-adapter.js +23 -0
- package/dist/collection/components/tl-event-card/tl-event-card.css +175 -0
- package/dist/collection/components/tl-event-card/tl-event-card.js +205 -0
- package/dist/collection/components/tl-event-list/tl-event-list.css +134 -0
- package/dist/collection/components/tl-event-list/tl-event-list.js +171 -0
- package/dist/collection/components/tl-login/tl-login.css +133 -0
- package/dist/collection/components/tl-login/tl-login.js +309 -0
- package/dist/collection/components/tl-my-orders/tl-my-orders.css +204 -0
- package/dist/collection/components/tl-my-orders/tl-my-orders.js +221 -0
- package/dist/collection/components/tl-occurrence-selector/tl-occurrence-selector.css +658 -0
- package/dist/collection/components/tl-occurrence-selector/tl-occurrence-selector.js +707 -0
- package/dist/collection/components/tl-order-confirmation/tl-order-confirmation.css +176 -0
- package/dist/collection/components/tl-order-confirmation/tl-order-confirmation.js +298 -0
- package/dist/collection/components/tl-order-tickets/tl-order-tickets.css +30 -0
- package/dist/collection/components/tl-order-tickets/tl-order-tickets.js +122 -0
- package/dist/collection/components/tl-promo-code/tl-promo-code.css +106 -0
- package/dist/collection/components/tl-promo-code/tl-promo-code.js +181 -0
- package/dist/collection/components/tl-seat-map/tl-seat-map.css +85 -0
- package/dist/collection/components/tl-seat-map/tl-seat-map.js +366 -0
- package/dist/collection/components/tl-ticket/tl-ticket.css +90 -0
- package/dist/collection/components/tl-ticket/tl-ticket.js +129 -0
- package/dist/collection/components/tl-ticket-selector/tl-ticket-selector.css +205 -0
- package/dist/collection/components/tl-ticket-selector/tl-ticket-selector.js +332 -0
- package/dist/collection/index.js +10 -0
- package/dist/collection/utils/order-summary.js +101 -0
- package/dist/collection/utils/sdk-connector.js +100 -0
- package/dist/collection/utils/testing/mock-sdk.js +95 -0
- package/dist/components/index.d.ts +35 -0
- package/dist/components/index.js +1 -0
- package/dist/components/order-summary.js +1 -0
- package/dist/components/sdk-connector.js +1 -0
- package/dist/components/tl-buy-modal.d.ts +11 -0
- package/dist/components/tl-buy-modal.js +1 -0
- package/dist/components/tl-buy-tickets-button.d.ts +11 -0
- package/dist/components/tl-buy-tickets-button.js +1 -0
- package/dist/components/tl-buy-tickets-wrapper.d.ts +11 -0
- package/dist/components/tl-buy-tickets-wrapper.js +1 -0
- package/dist/components/tl-buy-tickets-wrapper2.js +1 -0
- package/dist/components/tl-cart-badge.d.ts +11 -0
- package/dist/components/tl-cart-badge.js +1 -0
- package/dist/components/tl-cart-drawer.d.ts +11 -0
- package/dist/components/tl-cart-drawer.js +1 -0
- package/dist/components/tl-cart.d.ts +11 -0
- package/dist/components/tl-cart.js +1 -0
- package/dist/components/tl-cart2.js +1 -0
- package/dist/components/tl-checkout-wrapper.d.ts +11 -0
- package/dist/components/tl-checkout-wrapper.js +1 -0
- package/dist/components/tl-checkout-wrapper2.js +1 -0
- package/dist/components/tl-event-card.d.ts +11 -0
- package/dist/components/tl-event-card.js +1 -0
- package/dist/components/tl-event-card2.js +1 -0
- package/dist/components/tl-event-list.d.ts +11 -0
- package/dist/components/tl-event-list.js +1 -0
- package/dist/components/tl-login.d.ts +11 -0
- package/dist/components/tl-login.js +1 -0
- package/dist/components/tl-login2.js +1 -0
- package/dist/components/tl-my-orders.d.ts +11 -0
- package/dist/components/tl-my-orders.js +1 -0
- package/dist/components/tl-occurrence-selector.d.ts +11 -0
- package/dist/components/tl-occurrence-selector.js +1 -0
- package/dist/components/tl-occurrence-selector2.js +1 -0
- package/dist/components/tl-order-confirmation.d.ts +11 -0
- package/dist/components/tl-order-confirmation.js +1 -0
- package/dist/components/tl-order-tickets.d.ts +11 -0
- package/dist/components/tl-order-tickets.js +1 -0
- package/dist/components/tl-order-tickets2.js +1 -0
- package/dist/components/tl-promo-code.d.ts +11 -0
- package/dist/components/tl-promo-code.js +1 -0
- package/dist/components/tl-seat-map.d.ts +11 -0
- package/dist/components/tl-seat-map.js +1 -0
- package/dist/components/tl-ticket-selector.d.ts +11 -0
- package/dist/components/tl-ticket-selector.js +1 -0
- package/dist/components/tl-ticket-selector2.js +1 -0
- package/dist/components/tl-ticket.d.ts +11 -0
- package/dist/components/tl-ticket.js +1 -0
- package/dist/components/tl-ticket2.js +1 -0
- package/dist/elements.docs.d.ts +454 -0
- package/dist/elements.docs.json +3008 -0
- package/dist/esm/index-Ba-RZTFI.js +3588 -0
- package/dist/esm/index.js +4 -0
- package/dist/esm/loader.js +10 -0
- package/dist/esm/order-summary-BGekdjN9.js +103 -0
- package/dist/esm/sdk-connector-CqhvfyKV.js +102 -0
- package/dist/esm/ticketlayer-elements.js +20 -0
- package/dist/esm/tl-buy-modal.entry.js +125 -0
- package/dist/esm/tl-buy-tickets-button.entry.js +129 -0
- package/dist/esm/tl-buy-tickets-wrapper.entry.js +56 -0
- package/dist/esm/tl-cart-badge.entry.js +107 -0
- package/dist/esm/tl-cart-drawer.entry.js +65 -0
- package/dist/esm/tl-cart.entry.js +146 -0
- package/dist/esm/tl-checkout-wrapper.entry.js +55 -0
- package/dist/esm/tl-event-card.entry.js +138 -0
- package/dist/esm/tl-event-list.entry.js +78 -0
- package/dist/esm/tl-login.entry.js +182 -0
- package/dist/esm/tl-my-orders.entry.js +121 -0
- package/dist/esm/tl-occurrence-selector_2.entry.js +751 -0
- package/dist/esm/tl-order-confirmation.entry.js +144 -0
- package/dist/esm/tl-order-tickets_2.entry.js +3195 -0
- package/dist/esm/tl-promo-code.entry.js +73 -0
- package/dist/esm/tl-seat-map.entry.js +145 -0
- package/dist/index.cjs.js +1 -0
- package/dist/index.js +1 -0
- package/dist/ticketlayer-elements/index.esm.js +1 -0
- package/dist/ticketlayer-elements/p-0291df4c.entry.js +1 -0
- package/dist/ticketlayer-elements/p-10f01a1f.entry.js +1 -0
- package/dist/ticketlayer-elements/p-11273f8f.entry.js +1 -0
- package/dist/ticketlayer-elements/p-340ab4f5.entry.js +1 -0
- package/dist/ticketlayer-elements/p-41d22d7c.entry.js +1 -0
- package/dist/ticketlayer-elements/p-82752e3b.entry.js +1 -0
- package/dist/ticketlayer-elements/p-976770f3.entry.js +1 -0
- package/dist/ticketlayer-elements/p-9a903ff6.entry.js +1 -0
- package/dist/ticketlayer-elements/p-9c9072c8.entry.js +1 -0
- package/dist/ticketlayer-elements/p-9d8163d6.entry.js +1 -0
- package/dist/ticketlayer-elements/p-BGekdjN9.js +1 -0
- package/dist/ticketlayer-elements/p-Ba-RZTFI.js +2 -0
- package/dist/ticketlayer-elements/p-CqhvfyKV.js +1 -0
- package/dist/ticketlayer-elements/p-a5c90b68.entry.js +1 -0
- package/dist/ticketlayer-elements/p-b6d479ad.entry.js +1 -0
- package/dist/ticketlayer-elements/p-dd43f015.entry.js +1 -0
- package/dist/ticketlayer-elements/p-eb640760.entry.js +1 -0
- package/dist/ticketlayer-elements/p-ee3aa227.entry.js +1 -0
- package/dist/ticketlayer-elements/p-fb9444c4.entry.js +1 -0
- package/dist/ticketlayer-elements/ticketlayer-elements.css +1 -0
- package/dist/ticketlayer-elements/ticketlayer-elements.esm.js +1 -0
- package/dist/types/components/tl-buy-modal/tl-buy-modal.d.ts +54 -0
- package/dist/types/components/tl-buy-tickets-button/tl-buy-tickets-button.d.ts +61 -0
- package/dist/types/components/tl-buy-tickets-wrapper/tl-buy-tickets-wrapper.d.ts +52 -0
- package/dist/types/components/tl-cart/tl-cart.d.ts +80 -0
- package/dist/types/components/tl-cart-badge/tl-cart-badge.d.ts +36 -0
- package/dist/types/components/tl-cart-drawer/tl-cart-drawer.d.ts +35 -0
- package/dist/types/components/tl-checkout-wrapper/tl-checkout-wrapper.d.ts +39 -0
- package/dist/types/components/tl-event-card/event-card-adapter.d.ts +23 -0
- package/dist/types/components/tl-event-card/tl-event-card.d.ts +55 -0
- package/dist/types/components/tl-event-list/tl-event-list.d.ts +30 -0
- package/dist/types/components/tl-login/tl-login.d.ts +59 -0
- package/dist/types/components/tl-my-orders/tl-my-orders.d.ts +39 -0
- package/dist/types/components/tl-occurrence-selector/tl-occurrence-selector.d.ts +208 -0
- package/dist/types/components/tl-order-confirmation/tl-order-confirmation.d.ts +54 -0
- package/dist/types/components/tl-order-tickets/tl-order-tickets.d.ts +21 -0
- package/dist/types/components/tl-promo-code/tl-promo-code.d.ts +39 -0
- package/dist/types/components/tl-seat-map/tl-seat-map.d.ts +68 -0
- package/dist/types/components/tl-ticket/tl-ticket.d.ts +28 -0
- package/dist/types/components/tl-ticket-selector/tl-ticket-selector.d.ts +78 -0
- package/dist/types/components.d.ts +1629 -0
- package/dist/types/index.d.ts +14 -0
- package/dist/types/stencil-public-runtime.d.ts +1839 -0
- package/dist/types/utils/order-summary.d.ts +67 -0
- package/dist/types/utils/sdk-connector.d.ts +113 -0
- package/dist/types/utils/testing/mock-sdk.d.ts +36 -0
- package/elements.catalogue.json +968 -0
- package/loader/cdn.js +1 -0
- package/loader/index.cjs.js +1 -0
- package/loader/index.d.ts +24 -0
- package/loader/index.es2017.js +1 -0
- package/loader/index.js +2 -0
- package/package.json +101 -0
|
@@ -0,0 +1,754 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var index = require('./index-DfzcrE9-.js');
|
|
4
|
+
var sdkConnector = require('./sdk-connector-ClDF3013.js');
|
|
5
|
+
|
|
6
|
+
const tlOccurrenceSelectorCss = () => `:host{--tl-radius:var(--tl-radius-md, 0.5rem);display:block;font-family:var(--tl-font-family, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif);color:var(--tl-foreground, #0f172a)}*{box-sizing:border-box;margin:0;padding:0}.occurrence-selector{width:100%}.no-occurrences{text-align:center;padding:3rem 1rem;color:var(--tl-muted-foreground, #64748b)}.list-view{display:flex;flex-direction:column;gap:0.75rem}.occurrence-item{display:flex;justify-content:space-between;align-items:flex-start;width:100%;padding:1rem 1.25rem;background:var(--tl-background, #ffffff);border:1px solid var(--tl-border, #e2e8f0);border-radius:var(--tl-radius);text-align:left;cursor:pointer;transition:all 0.15s ease;font-family:inherit}.occurrence-item:hover:not(:disabled){border-color:var(--tl-primary, #e64b12);background:color-mix(in srgb, var(--tl-primary, #e64b12) 5%, var(--tl-background, #ffffff))}.occurrence-item:focus-visible{outline:2px solid var(--tl-primary);outline-offset:2px}.occurrence-item:disabled{cursor:not-allowed;opacity:0.6;background:var(--tl-muted, #f1f5f9)}.occurrence-item.sold-out{opacity:0.6;background:var(--tl-muted, #f1f5f9)}.occurrence-info{flex:1}.occurrence-date{font-weight:600;font-size:0.9375rem;color:var(--tl-foreground, #0f172a);margin-bottom:0.25rem}.occurrence-time{font-size:0.875rem;color:var(--tl-muted-foreground)}.occurrence-status{flex-shrink:0;text-align:right}.status-sold-out{font-size:0.875rem;font-weight:500;color:var(--tl-muted-foreground)}.status-available{font-size:0.875rem;color:var(--tl-muted-foreground)}.status-arrow{font-size:1.25rem;color:var(--tl-primary, #e64b12)}.slots-view{display:flex;flex-direction:column;gap:1rem}.slots-date-header{font-size:1.125rem;font-weight:600;color:var(--tl-foreground, #0f172a);padding-bottom:0.5rem;border-bottom:1px solid var(--tl-border)}.time-slots-grid{display:grid;grid-template-columns:repeat(auto-fill, minmax(100px, 1fr));gap:0.5rem}.time-slot{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:0.75rem 0.5rem;background:var(--tl-background, #ffffff);border:1px solid var(--tl-border, #e2e8f0);border-radius:var(--tl-radius);cursor:pointer;transition:all 0.15s ease;font-family:inherit;min-height:3.5rem}.time-slot:hover:not(:disabled){border-color:var(--tl-primary, #e64b12);background:color-mix(in srgb, var(--tl-primary, #e64b12) 5%, var(--tl-background, #ffffff))}.time-slot:focus-visible{outline:2px solid var(--tl-primary);outline-offset:2px}.time-slot:disabled{cursor:not-allowed;opacity:0.6;background:var(--tl-muted, #f1f5f9)}.time-slot.sold-out{opacity:0.6;background:var(--tl-muted, #f1f5f9)}.slot-time{font-size:0.9375rem;font-weight:600;color:var(--tl-foreground, #0f172a)}.slot-sold-out{font-size:0.6875rem;color:var(--tl-muted-foreground);margin-top:0.125rem}.calendar-view{display:flex;flex-direction:column;gap:1.5rem}.calendar-view.horizontal{flex-direction:row;gap:1.5rem;align-items:flex-start}.calendar-view.horizontal .calendar-panel{flex-shrink:0;width:340px}.calendar-view.horizontal .slots-panel{flex:1;min-width:0}.calendar-view.vertical .calendar-panel{width:100%}.calendar-view.vertical .slots-panel{width:100%}.calendar-panel{border:1px solid var(--tl-border, #e2e8f0);border-radius:var(--tl-radius);padding:1rem}.calendar-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:0.75rem}.month-label{font-size:1rem;font-weight:600;color:var(--tl-foreground, #0f172a);flex:1}.calendar-nav-buttons{display:flex;gap:0.375rem}.nav-button{display:flex;align-items:center;justify-content:center;width:1.75rem;height:1.75rem;padding:0;background:var(--tl-background, #ffffff);border:1px solid var(--tl-border, #e2e8f0);border-radius:var(--tl-radius);cursor:pointer;transition:all 0.15s ease;color:var(--tl-foreground, #0f172a)}.nav-button:hover:not(:disabled){background:var(--tl-muted, #f1f5f9)}.nav-button:disabled{cursor:not-allowed;opacity:0.4}.nav-button svg{width:0.875rem;height:0.875rem}.calendar-grid{display:grid;grid-template-columns:repeat(7, 1fr);gap:0.1875rem}.weekday-header{text-align:center;font-size:0.6875rem;font-weight:600;color:var(--tl-muted-foreground);padding:0.375rem 0;text-transform:uppercase;letter-spacing:0.05em}.calendar-day{position:relative;display:flex;flex-direction:column;align-items:center;justify-content:center;aspect-ratio:1;padding:0.1875rem;background:var(--tl-background, #ffffff);border:1px solid transparent;border-radius:var(--tl-radius);cursor:default;transition:all 0.15s ease;font-family:inherit;font-size:0.9375rem;font-weight:500;color:var(--tl-foreground, #0f172a)}.calendar-day.other-month{color:var(--tl-muted-foreground);opacity:0.5}.calendar-day.has-occurrences{cursor:pointer;background:color-mix(in srgb, var(--tl-primary, #e64b12) 10%, var(--tl-background, #ffffff));border-color:transparent;font-weight:600;color:var(--tl-primary, #e64b12)}.calendar-day.has-occurrences:hover{background:color-mix(in srgb, var(--tl-primary, #e64b12) 18%, var(--tl-background, #ffffff));border-color:transparent}.calendar-day.has-occurrences.sold-out{background:var(--tl-muted, #f1f5f9);border-color:transparent}.calendar-day.has-occurrences.sold-out:hover{background:color-mix(in srgb, var(--tl-muted, #f1f5f9) 80%, var(--tl-background, #ffffff));border-color:transparent}.calendar-day.selected{background:var(--tl-primary, #e64b12);border-color:var(--tl-primary, #e64b12);color:var(--tl-primary-foreground, #ffffff)}.calendar-day.selected:hover{background:color-mix(in srgb, var(--tl-primary, #e64b12) 90%, black)}.calendar-day.selected .occurrence-dot{background:var(--tl-primary-foreground)}.calendar-day.today .day-number{font-weight:700;text-decoration:underline;text-underline-offset:2px}.calendar-day.past:not(.has-occurrences){opacity:0.4}.day-number{line-height:1}.occurrence-dot{display:none}.slots-panel{min-height:200px}.selected-date-slots{height:100%}.selected-date-slots.empty{display:flex;align-items:center;justify-content:center;color:var(--tl-muted-foreground);background:var(--tl-muted, #f1f5f9);border-radius:var(--tl-radius);padding:2rem 1rem;text-align:center;min-height:200px;font-size:0.875rem}.selected-date-header{font-size:0.9375rem;font-weight:600;color:var(--tl-foreground, #0f172a);padding-bottom:0.625rem;margin-bottom:0.625rem}.time-slots-list{display:flex;flex-direction:column;gap:0.5rem}.time-slot-item{display:flex;align-items:center;justify-content:space-between;width:100%;padding:0.625rem 0.875rem;background:var(--tl-background, #ffffff);border:1px solid var(--tl-border, #e2e8f0);border-radius:var(--tl-radius);text-align:left;cursor:pointer;transition:all 0.15s ease;font-family:inherit}.time-slot-item:hover:not(:disabled){border-color:var(--tl-primary, #e64b12);background:color-mix(in srgb, var(--tl-primary, #e64b12) 5%, var(--tl-background, #ffffff))}.time-slot-item:focus-visible{outline:2px solid var(--tl-primary);outline-offset:2px}.time-slot-item:disabled{cursor:not-allowed;opacity:0.6;background:var(--tl-muted, #f1f5f9)}.time-slot-item.sold-out{opacity:0.6;background:var(--tl-muted, #f1f5f9)}.time-slot-item .slot-time{font-size:0.9375rem;font-weight:600}.slot-status{flex-shrink:0}.slot-status .status-sold-out{font-size:0.75rem}.slot-status .status-available{font-size:0.75rem}.slot-status .status-arrow{font-size:1rem}@media (max-width: 640px){.event-name{font-size:1.25rem}.occurrence-date{font-size:0.875rem}.time-slots-grid{grid-template-columns:repeat(auto-fill, minmax(80px, 1fr))}.time-slot{padding:0.625rem 0.375rem;min-height:3rem}.slot-time{font-size:0.875rem}.calendar-day{font-size:0.75rem}.weekday-header{font-size:0.625rem}.calendar-view.horizontal .calendar-panel{width:100%}}@media (max-width: 767px){.calendar-view.horizontal{flex-direction:column}}.loading-container{padding:1rem 0;min-height:400px}@keyframes skeleton-pulse{0%,100%{opacity:1}50%{opacity:0.5}}.skeleton{background:var(--tl-muted, #f1f5f9);border-radius:var(--tl-radius);animation:skeleton-pulse 1.5s ease-in-out infinite}.skeleton-list{display:flex;flex-direction:column;gap:0.75rem}.skeleton-list-item{height:4.5rem;border-radius:var(--tl-radius)}.skeleton-calendar{border:1px solid var(--tl-border, #e2e8f0);border-radius:var(--tl-radius);padding:1rem;min-height:350px}.skeleton-calendar-header{height:1.75rem;width:50%;margin-bottom:0.75rem;border-radius:var(--tl-radius)}.skeleton-calendar-grid{display:grid;grid-template-columns:repeat(7, 1fr);gap:0.1875rem;margin-top:0.75rem}.skeleton-calendar-day{aspect-ratio:1;border-radius:var(--tl-radius)}.skeleton-slots{display:flex;flex-direction:column;gap:1rem}.skeleton-slots-header{height:1.75rem;width:60%;border-radius:var(--tl-radius);margin-bottom:0.5rem}.skeleton-slots-grid{display:grid;grid-template-columns:repeat(auto-fill, minmax(100px, 1fr));gap:0.5rem}.skeleton-slot-item{height:3.5rem;border-radius:var(--tl-radius)}.error-container{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:3rem 1rem;gap:1rem;text-align:center}.error-icon{width:3rem;height:3rem;color:var(--tl-muted-foreground);stroke-width:1.5}.error-message{color:var(--tl-foreground, #0f172a);font-size:0.9375rem;max-width:400px}.retry-button{margin-top:0.5rem;padding:0.625rem 1.25rem;background:var(--tl-primary, #e64b12);color:var(--tl-primary-foreground, #ffffff);border:none;border-radius:var(--tl-radius);font-family:inherit;font-size:0.875rem;font-weight:500;cursor:pointer;transition:all 0.15s ease}.retry-button:hover{background:color-mix(in srgb, var(--tl-primary, #e64b12) 90%, black)}.retry-button:focus-visible{outline:2px solid var(--tl-primary);outline-offset:2px}`;
|
|
7
|
+
|
|
8
|
+
const TlOccurrenceSelector = class {
|
|
9
|
+
constructor(hostRef) {
|
|
10
|
+
index.registerInstance(this, hostRef);
|
|
11
|
+
this.tlSelect = index.createEvent(this, "tlSelect");
|
|
12
|
+
/**
|
|
13
|
+
* Layout orientation (auto = horizontal on desktop, vertical on mobile)
|
|
14
|
+
*/
|
|
15
|
+
this.orientation = 'auto';
|
|
16
|
+
/**
|
|
17
|
+
* Loading state
|
|
18
|
+
*/
|
|
19
|
+
this.loading = true;
|
|
20
|
+
/**
|
|
21
|
+
* Error message
|
|
22
|
+
*/
|
|
23
|
+
this.error = null;
|
|
24
|
+
/**
|
|
25
|
+
* Fetched occurrences
|
|
26
|
+
*/
|
|
27
|
+
this.occurrences = [];
|
|
28
|
+
/**
|
|
29
|
+
* Current month being displayed in calendar view
|
|
30
|
+
*/
|
|
31
|
+
this.currentMonth = new Date();
|
|
32
|
+
/**
|
|
33
|
+
* Currently selected date in calendar view
|
|
34
|
+
*/
|
|
35
|
+
this.selectedDate = null;
|
|
36
|
+
/**
|
|
37
|
+
* Computed display mode based on occurrences
|
|
38
|
+
*/
|
|
39
|
+
this.computedDisplayMode = 'list';
|
|
40
|
+
/**
|
|
41
|
+
* Container width for responsive layout
|
|
42
|
+
*/
|
|
43
|
+
this.containerWidth = 0;
|
|
44
|
+
}
|
|
45
|
+
async componentWillLoad() {
|
|
46
|
+
await this.waitForSDKAndFetch();
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Wait for SDK to be ready, then fetch occurrences
|
|
50
|
+
*/
|
|
51
|
+
async waitForSDKAndFetch() {
|
|
52
|
+
if (!this.eventId) {
|
|
53
|
+
this.error = 'Event ID is required';
|
|
54
|
+
this.loading = false;
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
try {
|
|
58
|
+
await sdkConnector.whenSDK();
|
|
59
|
+
await this.fetchOccurrences();
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
this.error = err instanceof Error ? err.message : 'SDK not initialized.';
|
|
63
|
+
this.loading = false;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
componentDidLoad() {
|
|
67
|
+
// Set up resize observer for responsive layout
|
|
68
|
+
if (this.containerRef && typeof ResizeObserver !== 'undefined') {
|
|
69
|
+
this.resizeObserver = new ResizeObserver((entries) => {
|
|
70
|
+
for (const entry of entries) {
|
|
71
|
+
this.containerWidth = entry.contentRect.width;
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
this.resizeObserver.observe(this.containerRef);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
disconnectedCallback() {
|
|
78
|
+
if (this.resizeObserver) {
|
|
79
|
+
this.resizeObserver.disconnect();
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
async onEventIdChange() {
|
|
83
|
+
await this.fetchOccurrences();
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Fetch occurrences from the SDK
|
|
87
|
+
*/
|
|
88
|
+
async fetchOccurrences() {
|
|
89
|
+
this.loading = true;
|
|
90
|
+
this.error = null;
|
|
91
|
+
try {
|
|
92
|
+
const sdk = sdkConnector.getSDK();
|
|
93
|
+
const eventsManager = sdk.getEventsManager();
|
|
94
|
+
const event = await eventsManager.get(this.eventId);
|
|
95
|
+
if (!event) {
|
|
96
|
+
throw new Error('Event not found');
|
|
97
|
+
}
|
|
98
|
+
// Map EventOccurrence to TlOccurrence
|
|
99
|
+
this.occurrences = (event.occurrences || []).map((occ) => ({
|
|
100
|
+
id: occ.id,
|
|
101
|
+
startsAt: occ.startsAt,
|
|
102
|
+
endsAt: occ.endsAt,
|
|
103
|
+
status: occ.availabilityStatus,
|
|
104
|
+
availableCapacity: occ.availableCapacity,
|
|
105
|
+
soldOut: occ.availabilityStatus === 'sold_out',
|
|
106
|
+
}));
|
|
107
|
+
this.computeDisplayMode();
|
|
108
|
+
this.computeInitialSelection();
|
|
109
|
+
}
|
|
110
|
+
catch (err) {
|
|
111
|
+
console.error('[tl-occurrence-selector] Failed to fetch occurrences:', err);
|
|
112
|
+
this.error = err instanceof Error ? err.message : 'Failed to load occurrences';
|
|
113
|
+
this.occurrences = [];
|
|
114
|
+
}
|
|
115
|
+
finally {
|
|
116
|
+
this.loading = false;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Compute the display mode based on occurrence count and date distribution
|
|
121
|
+
*/
|
|
122
|
+
computeDisplayMode() {
|
|
123
|
+
if (this.displayMode) {
|
|
124
|
+
this.computedDisplayMode = this.displayMode;
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
const count = this.occurrences.length;
|
|
128
|
+
if (count <= 5) {
|
|
129
|
+
this.computedDisplayMode = 'list';
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
// Check if all occurrences are on the same calendar date
|
|
133
|
+
const uniqueDates = new Set();
|
|
134
|
+
for (const occ of this.occurrences) {
|
|
135
|
+
const date = new Date(occ.startsAt);
|
|
136
|
+
uniqueDates.add(date.toDateString());
|
|
137
|
+
}
|
|
138
|
+
if (uniqueDates.size === 1) {
|
|
139
|
+
this.computedDisplayMode = 'slots';
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
this.computedDisplayMode = 'calendar';
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Compute the initial date selection for calendar/slots views
|
|
147
|
+
*/
|
|
148
|
+
computeInitialSelection() {
|
|
149
|
+
if (this.occurrences.length === 0)
|
|
150
|
+
return;
|
|
151
|
+
const today = new Date();
|
|
152
|
+
today.setHours(0, 0, 0, 0);
|
|
153
|
+
// Get all dates with occurrences
|
|
154
|
+
const datesWithOccurrences = this.getDatesWithOccurrences();
|
|
155
|
+
// Find the first date that is today or in the future
|
|
156
|
+
let selectedDate = null;
|
|
157
|
+
for (const dateStr of Array.from(datesWithOccurrences.keys()).sort()) {
|
|
158
|
+
const date = new Date(dateStr);
|
|
159
|
+
if (date >= today) {
|
|
160
|
+
selectedDate = date;
|
|
161
|
+
break;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
// If no future dates, select the last available date
|
|
165
|
+
if (!selectedDate && datesWithOccurrences.size > 0) {
|
|
166
|
+
const sortedDates = Array.from(datesWithOccurrences.keys()).sort();
|
|
167
|
+
selectedDate = new Date(sortedDates[sortedDates.length - 1]);
|
|
168
|
+
}
|
|
169
|
+
if (selectedDate) {
|
|
170
|
+
this.selectedDate = selectedDate;
|
|
171
|
+
this.currentMonth = new Date(selectedDate.getFullYear(), selectedDate.getMonth(), 1);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Get a map of dates to occurrences
|
|
176
|
+
*/
|
|
177
|
+
getDatesWithOccurrences() {
|
|
178
|
+
const map = new Map();
|
|
179
|
+
for (const occ of this.occurrences) {
|
|
180
|
+
const date = new Date(occ.startsAt);
|
|
181
|
+
date.setHours(0, 0, 0, 0);
|
|
182
|
+
const dateStr = date.toDateString();
|
|
183
|
+
if (!map.has(dateStr)) {
|
|
184
|
+
map.set(dateStr, []);
|
|
185
|
+
}
|
|
186
|
+
map.get(dateStr).push(occ);
|
|
187
|
+
}
|
|
188
|
+
return map;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Get the first and last occurrence dates
|
|
192
|
+
*/
|
|
193
|
+
getDateRange() {
|
|
194
|
+
if (this.occurrences.length === 0)
|
|
195
|
+
return null;
|
|
196
|
+
let first = new Date(this.occurrences[0].startsAt);
|
|
197
|
+
let last = new Date(this.occurrences[0].startsAt);
|
|
198
|
+
for (const occ of this.occurrences) {
|
|
199
|
+
const date = new Date(occ.startsAt);
|
|
200
|
+
if (date < first)
|
|
201
|
+
first = date;
|
|
202
|
+
if (date > last)
|
|
203
|
+
last = date;
|
|
204
|
+
}
|
|
205
|
+
return { first, last };
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Check if all occurrences fit in one week (Monday-Sunday)
|
|
209
|
+
*/
|
|
210
|
+
isSingleWeek() {
|
|
211
|
+
const dateRange = this.getDateRange();
|
|
212
|
+
if (!dateRange)
|
|
213
|
+
return false;
|
|
214
|
+
// Get Monday of the week containing the first date
|
|
215
|
+
const firstMonday = new Date(dateRange.first);
|
|
216
|
+
const dayOfWeek = firstMonday.getDay();
|
|
217
|
+
const daysToSubtract = dayOfWeek === 0 ? 6 : dayOfWeek - 1; // Monday = 0 days
|
|
218
|
+
firstMonday.setDate(firstMonday.getDate() - daysToSubtract);
|
|
219
|
+
firstMonday.setHours(0, 0, 0, 0);
|
|
220
|
+
// Get Sunday of that week
|
|
221
|
+
const sunday = new Date(firstMonday);
|
|
222
|
+
sunday.setDate(sunday.getDate() + 6);
|
|
223
|
+
sunday.setHours(23, 59, 59, 999);
|
|
224
|
+
// Check if last date is within this week
|
|
225
|
+
return dateRange.last <= sunday;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Check if all occurrences are in the same month
|
|
229
|
+
*/
|
|
230
|
+
isSingleMonth() {
|
|
231
|
+
const dateRange = this.getDateRange();
|
|
232
|
+
if (!dateRange)
|
|
233
|
+
return false;
|
|
234
|
+
return (dateRange.first.getMonth() === dateRange.last.getMonth() &&
|
|
235
|
+
dateRange.first.getFullYear() === dateRange.last.getFullYear());
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Check if we should use horizontal layout
|
|
239
|
+
*/
|
|
240
|
+
isHorizontalLayout() {
|
|
241
|
+
if (this.orientation === 'horizontal')
|
|
242
|
+
return true;
|
|
243
|
+
if (this.orientation === 'vertical')
|
|
244
|
+
return false;
|
|
245
|
+
// Auto: use horizontal if container is wide enough (768px)
|
|
246
|
+
return this.containerWidth >= 768;
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Format a date for display
|
|
250
|
+
*/
|
|
251
|
+
formatDate(date) {
|
|
252
|
+
return date.toLocaleDateString('en-GB', {
|
|
253
|
+
weekday: 'long',
|
|
254
|
+
day: 'numeric',
|
|
255
|
+
month: 'long',
|
|
256
|
+
year: 'numeric',
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Format a time for display
|
|
261
|
+
*/
|
|
262
|
+
formatTime(dateString) {
|
|
263
|
+
const date = new Date(dateString);
|
|
264
|
+
return date.toLocaleTimeString('en-GB', {
|
|
265
|
+
hour: '2-digit',
|
|
266
|
+
minute: '2-digit',
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Format a full date and time for list view
|
|
271
|
+
*/
|
|
272
|
+
formatDateTime(dateString) {
|
|
273
|
+
const date = new Date(dateString);
|
|
274
|
+
return {
|
|
275
|
+
date: date.toLocaleDateString('en-GB', {
|
|
276
|
+
weekday: 'long',
|
|
277
|
+
day: 'numeric',
|
|
278
|
+
month: 'long',
|
|
279
|
+
year: 'numeric',
|
|
280
|
+
}),
|
|
281
|
+
time: date.toLocaleTimeString('en-GB', {
|
|
282
|
+
hour: '2-digit',
|
|
283
|
+
minute: '2-digit',
|
|
284
|
+
}),
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Handle occurrence selection
|
|
289
|
+
*/
|
|
290
|
+
handleSelect(occurrence) {
|
|
291
|
+
if (occurrence.soldOut || occurrence.availableCapacity === 0)
|
|
292
|
+
return;
|
|
293
|
+
console.log('[tl-occurrence-selector] Selected:', occurrence.id);
|
|
294
|
+
this.tlSelect.emit({ occurrence });
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Handle date selection in calendar view
|
|
298
|
+
*/
|
|
299
|
+
handleDateSelect(date) {
|
|
300
|
+
this.selectedDate = date;
|
|
301
|
+
// If date is in a different month, navigate to that month
|
|
302
|
+
if (date.getMonth() !== this.currentMonth.getMonth() ||
|
|
303
|
+
date.getFullYear() !== this.currentMonth.getFullYear()) {
|
|
304
|
+
this.currentMonth = new Date(date.getFullYear(), date.getMonth(), 1);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Navigate to previous month
|
|
309
|
+
*/
|
|
310
|
+
handlePrevMonth() {
|
|
311
|
+
const dateRange = this.getDateRange();
|
|
312
|
+
if (!dateRange)
|
|
313
|
+
return;
|
|
314
|
+
const newMonth = new Date(this.currentMonth);
|
|
315
|
+
newMonth.setMonth(newMonth.getMonth() - 1);
|
|
316
|
+
// Don't go before the first occurrence month
|
|
317
|
+
const firstMonth = new Date(dateRange.first.getFullYear(), dateRange.first.getMonth(), 1);
|
|
318
|
+
if (newMonth >= firstMonth) {
|
|
319
|
+
this.currentMonth = newMonth;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Navigate to next month
|
|
324
|
+
*/
|
|
325
|
+
handleNextMonth() {
|
|
326
|
+
const dateRange = this.getDateRange();
|
|
327
|
+
if (!dateRange)
|
|
328
|
+
return;
|
|
329
|
+
const newMonth = new Date(this.currentMonth);
|
|
330
|
+
newMonth.setMonth(newMonth.getMonth() + 1);
|
|
331
|
+
// Don't go past the last occurrence month
|
|
332
|
+
const lastMonth = new Date(dateRange.last.getFullYear(), dateRange.last.getMonth(), 1);
|
|
333
|
+
if (newMonth <= lastMonth) {
|
|
334
|
+
this.currentMonth = newMonth;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Check if prev month button should be disabled
|
|
339
|
+
*/
|
|
340
|
+
isPrevMonthDisabled() {
|
|
341
|
+
const dateRange = this.getDateRange();
|
|
342
|
+
if (!dateRange)
|
|
343
|
+
return true;
|
|
344
|
+
const firstMonth = new Date(dateRange.first.getFullYear(), dateRange.first.getMonth(), 1);
|
|
345
|
+
return this.currentMonth <= firstMonth;
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Check if next month button should be disabled
|
|
349
|
+
*/
|
|
350
|
+
isNextMonthDisabled() {
|
|
351
|
+
const dateRange = this.getDateRange();
|
|
352
|
+
if (!dateRange)
|
|
353
|
+
return true;
|
|
354
|
+
const lastMonth = new Date(dateRange.last.getFullYear(), dateRange.last.getMonth(), 1);
|
|
355
|
+
return this.currentMonth >= lastMonth;
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* Get calendar grid days for the current month
|
|
359
|
+
*/
|
|
360
|
+
getCalendarDays() {
|
|
361
|
+
// If all occurrences fit in one week, only show that week
|
|
362
|
+
if (this.isSingleWeek()) {
|
|
363
|
+
const dateRange = this.getDateRange();
|
|
364
|
+
if (!dateRange)
|
|
365
|
+
return [];
|
|
366
|
+
// Get Monday of the week containing the first date
|
|
367
|
+
const firstMonday = new Date(dateRange.first);
|
|
368
|
+
const dayOfWeek = firstMonday.getDay();
|
|
369
|
+
const daysToSubtract = dayOfWeek === 0 ? 6 : dayOfWeek - 1;
|
|
370
|
+
firstMonday.setDate(firstMonday.getDate() - daysToSubtract);
|
|
371
|
+
firstMonday.setHours(0, 0, 0, 0);
|
|
372
|
+
// Generate 7 days (Monday to Sunday)
|
|
373
|
+
const days = [];
|
|
374
|
+
const current = new Date(firstMonday);
|
|
375
|
+
for (let i = 0; i < 7; i++) {
|
|
376
|
+
days.push(new Date(current));
|
|
377
|
+
current.setDate(current.getDate() + 1);
|
|
378
|
+
}
|
|
379
|
+
return days;
|
|
380
|
+
}
|
|
381
|
+
// Otherwise show full month grid
|
|
382
|
+
const year = this.currentMonth.getFullYear();
|
|
383
|
+
const month = this.currentMonth.getMonth();
|
|
384
|
+
// Start of month
|
|
385
|
+
const monthStart = new Date(year, month, 1);
|
|
386
|
+
// End of month
|
|
387
|
+
const monthEnd = new Date(year, month + 1, 0);
|
|
388
|
+
// Get the start of the week containing the first day (Monday = 1)
|
|
389
|
+
const startDay = monthStart.getDay();
|
|
390
|
+
const daysToSubtract = startDay === 0 ? 6 : startDay - 1; // Monday = 0 days to subtract
|
|
391
|
+
const calendarStart = new Date(monthStart);
|
|
392
|
+
calendarStart.setDate(calendarStart.getDate() - daysToSubtract);
|
|
393
|
+
// Get the end of the week containing the last day
|
|
394
|
+
const endDay = monthEnd.getDay();
|
|
395
|
+
const daysToAdd = endDay === 0 ? 0 : 7 - endDay;
|
|
396
|
+
const calendarEnd = new Date(monthEnd);
|
|
397
|
+
calendarEnd.setDate(calendarEnd.getDate() + daysToAdd);
|
|
398
|
+
// Generate all days
|
|
399
|
+
const days = [];
|
|
400
|
+
const current = new Date(calendarStart);
|
|
401
|
+
while (current <= calendarEnd) {
|
|
402
|
+
days.push(new Date(current));
|
|
403
|
+
current.setDate(current.getDate() + 1);
|
|
404
|
+
}
|
|
405
|
+
return days;
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Check if a date has occurrences
|
|
409
|
+
*/
|
|
410
|
+
hasOccurrencesOnDate(date) {
|
|
411
|
+
const dateStr = date.toDateString();
|
|
412
|
+
return this.getDatesWithOccurrences().has(dateStr);
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* Check if all occurrences on a date are sold out
|
|
416
|
+
*/
|
|
417
|
+
isDateSoldOut(date) {
|
|
418
|
+
const dateStr = date.toDateString();
|
|
419
|
+
const occurrences = this.getDatesWithOccurrences().get(dateStr);
|
|
420
|
+
if (!occurrences)
|
|
421
|
+
return false;
|
|
422
|
+
return occurrences.every(occ => occ.soldOut || occ.availableCapacity === 0);
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* Get occurrences for the selected date
|
|
426
|
+
*/
|
|
427
|
+
getSelectedDateOccurrences() {
|
|
428
|
+
if (!this.selectedDate)
|
|
429
|
+
return [];
|
|
430
|
+
const dateStr = this.selectedDate.toDateString();
|
|
431
|
+
const occurrences = this.getDatesWithOccurrences().get(dateStr) || [];
|
|
432
|
+
// Sort by start time
|
|
433
|
+
return occurrences.sort((a, b) => new Date(a.startsAt).getTime() - new Date(b.startsAt).getTime());
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Get CSS custom properties from theme
|
|
437
|
+
*/
|
|
438
|
+
getThemeStyles() {
|
|
439
|
+
if (!this.theme)
|
|
440
|
+
return {};
|
|
441
|
+
const styles = {};
|
|
442
|
+
if (this.theme.primaryColor) {
|
|
443
|
+
styles['--tl-primary'] = this.theme.primaryColor;
|
|
444
|
+
}
|
|
445
|
+
if (this.theme.primaryForeground) {
|
|
446
|
+
styles['--tl-primary-foreground'] = this.theme.primaryForeground;
|
|
447
|
+
}
|
|
448
|
+
if (this.theme.backgroundColor) {
|
|
449
|
+
styles['--tl-background'] = this.theme.backgroundColor;
|
|
450
|
+
}
|
|
451
|
+
if (this.theme.foregroundColor) {
|
|
452
|
+
styles['--tl-foreground'] = this.theme.foregroundColor;
|
|
453
|
+
}
|
|
454
|
+
if (this.theme.mutedColor) {
|
|
455
|
+
styles['--tl-muted'] = this.theme.mutedColor;
|
|
456
|
+
}
|
|
457
|
+
if (this.theme.mutedForeground) {
|
|
458
|
+
styles['--tl-muted-foreground'] = this.theme.mutedForeground;
|
|
459
|
+
}
|
|
460
|
+
if (this.theme.borderColor) {
|
|
461
|
+
styles['--tl-border'] = this.theme.borderColor;
|
|
462
|
+
}
|
|
463
|
+
if (this.theme.borderRadius) {
|
|
464
|
+
styles['--tl-radius'] = this.theme.borderRadius;
|
|
465
|
+
}
|
|
466
|
+
if (this.theme.fontFamily) {
|
|
467
|
+
styles['--tl-font-family'] = this.theme.fontFamily;
|
|
468
|
+
}
|
|
469
|
+
return styles;
|
|
470
|
+
}
|
|
471
|
+
// =========================================================================
|
|
472
|
+
// RENDER METHODS
|
|
473
|
+
// =========================================================================
|
|
474
|
+
/**
|
|
475
|
+
* Render the list view (≤5 occurrences)
|
|
476
|
+
*/
|
|
477
|
+
renderListView() {
|
|
478
|
+
return (index.h("div", { class: "list-view" }, this.occurrences.map((occurrence) => {
|
|
479
|
+
const isSoldOut = occurrence.soldOut || occurrence.availableCapacity === 0;
|
|
480
|
+
const { date, time } = this.formatDateTime(occurrence.startsAt);
|
|
481
|
+
return (index.h("button", { key: occurrence.id, class: `occurrence-item ${isSoldOut ? 'sold-out' : ''}`, onClick: () => this.handleSelect(occurrence), disabled: isSoldOut }, index.h("div", { class: "occurrence-info" }, index.h("div", { class: "occurrence-date" }, date), index.h("div", { class: "occurrence-time" }, time, occurrence.endsAt && ` - ${this.formatTime(occurrence.endsAt)}`)), index.h("div", { class: "occurrence-status" }, isSoldOut ? (index.h("span", { class: "status-sold-out" }, "Sold Out")) : occurrence.availableCapacity ? (index.h("span", { class: "status-available" }, occurrence.availableCapacity, " available")) : (index.h("span", { class: "status-arrow" }, "\u2192")))));
|
|
482
|
+
})));
|
|
483
|
+
}
|
|
484
|
+
/**
|
|
485
|
+
* Render the time slots view (>5 occurrences, same day)
|
|
486
|
+
*/
|
|
487
|
+
renderSlotsView() {
|
|
488
|
+
// Sort occurrences by time
|
|
489
|
+
const sortedOccurrences = [...this.occurrences].sort((a, b) => new Date(a.startsAt).getTime() - new Date(b.startsAt).getTime());
|
|
490
|
+
// Get the date from the first occurrence
|
|
491
|
+
const dateLabel = sortedOccurrences.length > 0
|
|
492
|
+
? this.formatDate(new Date(sortedOccurrences[0].startsAt))
|
|
493
|
+
: '';
|
|
494
|
+
return (index.h("div", { class: "slots-view" }, dateLabel && index.h("div", { class: "slots-date-header" }, dateLabel), index.h("div", { class: "time-slots-grid" }, sortedOccurrences.map((occurrence) => {
|
|
495
|
+
const isSoldOut = occurrence.soldOut || occurrence.availableCapacity === 0;
|
|
496
|
+
return (index.h("button", { key: occurrence.id, class: `time-slot ${isSoldOut ? 'sold-out' : ''}`, onClick: () => this.handleSelect(occurrence), disabled: isSoldOut }, index.h("span", { class: "slot-time" }, this.formatTime(occurrence.startsAt)), isSoldOut && index.h("span", { class: "slot-sold-out" }, "Sold Out")));
|
|
497
|
+
}))));
|
|
498
|
+
}
|
|
499
|
+
/**
|
|
500
|
+
* Render the calendar header
|
|
501
|
+
*/
|
|
502
|
+
renderCalendarHeader() {
|
|
503
|
+
const monthLabel = this.currentMonth.toLocaleDateString('en-GB', {
|
|
504
|
+
month: 'long',
|
|
505
|
+
year: 'numeric',
|
|
506
|
+
});
|
|
507
|
+
const showNavigation = !this.isSingleMonth();
|
|
508
|
+
return (index.h("div", { class: "calendar-header" }, index.h("span", { class: "month-label" }, monthLabel), showNavigation && (index.h("div", { class: "calendar-nav-buttons" }, index.h("button", { class: "nav-button", onClick: () => this.handlePrevMonth(), disabled: this.isPrevMonthDisabled(), "aria-label": "Previous month" }, index.h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2" }, index.h("path", { d: "M15 18l-6-6 6-6" }))), index.h("button", { class: "nav-button", onClick: () => this.handleNextMonth(), disabled: this.isNextMonthDisabled(), "aria-label": "Next month" }, index.h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2" }, index.h("path", { d: "M9 18l6-6-6-6" })))))));
|
|
509
|
+
}
|
|
510
|
+
/**
|
|
511
|
+
* Render the calendar grid
|
|
512
|
+
*/
|
|
513
|
+
renderCalendarGrid() {
|
|
514
|
+
const days = this.getCalendarDays();
|
|
515
|
+
const weekDays = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
|
|
516
|
+
const today = new Date();
|
|
517
|
+
today.setHours(0, 0, 0, 0);
|
|
518
|
+
return (index.h("div", { class: "calendar-grid" }, weekDays.map((day) => (index.h("div", { class: "weekday-header", key: day }, day))), days.map((date) => {
|
|
519
|
+
const isCurrentMonth = date.getMonth() === this.currentMonth.getMonth();
|
|
520
|
+
const hasOccurrences = this.hasOccurrencesOnDate(date);
|
|
521
|
+
const isSoldOut = this.isDateSoldOut(date);
|
|
522
|
+
const isSelected = this.selectedDate && date.toDateString() === this.selectedDate.toDateString();
|
|
523
|
+
const isToday = date.toDateString() === today.toDateString();
|
|
524
|
+
const isPast = date < today;
|
|
525
|
+
return (index.h("button", { key: date.toISOString(), class: `calendar-day
|
|
526
|
+
${!isCurrentMonth ? 'other-month' : ''}
|
|
527
|
+
${hasOccurrences ? 'has-occurrences' : ''}
|
|
528
|
+
${isSoldOut ? 'sold-out' : ''}
|
|
529
|
+
${isSelected ? 'selected' : ''}
|
|
530
|
+
${isToday ? 'today' : ''}
|
|
531
|
+
${isPast ? 'past' : ''}`, onClick: () => hasOccurrences && this.handleDateSelect(date), disabled: !hasOccurrences }, index.h("span", { class: "day-number" }, date.getDate()), hasOccurrences && index.h("span", { class: "occurrence-dot" })));
|
|
532
|
+
})));
|
|
533
|
+
}
|
|
534
|
+
/**
|
|
535
|
+
* Render the selected date's time slots
|
|
536
|
+
*/
|
|
537
|
+
renderSelectedDateSlots() {
|
|
538
|
+
const occurrences = this.getSelectedDateOccurrences();
|
|
539
|
+
if (!this.selectedDate || occurrences.length === 0) {
|
|
540
|
+
return (index.h("div", { class: "selected-date-slots empty" }, index.h("p", null, "Select a date to see available times")));
|
|
541
|
+
}
|
|
542
|
+
const dateLabel = this.formatDate(this.selectedDate);
|
|
543
|
+
return (index.h("div", { class: "selected-date-slots" }, index.h("div", { class: "selected-date-header" }, dateLabel), index.h("div", { class: "time-slots-list" }, occurrences.map((occurrence) => {
|
|
544
|
+
const isSoldOut = occurrence.soldOut || occurrence.availableCapacity === 0;
|
|
545
|
+
return (index.h("button", { key: occurrence.id, class: `time-slot-item ${isSoldOut ? 'sold-out' : ''}`, onClick: () => this.handleSelect(occurrence), disabled: isSoldOut }, index.h("span", { class: "slot-time" }, this.formatTime(occurrence.startsAt)), index.h("div", { class: "slot-status" }, isSoldOut ? (index.h("span", { class: "status-sold-out" }, "Sold Out")) : occurrence.availableCapacity ? (index.h("span", { class: "status-available" }, occurrence.availableCapacity, " available")) : (index.h("span", { class: "status-arrow" }, "\u2192")))));
|
|
546
|
+
}))));
|
|
547
|
+
}
|
|
548
|
+
/**
|
|
549
|
+
* Render the calendar view with slots panel
|
|
550
|
+
*/
|
|
551
|
+
renderCalendarView() {
|
|
552
|
+
const isHorizontal = this.isHorizontalLayout();
|
|
553
|
+
return (index.h("div", { class: `calendar-view ${isHorizontal ? 'horizontal' : 'vertical'}` }, index.h("div", { class: "calendar-panel" }, this.renderCalendarHeader(), this.renderCalendarGrid()), index.h("div", { class: "slots-panel" }, this.renderSelectedDateSlots())));
|
|
554
|
+
}
|
|
555
|
+
/**
|
|
556
|
+
* Render skeleton loading state
|
|
557
|
+
*/
|
|
558
|
+
renderSkeleton() {
|
|
559
|
+
// Default to calendar skeleton as it's the most common
|
|
560
|
+
return (index.h("div", { class: "loading-container" }, index.h("div", { class: "skeleton-calendar" }, index.h("div", { class: "skeleton skeleton-calendar-header" }), index.h("div", { class: "skeleton-calendar-grid" }, Array.from({ length: 35 }, (_, i) => (index.h("div", { key: i, class: "skeleton skeleton-calendar-day" })))))));
|
|
561
|
+
}
|
|
562
|
+
render() {
|
|
563
|
+
const themeStyles = this.getThemeStyles();
|
|
564
|
+
return (index.h("div", { key: '40758aaa06e5f062de92675766e4531995f74bc7', class: "occurrence-selector", ref: (el) => (this.containerRef = el), style: themeStyles }, this.loading && this.renderSkeleton(), !this.loading && this.error && (index.h("div", { key: '6d2f5b9c07f65d4941bfb4870729fe3248cb277e', class: "error-container" }, index.h("svg", { key: 'daaf1f48dcff80ae7cf94ae0da1d66d9c9b7fb0f', class: "error-icon", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2" }, index.h("circle", { key: '0a9f494a5cf7f79146e56fc44469a3ca9e5238ae', cx: "12", cy: "12", r: "10" }), index.h("path", { key: '67d7b453508c847593136bd60e34ea0c38d30cda', d: "M12 8v4m0 4h.01" })), index.h("p", { key: 'c1cc92585ed4451b686350b6d718c3ba06b72dd3', class: "error-message" }, this.error), index.h("button", { key: '4ec9d048af704af12a800385b9868501973ba477', class: "retry-button", onClick: () => this.fetchOccurrences() }, "Retry"))), !this.loading && !this.error && (this.occurrences.length === 0 ? (index.h("div", { class: "no-occurrences" }, index.h("p", null, "No dates available for this event."))) : (index.h("div", { class: "selector-content" }, this.computedDisplayMode === 'list' && this.renderListView(), this.computedDisplayMode === 'slots' && this.renderSlotsView(), this.computedDisplayMode === 'calendar' && this.renderCalendarView())))));
|
|
565
|
+
}
|
|
566
|
+
static get watchers() { return {
|
|
567
|
+
"eventId": [{
|
|
568
|
+
"onEventIdChange": 0
|
|
569
|
+
}]
|
|
570
|
+
}; }
|
|
571
|
+
};
|
|
572
|
+
TlOccurrenceSelector.style = tlOccurrenceSelectorCss();
|
|
573
|
+
|
|
574
|
+
const tlTicketSelectorCss = () => `:host{--tl-color-primary:var(--tl-primary, #6366f1);--tl-color-primary-foreground:var(--tl-primary-foreground, #ffffff);--tl-color-background:var(--tl-background, #ffffff);--tl-color-foreground:var(--tl-foreground, #111827);--tl-color-muted:var(--tl-muted, #f3f4f6);--tl-color-muted-foreground:var(--tl-muted-foreground, #6b7280);--tl-color-border:var(--tl-border, #e5e7eb);--tl-color-destructive:var(--tl-error, #ef4444);display:block;font-family:var(--tl-font-family, system-ui, sans-serif)}.ticket-selector{background:var(--tl-color-background, #ffffff)}.skeleton-container{padding:var(--tl-spacing-4, 1rem)}.skeleton-row{height:60px;background:var(--tl-color-muted, #f3f4f6);border-radius:var(--tl-radius-md, 0.5rem);margin-bottom:var(--tl-spacing-3, 0.75rem);animation:pulse 1.5s ease-in-out infinite}@keyframes pulse{0%,100%{opacity:1}50%{opacity:0.5}}.error-state{padding:var(--tl-spacing-4, 1rem);color:var(--tl-color-destructive, #ef4444)}.error-message{margin:0;font-size:14px}.ticket-list{margin-bottom:var(--tl-spacing-4, 1rem)}.ticket-row{display:flex;align-items:center;justify-content:space-between;padding:var(--tl-spacing-3, 0.75rem) 0;border-bottom:1px solid var(--tl-color-border, #e5e7eb)}.ticket-row.sold-out{opacity:0.5}.ticket-info{flex:1;min-width:0}.ticket-header{display:flex;align-items:center;gap:var(--tl-spacing-2, 0.5rem);flex-wrap:wrap}.ticket-name{font-weight:600;color:var(--tl-color-foreground, #111827);font-size:15px}.status-badge{font-size:12px;font-weight:500;padding:2px 8px;border-radius:4px}.status-badge.limited{background-color:var(--tl-color-warning-bg, #fef3c7);color:var(--tl-color-warning, #d97706)}.status-badge.sold-out{background-color:var(--tl-color-destructive-bg, #fee2e2);color:var(--tl-color-destructive, #ef4444)}.ticket-description{margin:var(--tl-spacing-1, 0.25rem) 0 0;font-size:14px;color:var(--tl-color-muted-foreground, #6b7280)}.ticket-price{margin:var(--tl-spacing-1, 0.25rem) 0 0;font-size:14px;font-weight:600;color:var(--tl-color-foreground, #111827)}.quantity-controls{display:flex;align-items:center;gap:var(--tl-spacing-2, 0.5rem);margin-left:var(--tl-spacing-4, 1rem)}.qty-btn{width:32px;height:32px;border-radius:var(--tl-radius-sm, 0.25rem);border:1px solid var(--tl-color-border, #d1d5db);background:var(--tl-color-background, #ffffff);color:var(--tl-color-foreground, #111827);font-size:18px;font-weight:500;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:background-color 0.15s, border-color 0.15s}.qty-btn:hover:not(:disabled){background-color:var(--tl-color-muted, #f3f4f6);border-color:var(--tl-color-border-hover, #9ca3af)}.qty-btn:disabled{cursor:not-allowed;opacity:0.5}.qty-value{width:32px;text-align:center;font-weight:600;font-size:16px;color:var(--tl-color-foreground, #111827)}.success-message{display:flex;align-items:center;justify-content:center;gap:var(--tl-spacing-2, 0.5rem);padding:var(--tl-spacing-3, 0.75rem);background-color:var(--tl-color-success, #10b981);color:#ffffff;border-radius:var(--tl-radius-md, 0.5rem);margin-bottom:var(--tl-spacing-3, 0.75rem);font-weight:500;font-size:14px}.success-icon{width:18px;height:18px}.add-to-cart-btn{width:100%;padding:var(--tl-spacing-3, 0.75rem) var(--tl-spacing-4, 1rem);border-radius:var(--tl-radius-md, 0.5rem);border:none;background-color:var(--tl-color-primary, #6366f1);color:var(--tl-color-primary-foreground, #ffffff);font-weight:600;font-size:16px;cursor:pointer;transition:opacity 0.15s, transform 0.1s}.add-to-cart-btn:hover:not(:disabled){opacity:0.9}.add-to-cart-btn:active:not(:disabled){transform:scale(0.98)}.add-to-cart-btn.disabled,.add-to-cart-btn:disabled{background-color:var(--tl-color-muted, #d1d5db);color:var(--tl-color-muted-foreground, #6b7280);cursor:not-allowed}`;
|
|
575
|
+
|
|
576
|
+
const TlTicketSelector = class {
|
|
577
|
+
constructor(hostRef) {
|
|
578
|
+
index.registerInstance(this, hostRef);
|
|
579
|
+
this.tlAddToCart = index.createEvent(this, "tlAddToCart");
|
|
580
|
+
this.tlQuantityChange = index.createEvent(this, "tlQuantityChange");
|
|
581
|
+
this.tlError = index.createEvent(this, "tlError");
|
|
582
|
+
/**
|
|
583
|
+
* Show add to cart button
|
|
584
|
+
*/
|
|
585
|
+
this.showButton = true;
|
|
586
|
+
/**
|
|
587
|
+
* Button label
|
|
588
|
+
*/
|
|
589
|
+
this.buttonLabel = 'Add to Cart';
|
|
590
|
+
this.ticketTypes = [];
|
|
591
|
+
this.quantities = {};
|
|
592
|
+
this.loading = true;
|
|
593
|
+
this.error = null;
|
|
594
|
+
this.adding = false;
|
|
595
|
+
this.addSuccess = false;
|
|
596
|
+
}
|
|
597
|
+
watchPropsChange() {
|
|
598
|
+
this.loadTicketTypes();
|
|
599
|
+
}
|
|
600
|
+
async componentWillLoad() {
|
|
601
|
+
await this.waitForSDKAndLoad();
|
|
602
|
+
}
|
|
603
|
+
/**
|
|
604
|
+
* Wait for SDK to be ready, then load ticket types
|
|
605
|
+
*/
|
|
606
|
+
async waitForSDKAndLoad() {
|
|
607
|
+
if (!this.eventId || !this.occurrenceId) {
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
610
|
+
try {
|
|
611
|
+
await sdkConnector.whenSDK();
|
|
612
|
+
await this.loadTicketTypes();
|
|
613
|
+
}
|
|
614
|
+
catch (err) {
|
|
615
|
+
this.error = err instanceof Error ? err.message : 'SDK not initialized.';
|
|
616
|
+
this.loading = false;
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
async loadTicketTypes() {
|
|
620
|
+
try {
|
|
621
|
+
console.log('[tl-ticket-selector] Loading ticket types...', {
|
|
622
|
+
eventId: this.eventId,
|
|
623
|
+
occurrenceId: this.occurrenceId
|
|
624
|
+
});
|
|
625
|
+
this.loading = true;
|
|
626
|
+
this.error = null;
|
|
627
|
+
const sdk = sdkConnector.getSDK();
|
|
628
|
+
const eventsManager = sdk.getEventsManager();
|
|
629
|
+
const types = await eventsManager.getTicketTypes(this.eventId, this.occurrenceId);
|
|
630
|
+
console.log('[tl-ticket-selector] Ticket types loaded:', types.length);
|
|
631
|
+
this.ticketTypes = types;
|
|
632
|
+
// Initialise quantities to 0
|
|
633
|
+
const initialQuantities = {};
|
|
634
|
+
types.forEach((t) => {
|
|
635
|
+
initialQuantities[t.id] = 0;
|
|
636
|
+
});
|
|
637
|
+
this.quantities = initialQuantities;
|
|
638
|
+
}
|
|
639
|
+
catch (err) {
|
|
640
|
+
console.error('[tl-ticket-selector] Failed to load ticket types:', err);
|
|
641
|
+
this.error = err instanceof Error ? err.message : 'Failed to load tickets';
|
|
642
|
+
this.tlError.emit({ message: this.error });
|
|
643
|
+
}
|
|
644
|
+
finally {
|
|
645
|
+
this.loading = false;
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
handleQuantityChange(ticketTypeId, delta) {
|
|
649
|
+
const ticketType = this.ticketTypes.find(t => t.id === ticketTypeId);
|
|
650
|
+
if (!ticketType)
|
|
651
|
+
return;
|
|
652
|
+
const currentQty = this.quantities[ticketTypeId] || 0;
|
|
653
|
+
const newQty = Math.max(0, Math.min(ticketType.maxPerOrder, currentQty + delta));
|
|
654
|
+
this.quantities = {
|
|
655
|
+
...this.quantities,
|
|
656
|
+
[ticketTypeId]: newQty,
|
|
657
|
+
};
|
|
658
|
+
const total = this.calculateTotal();
|
|
659
|
+
this.tlQuantityChange.emit({ ticketTypeId, quantity: newQty, total });
|
|
660
|
+
}
|
|
661
|
+
calculateTotal() {
|
|
662
|
+
return this.ticketTypes.reduce((sum, tt) => {
|
|
663
|
+
return sum + (this.quantities[tt.id] || 0) * tt.price;
|
|
664
|
+
}, 0);
|
|
665
|
+
}
|
|
666
|
+
getTotalItems() {
|
|
667
|
+
return Object.values(this.quantities).reduce((sum, qty) => sum + qty, 0);
|
|
668
|
+
}
|
|
669
|
+
async handleAddToCart() {
|
|
670
|
+
const itemsToAdd = Object.entries(this.quantities)
|
|
671
|
+
.filter(([_, qty]) => qty > 0)
|
|
672
|
+
.map(([ticketTypeId, quantity]) => ({ ticketTypeId, quantity }));
|
|
673
|
+
if (itemsToAdd.length === 0)
|
|
674
|
+
return;
|
|
675
|
+
this.adding = true;
|
|
676
|
+
this.addSuccess = false;
|
|
677
|
+
try {
|
|
678
|
+
if (sdkConnector.hasSDK()) {
|
|
679
|
+
const sdk = sdkConnector.getSDK();
|
|
680
|
+
const cartManager = sdk.getCartManager?.();
|
|
681
|
+
if (cartManager) {
|
|
682
|
+
for (const item of itemsToAdd) {
|
|
683
|
+
await cartManager.addItem(item.ticketTypeId, item.quantity, this.occurrenceId);
|
|
684
|
+
}
|
|
685
|
+
console.log('[tl-ticket-selector] Items added to cart');
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
// Reset quantities after adding
|
|
689
|
+
const resetQuantities = {};
|
|
690
|
+
this.ticketTypes.forEach(t => {
|
|
691
|
+
resetQuantities[t.id] = 0;
|
|
692
|
+
});
|
|
693
|
+
this.quantities = resetQuantities;
|
|
694
|
+
this.addSuccess = true;
|
|
695
|
+
// Clear success message after 3 seconds
|
|
696
|
+
setTimeout(() => {
|
|
697
|
+
this.addSuccess = false;
|
|
698
|
+
}, 3000);
|
|
699
|
+
this.tlAddToCart.emit({ items: itemsToAdd });
|
|
700
|
+
}
|
|
701
|
+
catch (err) {
|
|
702
|
+
console.error('[tl-ticket-selector] Failed to add to cart:', err);
|
|
703
|
+
this.error = err instanceof Error ? err.message : 'Failed to add to cart';
|
|
704
|
+
this.tlError.emit({ message: this.error });
|
|
705
|
+
}
|
|
706
|
+
finally {
|
|
707
|
+
this.adding = false;
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
formatPrice(priceInPence, currency = 'GBP') {
|
|
711
|
+
return new Intl.NumberFormat('en-GB', {
|
|
712
|
+
style: 'currency',
|
|
713
|
+
currency,
|
|
714
|
+
}).format(priceInPence / 100);
|
|
715
|
+
}
|
|
716
|
+
renderSkeleton() {
|
|
717
|
+
return (index.h("div", { class: "skeleton-container" }, index.h("div", { class: "skeleton-row" }), index.h("div", { class: "skeleton-row" }), index.h("div", { class: "skeleton-row" })));
|
|
718
|
+
}
|
|
719
|
+
renderError() {
|
|
720
|
+
return (index.h("div", { class: "error-state" }, index.h("p", { class: "error-message" }, this.error)));
|
|
721
|
+
}
|
|
722
|
+
renderTicketRow(tt) {
|
|
723
|
+
const isSoldOut = tt.availabilityStatus === 'sold_out';
|
|
724
|
+
const qty = this.quantities[tt.id] || 0;
|
|
725
|
+
return (index.h("div", { class: `ticket-row ${isSoldOut ? 'sold-out' : ''}`, key: tt.id }, index.h("div", { class: "ticket-info" }, index.h("div", { class: "ticket-header" }, index.h("span", { class: "ticket-name" }, tt.name), tt.availabilityStatus === 'limited' && (index.h("span", { class: "status-badge limited" }, "Limited")), isSoldOut && (index.h("span", { class: "status-badge sold-out" }, "Sold Out"))), tt.description && (index.h("p", { class: "ticket-description" }, tt.description)), index.h("p", { class: "ticket-price" }, this.formatPrice(tt.price, tt.currency))), index.h("div", { class: "quantity-controls" }, index.h("button", { class: "qty-btn", onClick: () => this.handleQuantityChange(tt.id, -1), disabled: isSoldOut || qty === 0, "aria-label": "Decrease quantity" }, "\u2212"), index.h("span", { class: "qty-value" }, qty), index.h("button", { class: "qty-btn", onClick: () => this.handleQuantityChange(tt.id, 1), disabled: isSoldOut || qty >= tt.maxPerOrder, "aria-label": "Increase quantity" }, "+"))));
|
|
726
|
+
}
|
|
727
|
+
render() {
|
|
728
|
+
if (this.loading) {
|
|
729
|
+
return this.renderSkeleton();
|
|
730
|
+
}
|
|
731
|
+
if (this.error) {
|
|
732
|
+
return this.renderError();
|
|
733
|
+
}
|
|
734
|
+
const totalItems = this.getTotalItems();
|
|
735
|
+
const totalPrice = this.calculateTotal();
|
|
736
|
+
return (index.h("div", { class: "ticket-selector" }, index.h("div", { class: "ticket-list" }, this.ticketTypes.map(tt => this.renderTicketRow(tt))), this.addSuccess && (index.h("div", { class: "success-message" }, index.h("svg", { class: "success-icon", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor" }, index.h("path", { "stroke-linecap": "round", "stroke-linejoin": "round", "stroke-width": "2", d: "M5 13l4 4L19 7" })), "Added to cart!")), this.showButton && (index.h("button", { class: `add-to-cart-btn ${totalItems === 0 ? 'disabled' : ''}`, onClick: () => this.handleAddToCart(), disabled: totalItems === 0 || this.adding }, this.adding
|
|
737
|
+
? 'Adding...'
|
|
738
|
+
: totalItems === 0
|
|
739
|
+
? 'Select Tickets'
|
|
740
|
+
: `${this.buttonLabel} - ${this.formatPrice(totalPrice)}`))));
|
|
741
|
+
}
|
|
742
|
+
static get watchers() { return {
|
|
743
|
+
"eventId": [{
|
|
744
|
+
"watchPropsChange": 0
|
|
745
|
+
}],
|
|
746
|
+
"occurrenceId": [{
|
|
747
|
+
"watchPropsChange": 0
|
|
748
|
+
}]
|
|
749
|
+
}; }
|
|
750
|
+
};
|
|
751
|
+
TlTicketSelector.style = tlTicketSelectorCss();
|
|
752
|
+
|
|
753
|
+
exports.tl_occurrence_selector = TlOccurrenceSelector;
|
|
754
|
+
exports.tl_ticket_selector = TlTicketSelector;
|