@seatlayer/js 0.47.1 → 0.47.2
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/index.cjs +296 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +91 -1
- package/dist/index.d.ts +91 -1
- package/dist/index.js +293 -4
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -3957,11 +3957,13 @@ __export(index_exports, {
|
|
|
3957
3957
|
markerOf: () => markerOf,
|
|
3958
3958
|
mutationCount: () => mutationCount,
|
|
3959
3959
|
needsMoveConfirmation: () => needsMoveConfirmation,
|
|
3960
|
+
parseTicketOfferAvailability: () => parseTicketOfferAvailability,
|
|
3960
3961
|
planAssignment: () => planAssignment,
|
|
3961
3962
|
retryAfterCopy: () => retryAfterCopy,
|
|
3962
3963
|
selectionSources: () => selectionSources,
|
|
3963
3964
|
stateBadge: () => stateBadge,
|
|
3964
|
-
suggestMarker: () => suggestMarker
|
|
3965
|
+
suggestMarker: () => suggestMarker,
|
|
3966
|
+
ticketOfferPrices: () => ticketOfferPrices
|
|
3965
3967
|
});
|
|
3966
3968
|
module.exports = __toCommonJS(index_exports);
|
|
3967
3969
|
|
|
@@ -4497,6 +4499,10 @@ var PubApi = class {
|
|
|
4497
4499
|
paymentOptions(key) {
|
|
4498
4500
|
return this.request(`/pub/events/${encodeURIComponent(key)}/payment-options`);
|
|
4499
4501
|
}
|
|
4502
|
+
/** Server-resolved active ticket offers and category prices. */
|
|
4503
|
+
availability(key, live = false) {
|
|
4504
|
+
return this.request(`/pub/events/${encodeURIComponent(key)}/availability${live ? "?live=1" : ""}`);
|
|
4505
|
+
}
|
|
4500
4506
|
/**
|
|
4501
4507
|
* Turn a live hold into an order and start a payment.
|
|
4502
4508
|
*
|
|
@@ -4993,7 +4999,7 @@ var SeatingChart = class {
|
|
|
4993
4999
|
this.tipEl.style.display = "none";
|
|
4994
5000
|
return;
|
|
4995
5001
|
}
|
|
4996
|
-
const
|
|
5002
|
+
const money2 = (() => {
|
|
4997
5003
|
try {
|
|
4998
5004
|
return new Intl.NumberFormat(void 0, { style: "currency", currency: details.currency }).format(details.price);
|
|
4999
5005
|
} catch {
|
|
@@ -5001,7 +5007,7 @@ var SeatingChart = class {
|
|
|
5001
5007
|
}
|
|
5002
5008
|
})();
|
|
5003
5009
|
const statusLine = details.status === "free" ? "" : `<div style="margin-top:5px;font-size:10.5px;letter-spacing:.08em;text-transform:uppercase;color:#fca5a5;font-weight:700">${details.status === "held" ? (0, import_core.t)("map.statusHeld") : (0, import_core.t)("map.statusTaken")}</div>`;
|
|
5004
|
-
this.tipEl.innerHTML = `<div style="font-weight:700;font-size:13px">${details.label}</div><div style="display:flex;align-items:center;gap:6px;margin-top:4px;color:#c7cddc"><span style="width:9px;height:9px;border-radius:50%;flex:none;background:${details.categoryColor}"></span><span>${details.categoryLabel}</span><span style="margin-left:auto;font-weight:700;color:#fff">${
|
|
5010
|
+
this.tipEl.innerHTML = `<div style="font-weight:700;font-size:13px">${details.label}</div><div style="display:flex;align-items:center;gap:6px;margin-top:4px;color:#c7cddc"><span style="width:9px;height:9px;border-radius:50%;flex:none;background:${details.categoryColor}"></span><span>${details.categoryLabel}</span><span style="margin-left:auto;font-weight:700;color:#fff">${money2}</span></div>` + statusLine;
|
|
5005
5011
|
this.tipEl.style.display = "block";
|
|
5006
5012
|
this.placeTooltip();
|
|
5007
5013
|
}
|
|
@@ -5895,6 +5901,136 @@ var EmbeddedDesigner = class {
|
|
|
5895
5901
|
var import_core2 = require("@seatlayer/core");
|
|
5896
5902
|
var import_panorama = require("@seatlayer/core/view3d/crossfade/panorama");
|
|
5897
5903
|
var import_panoramaDelivery = require("@seatlayer/core/view/panoramaDelivery");
|
|
5904
|
+
|
|
5905
|
+
// src/offerAvailability.ts
|
|
5906
|
+
var SALE_STATES = [
|
|
5907
|
+
"on-sale",
|
|
5908
|
+
"low",
|
|
5909
|
+
"sold-out",
|
|
5910
|
+
"presale",
|
|
5911
|
+
"closed"
|
|
5912
|
+
];
|
|
5913
|
+
function money(value) {
|
|
5914
|
+
if (value === null || value === void 0) return null;
|
|
5915
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
|
|
5916
|
+
}
|
|
5917
|
+
function timestamp(value) {
|
|
5918
|
+
if (value === null || value === void 0) return null;
|
|
5919
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
|
|
5920
|
+
}
|
|
5921
|
+
function parseSummary(value) {
|
|
5922
|
+
if (value == null) return null;
|
|
5923
|
+
if (typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
5924
|
+
const source = value;
|
|
5925
|
+
const count = source.count;
|
|
5926
|
+
const index = source.index;
|
|
5927
|
+
if (typeof index !== "number" || !Number.isInteger(index) || index < 1) return void 0;
|
|
5928
|
+
if (typeof count !== "number" || !Number.isInteger(count) || count < index) return void 0;
|
|
5929
|
+
const remaining = source.remaining;
|
|
5930
|
+
if (remaining != null && (typeof remaining !== "number" || !Number.isInteger(remaining) || remaining < 0)) {
|
|
5931
|
+
return void 0;
|
|
5932
|
+
}
|
|
5933
|
+
const result = {
|
|
5934
|
+
index,
|
|
5935
|
+
count,
|
|
5936
|
+
remaining: remaining == null ? null : remaining
|
|
5937
|
+
};
|
|
5938
|
+
if (source.id !== void 0) {
|
|
5939
|
+
if (typeof source.id !== "string" || !source.id.trim()) return void 0;
|
|
5940
|
+
result.id = source.id.trim();
|
|
5941
|
+
}
|
|
5942
|
+
if (source.name !== void 0) {
|
|
5943
|
+
if (typeof source.name !== "string" || !source.name.trim()) return void 0;
|
|
5944
|
+
result.name = source.name.trim();
|
|
5945
|
+
}
|
|
5946
|
+
if (source.categoryKey !== void 0) {
|
|
5947
|
+
if (source.categoryKey !== null && (typeof source.categoryKey !== "string" || !source.categoryKey.trim())) {
|
|
5948
|
+
return void 0;
|
|
5949
|
+
}
|
|
5950
|
+
result.categoryKey = source.categoryKey == null ? null : source.categoryKey.trim();
|
|
5951
|
+
}
|
|
5952
|
+
for (const key of ["startsAt", "endsAt"]) {
|
|
5953
|
+
if (source[key] === void 0) continue;
|
|
5954
|
+
const parsed = timestamp(source[key]);
|
|
5955
|
+
if (parsed === void 0) return void 0;
|
|
5956
|
+
result[key] = parsed;
|
|
5957
|
+
}
|
|
5958
|
+
return result;
|
|
5959
|
+
}
|
|
5960
|
+
function parseTicketOfferAvailability(body) {
|
|
5961
|
+
if (!body || typeof body !== "object" || Array.isArray(body)) return null;
|
|
5962
|
+
const raw = body;
|
|
5963
|
+
const state = SALE_STATES.find((candidate) => candidate === raw.state);
|
|
5964
|
+
if (!state) return null;
|
|
5965
|
+
const fromPrice = money(raw.fromPrice);
|
|
5966
|
+
const previousPrice = money(raw.previousPrice);
|
|
5967
|
+
if (fromPrice === void 0 || previousPrice === void 0) return null;
|
|
5968
|
+
const currency = raw.currency == null ? null : typeof raw.currency === "string" && raw.currency.trim() ? raw.currency.trim() : void 0;
|
|
5969
|
+
if (currency === void 0) return null;
|
|
5970
|
+
const release = parseSummary(raw.release);
|
|
5971
|
+
if (release === void 0) return null;
|
|
5972
|
+
const upcoming = raw.upcoming === void 0 ? null : parseSummary(raw.upcoming);
|
|
5973
|
+
if (upcoming === void 0) return null;
|
|
5974
|
+
let prices = [];
|
|
5975
|
+
if (raw.prices != null) {
|
|
5976
|
+
if (!Array.isArray(raw.prices)) return null;
|
|
5977
|
+
const parsed = [];
|
|
5978
|
+
for (const entry of raw.prices) {
|
|
5979
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) return null;
|
|
5980
|
+
const row = entry;
|
|
5981
|
+
const categoryKey = typeof row.categoryKey === "string" ? row.categoryKey.trim() : "";
|
|
5982
|
+
if (!categoryKey) return null;
|
|
5983
|
+
const price = money(row.price);
|
|
5984
|
+
const previous = money(row.previousPrice);
|
|
5985
|
+
if (price === void 0 || price === null || previous === void 0) return null;
|
|
5986
|
+
const item = { categoryKey, price, previousPrice: previous };
|
|
5987
|
+
if (row.offerId !== void 0) {
|
|
5988
|
+
if (typeof row.offerId !== "string" || !row.offerId.trim()) return null;
|
|
5989
|
+
item.offerId = row.offerId.trim();
|
|
5990
|
+
}
|
|
5991
|
+
if (row.offerName !== void 0) {
|
|
5992
|
+
if (typeof row.offerName !== "string" || !row.offerName.trim()) return null;
|
|
5993
|
+
item.offerName = row.offerName.trim();
|
|
5994
|
+
}
|
|
5995
|
+
if (row.remaining !== void 0) {
|
|
5996
|
+
if (row.remaining !== null && (typeof row.remaining !== "number" || !Number.isInteger(row.remaining) || row.remaining < 0)) return null;
|
|
5997
|
+
item.remaining = row.remaining == null ? null : row.remaining;
|
|
5998
|
+
}
|
|
5999
|
+
for (const key of ["startsAt", "endsAt"]) {
|
|
6000
|
+
if (row[key] === void 0) continue;
|
|
6001
|
+
const at = timestamp(row[key]);
|
|
6002
|
+
if (at === void 0) return null;
|
|
6003
|
+
item[key] = at;
|
|
6004
|
+
}
|
|
6005
|
+
parsed.push(item);
|
|
6006
|
+
}
|
|
6007
|
+
prices = parsed;
|
|
6008
|
+
}
|
|
6009
|
+
return { state, fromPrice, previousPrice, currency, release, upcoming, prices };
|
|
6010
|
+
}
|
|
6011
|
+
function nextOfferTransitionAt(availability, now) {
|
|
6012
|
+
if (!availability) return null;
|
|
6013
|
+
let next = null;
|
|
6014
|
+
const consider = (at) => {
|
|
6015
|
+
if (at != null && at > now && (next === null || at < next)) next = at;
|
|
6016
|
+
};
|
|
6017
|
+
for (const summary of [availability.release, availability.upcoming]) {
|
|
6018
|
+
consider(summary?.startsAt);
|
|
6019
|
+
consider(summary?.endsAt);
|
|
6020
|
+
}
|
|
6021
|
+
for (const price of availability.prices) {
|
|
6022
|
+
consider(price.startsAt);
|
|
6023
|
+
consider(price.endsAt);
|
|
6024
|
+
}
|
|
6025
|
+
return next;
|
|
6026
|
+
}
|
|
6027
|
+
function ticketOfferPrices(availability) {
|
|
6028
|
+
const map = {};
|
|
6029
|
+
for (const entry of availability?.prices ?? []) map[entry.categoryKey] = entry.price;
|
|
6030
|
+
return map;
|
|
6031
|
+
}
|
|
6032
|
+
|
|
6033
|
+
// src/SeatPicker.ts
|
|
5898
6034
|
var import_meta = {};
|
|
5899
6035
|
var DEFAULT_API_BASE2 = "https://api.seatlayer.io";
|
|
5900
6036
|
var DEFAULT_MAX_SELECTION2 = 10;
|
|
@@ -6116,6 +6252,15 @@ var CSS2 = (
|
|
|
6116
6252
|
.sl-cbbtn svg{width:14px;height:14px;stroke:currentColor;stroke-width:2;fill:none;stroke-linecap:round;stroke-linejoin:round}
|
|
6117
6253
|
|
|
6118
6254
|
/* price panel \u2014 one compact filter control replaces the wrapping price-chip row. */
|
|
6255
|
+
.sl-offer{display:none;margin:12px 14px 2px;padding:12px;border:1px solid color-mix(in srgb,var(--sl-accent) 34%,var(--sl-line));
|
|
6256
|
+
border-radius:12px;background:color-mix(in srgb,var(--sl-accent) 8%,var(--sl-surface));color:var(--sl-text)}
|
|
6257
|
+
.sl-offer.has{display:block}.sl-offer-main{display:flex;align-items:flex-start;justify-content:space-between;gap:10px}
|
|
6258
|
+
.sl-offer-copy{min-width:0}.sl-offer-kicker{display:block;font-size:9px;font-weight:800;letter-spacing:.14em;text-transform:uppercase;color:var(--sl-accent)}
|
|
6259
|
+
.sl-offer-name{display:block;margin-top:2px;font-size:13px;font-weight:800;line-height:1.3}.sl-offer-line{display:block;margin-top:3px;font-size:11px;line-height:1.35;color:var(--sl-muted)}
|
|
6260
|
+
.sl-offer-info{position:relative;flex:none}.sl-offer-info>summary{list-style:none;width:25px;height:25px;border:1px solid var(--sl-line);border-radius:999px;
|
|
6261
|
+
display:grid;place-items:center;cursor:pointer;font-size:12px;font-weight:850;color:var(--sl-text);background:var(--sl-surface)}
|
|
6262
|
+
.sl-offer-info>summary::-webkit-details-marker{display:none}.sl-offer-info[open]>summary{border-color:var(--sl-accent);color:var(--sl-accent)}
|
|
6263
|
+
.sl-offer-detail{margin-top:10px;padding-top:9px;border-top:1px solid var(--sl-line);font-size:10.5px;line-height:1.45;color:var(--sl-muted)}
|
|
6119
6264
|
.sl-sec{padding:14px 14px 4px;font-size:9.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--sl-muted);font-weight:700}
|
|
6120
6265
|
.sl-prices-sec{display:flex;align-items:center;justify-content:space-between;gap:10px;padding-top:13px}
|
|
6121
6266
|
.sl-price-select{min-height:32px;max-width:130px;padding:5px 28px 5px 9px;border:1px solid var(--sl-line);border-radius:9px;
|
|
@@ -6126,6 +6271,7 @@ var CSS2 = (
|
|
|
6126
6271
|
padding:0 6px;margin:0 -6px;border-radius:8px;cursor:pointer;transition:background .15s}
|
|
6127
6272
|
.sl-price-row:hover,.sl-price-row:focus-visible{background:color-mix(in srgb,var(--sl-line) 40%,transparent)}
|
|
6128
6273
|
.sl-price-row.sl-active{background:color-mix(in srgb,var(--sl-accent) 9%,transparent)}
|
|
6274
|
+
.sl-price-was{margin-left:auto;color:var(--sl-muted);font-size:10px;text-decoration:line-through}.sl-price-offer{display:block;color:var(--sl-accent);font-size:9px;font-weight:750}
|
|
6129
6275
|
.sl-price-row.sl-active .sl-price-label{color:var(--sl-accent)}
|
|
6130
6276
|
.sl-dot{width:9px;height:9px;border-radius:50%;flex:none}
|
|
6131
6277
|
.sl-price-label{flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-weight:600}
|
|
@@ -6851,10 +6997,17 @@ var SeatPicker = class _SeatPicker {
|
|
|
6851
6997
|
this.ro = null;
|
|
6852
6998
|
this.holdTimer = null;
|
|
6853
6999
|
this.toastTimer = null;
|
|
7000
|
+
this.offerRefreshTimer = null;
|
|
7001
|
+
/** Armed only when the offer schedule has a known future transition (or as a
|
|
7002
|
+
* bounded retry after a failed read) — never a fixed-cadence poll. */
|
|
7003
|
+
this.offerBoundaryTimer = null;
|
|
7004
|
+
this.offerVisibilityHandler = null;
|
|
6854
7005
|
/** Short-lived UI motion timers; all are cancelled on destroy. */
|
|
6855
7006
|
this.motionTimers = /* @__PURE__ */ new Set();
|
|
6856
7007
|
// state
|
|
6857
7008
|
this.currency = "USD";
|
|
7009
|
+
this.eventTimezone = null;
|
|
7010
|
+
this.offerAvailability = null;
|
|
6858
7011
|
this.hold = null;
|
|
6859
7012
|
/** Latest server expiry for the open hold (moves on extend). */
|
|
6860
7013
|
this.holdExpiresAt = 0;
|
|
@@ -6968,6 +7121,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
6968
7121
|
if (!options.event || typeof options.event !== "string") throw new Error("seatmap: `event` key is required");
|
|
6969
7122
|
if (!options.container) throw new Error("seatmap: `container` is required (or use SeatPicker.open())");
|
|
6970
7123
|
this.opts = { ...options, confirmSelection: options.confirmSelection ?? true };
|
|
7124
|
+
this.hostPricing = options.pricing;
|
|
6971
7125
|
this.apiBase = (options.apiBase ?? DEFAULT_API_BASE2).replace(/\/+$/, "");
|
|
6972
7126
|
this.access = options.transport ? null : createBuyerAccessContext(options, {
|
|
6973
7127
|
onExpired: (event) => {
|
|
@@ -7009,6 +7163,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
7009
7163
|
},
|
|
7010
7164
|
onStatusChange: () => {
|
|
7011
7165
|
this.syncPrices();
|
|
7166
|
+
this.scheduleOfferRefresh(true);
|
|
7012
7167
|
this.evictTakenSelections();
|
|
7013
7168
|
this.detectBooked();
|
|
7014
7169
|
this.refreshMinimap();
|
|
@@ -7430,6 +7585,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
7430
7585
|
</div>
|
|
7431
7586
|
<div class="sl-sec sl-filtersec" data-ref="filtersSec">Filters</div>
|
|
7432
7587
|
<div class="sl-filters" data-ref="filters"></div>
|
|
7588
|
+
<div class="sl-offer" data-ref="offer" role="status" aria-live="polite"></div>
|
|
7433
7589
|
<div class="sl-sec sl-prices-sec" data-ref="pricesSec"><span>Ticket prices</span></div>
|
|
7434
7590
|
<div class="sl-prices" data-ref="prices"></div>
|
|
7435
7591
|
<div class="sl-live" data-ref="live" role="status" aria-live="polite"><span class="dot" aria-hidden="true"></span><span data-ref="liveText">Live availability \u2014 seats update in real time</span></div>
|
|
@@ -7450,6 +7606,13 @@ var SeatPicker = class _SeatPicker {
|
|
|
7450
7606
|
this.els[el2.dataset.ref] = el2;
|
|
7451
7607
|
});
|
|
7452
7608
|
this.mapHost = this.els.map;
|
|
7609
|
+
void this.refreshOfferAvailability(false);
|
|
7610
|
+
if (this.api.availability) {
|
|
7611
|
+
this.offerVisibilityHandler = () => {
|
|
7612
|
+
if (!document.hidden && !this.destroyed) void this.refreshOfferAvailability(false);
|
|
7613
|
+
};
|
|
7614
|
+
document.addEventListener("visibilitychange", this.offerVisibilityHandler);
|
|
7615
|
+
}
|
|
7453
7616
|
const applyLayout = () => {
|
|
7454
7617
|
const w = root.clientWidth;
|
|
7455
7618
|
if (w <= 0) return;
|
|
@@ -7569,6 +7732,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
7569
7732
|
const chartTheme = this.controller.doc?.theme;
|
|
7570
7733
|
Object.entries(resolveTokens(chartTheme, this.opts.theme)).forEach(([k, v]) => root.style.setProperty(k, v));
|
|
7571
7734
|
this.currency = info.currency ?? this.opts.currency ?? "USD";
|
|
7735
|
+
this.eventTimezone = info.timezone ?? null;
|
|
7572
7736
|
const logoUrl = this.opts.theme?.logoUrl ?? chartTheme?.logoUrl;
|
|
7573
7737
|
if (logoUrl) this.els.logo.innerHTML = `<img src="${logoUrl}" alt="">`;
|
|
7574
7738
|
else this.els.logo.textContent = (this.opts.theme?.brandName ?? chartTheme?.brandName ?? info.eventName ?? "?").slice(0, 1).toUpperCase();
|
|
@@ -8909,6 +9073,119 @@ var SeatPicker = class _SeatPicker {
|
|
|
8909
9073
|
return `${n} ${this.currency}`;
|
|
8910
9074
|
}
|
|
8911
9075
|
}
|
|
9076
|
+
/**
|
|
9077
|
+
* Sleep until the offer schedule's next known transition, then re-read.
|
|
9078
|
+
*
|
|
9079
|
+
* A far-away boundary is capped: the wake re-reads, learns the (unchanged)
|
|
9080
|
+
* schedule, and re-arms — so a picker left open for days still tracks an
|
|
9081
|
+
* organizer's schedule edits at a cost of one request every few hours. No
|
|
9082
|
+
* future transition means no timer at all; an event with no releases does
|
|
9083
|
+
* zero background traffic. A wake in a hidden tab fetches nothing — the
|
|
9084
|
+
* visibilitychange handler owns catching that tab up.
|
|
9085
|
+
*/
|
|
9086
|
+
scheduleOfferBoundary(availability) {
|
|
9087
|
+
if (this.offerBoundaryTimer) clearTimeout(this.offerBoundaryTimer);
|
|
9088
|
+
this.offerBoundaryTimer = null;
|
|
9089
|
+
if (!this.api.availability || this.destroyed) return;
|
|
9090
|
+
const now = Date.now();
|
|
9091
|
+
const boundary = nextOfferTransitionAt(availability, now);
|
|
9092
|
+
if (boundary == null) return;
|
|
9093
|
+
const MAX_SLEEP_MS = 6 * 36e5;
|
|
9094
|
+
const delay = Math.min(Math.max(boundary - now + 1e3, 1e3), MAX_SLEEP_MS);
|
|
9095
|
+
this.offerBoundaryTimer = setTimeout(() => {
|
|
9096
|
+
this.offerBoundaryTimer = null;
|
|
9097
|
+
if (document.hidden) return;
|
|
9098
|
+
void this.refreshOfferAvailability(false);
|
|
9099
|
+
}, delay);
|
|
9100
|
+
}
|
|
9101
|
+
/** Debounce the no-store offer read behind a burst of seat-status frames. */
|
|
9102
|
+
scheduleOfferRefresh(live) {
|
|
9103
|
+
if (!this.api.availability || this.destroyed) return;
|
|
9104
|
+
if (this.offerRefreshTimer) clearTimeout(this.offerRefreshTimer);
|
|
9105
|
+
this.offerRefreshTimer = setTimeout(() => {
|
|
9106
|
+
this.offerRefreshTimer = null;
|
|
9107
|
+
void this.refreshOfferAvailability(live);
|
|
9108
|
+
}, live ? 180 : 0);
|
|
9109
|
+
}
|
|
9110
|
+
/**
|
|
9111
|
+
* Pull the server's resolved answer. A failed refresh keeps the last truthful
|
|
9112
|
+
* answer: flashing back to a chart price while checkout still charges an
|
|
9113
|
+
* offer is worse than a temporarily stale remaining count.
|
|
9114
|
+
*/
|
|
9115
|
+
async refreshOfferAvailability(live) {
|
|
9116
|
+
if (!this.api.availability || this.destroyed) return;
|
|
9117
|
+
try {
|
|
9118
|
+
const body = await this.api.availability(this.opts.event, live);
|
|
9119
|
+
if (this.destroyed) return;
|
|
9120
|
+
const availability = parseTicketOfferAvailability(body);
|
|
9121
|
+
if (!availability) return;
|
|
9122
|
+
this.offerAvailability = availability;
|
|
9123
|
+
this.scheduleOfferBoundary(availability);
|
|
9124
|
+
const server = ticketOfferPrices(availability);
|
|
9125
|
+
const merged = { ...this.hostPricing?.prices ?? {}, ...server };
|
|
9126
|
+
const pricing = Object.keys(merged).length > 0 || this.hostPricing?.formatter ? { prices: merged, ...this.hostPricing?.formatter ? { formatter: this.hostPricing.formatter } : {} } : void 0;
|
|
9127
|
+
this.setPricing(pricing);
|
|
9128
|
+
this.syncOffer();
|
|
9129
|
+
this.opts.onOfferAvailabilityChange?.(availability);
|
|
9130
|
+
} catch {
|
|
9131
|
+
if (!this.destroyed && !this.offerBoundaryTimer && !document.hidden) {
|
|
9132
|
+
this.offerBoundaryTimer = setTimeout(() => {
|
|
9133
|
+
this.offerBoundaryTimer = null;
|
|
9134
|
+
if (document.hidden) return;
|
|
9135
|
+
void this.refreshOfferAvailability(false);
|
|
9136
|
+
}, 3e4);
|
|
9137
|
+
}
|
|
9138
|
+
}
|
|
9139
|
+
}
|
|
9140
|
+
offerPrice(categoryKey) {
|
|
9141
|
+
if (!categoryKey) return null;
|
|
9142
|
+
return this.offerAvailability?.prices.find((entry) => entry.categoryKey === categoryKey) ?? null;
|
|
9143
|
+
}
|
|
9144
|
+
/** The compact current/upcoming offer card above Ticket prices. */
|
|
9145
|
+
syncOffer() {
|
|
9146
|
+
const host = this.els.offer;
|
|
9147
|
+
if (!host) return;
|
|
9148
|
+
const availability = this.offerAvailability;
|
|
9149
|
+
const active = availability?.release ?? null;
|
|
9150
|
+
const upcoming = !active ? availability?.upcoming ?? null : null;
|
|
9151
|
+
if (!availability || availability.state === "closed" || availability.state === "sold-out" || !active && !upcoming) {
|
|
9152
|
+
host.classList.remove("has");
|
|
9153
|
+
host.replaceChildren();
|
|
9154
|
+
return;
|
|
9155
|
+
}
|
|
9156
|
+
const offer = active ?? upcoming;
|
|
9157
|
+
const main = document.createElement("div");
|
|
9158
|
+
main.className = "sl-offer-main";
|
|
9159
|
+
const copy = document.createElement("div");
|
|
9160
|
+
copy.className = "sl-offer-copy";
|
|
9161
|
+
const kicker = document.createElement("span");
|
|
9162
|
+
kicker.className = "sl-offer-kicker";
|
|
9163
|
+
kicker.textContent = active ? "Current ticket offer" : "Upcoming ticket offer";
|
|
9164
|
+
const name = document.createElement("strong");
|
|
9165
|
+
name.className = "sl-offer-name";
|
|
9166
|
+
name.textContent = offer.name || (active ? "Current offer" : "Scheduled offer");
|
|
9167
|
+
const line = document.createElement("span");
|
|
9168
|
+
line.className = "sl-offer-line";
|
|
9169
|
+
const facts = [];
|
|
9170
|
+
if (active && availability.fromPrice != null) facts.push(this.money(availability.fromPrice / 100));
|
|
9171
|
+
if (active && offer.remaining != null) facts.push(`${offer.remaining} available`);
|
|
9172
|
+
if (active && offer.endsAt != null) facts.push(`until ${formatWhen(offer.endsAt, this.eventTimezone, this.opts.locale)}`);
|
|
9173
|
+
if (upcoming?.startsAt != null) facts.push(`starts ${formatWhen(upcoming.startsAt, this.eventTimezone, this.opts.locale)}`);
|
|
9174
|
+
line.textContent = facts.join(" \xB7 ");
|
|
9175
|
+
copy.append(kicker, name, line);
|
|
9176
|
+
const info = document.createElement("details");
|
|
9177
|
+
info.className = "sl-offer-info";
|
|
9178
|
+
const summary = document.createElement("summary");
|
|
9179
|
+
summary.setAttribute("aria-label", `How the ${name.textContent} offer works`);
|
|
9180
|
+
summary.textContent = "i";
|
|
9181
|
+
const detail = document.createElement("div");
|
|
9182
|
+
detail.className = "sl-offer-detail";
|
|
9183
|
+
detail.textContent = active ? `This price applies automatically to eligible seats. Tickets in active carts temporarily reduce the available quantity; released or expired holds return it. When the offer ends, the next matching offer or normal ticket price takes over.` : `Tickets are available at their normal price now. This scheduled offer will apply automatically to eligible seats when it starts.`;
|
|
9184
|
+
info.append(summary, detail);
|
|
9185
|
+
main.append(copy, info);
|
|
9186
|
+
host.replaceChildren(main);
|
|
9187
|
+
host.classList.add("has");
|
|
9188
|
+
}
|
|
8912
9189
|
/**
|
|
8913
9190
|
* The price the buyer will actually pay for a category (+tier): the host's
|
|
8914
9191
|
* `pricing` override when present, else the chart's stored price. Every
|
|
@@ -8935,9 +9212,11 @@ var SeatPicker = class _SeatPicker {
|
|
|
8935
9212
|
this.els.prices.classList.toggle("sl-expanded", overflow > 1 && this.pricesExpanded);
|
|
8936
9213
|
this.els.prices.innerHTML = shown.map((c) => {
|
|
8937
9214
|
const price = this.catPrice(c);
|
|
9215
|
+
const offer = this.offerPrice(c.key);
|
|
9216
|
+
const previous = offer?.previousPrice != null && offer.previousPrice > offer.price ? offer.previousPrice : null;
|
|
8938
9217
|
const active = this.focusedCatKey === c.key;
|
|
8939
9218
|
const dim = this.priceBandKeys != null && !this.priceBandKeys.has(c.key);
|
|
8940
|
-
return `<div class="sl-price-row${dim ? " sl-dim" : ""}${active ? " sl-active" : ""}" data-cat="${c.key}" role="button" tabindex="0" aria-pressed="${active}" title="${active ? "Show all seats" : `Show ${c.label} seats on the map`}"><span class="sl-dot" style="background:${c.color}"></span><span class="sl-price-label">${c.label}</span><span class="sl-price-left">${left[c.key] ?? 0} left</span>` + (price != null ? `<span class="sl-price-amt">${this.money(price)}</span>` : "") + `</div>`;
|
|
9219
|
+
return `<div class="sl-price-row${dim ? " sl-dim" : ""}${active ? " sl-active" : ""}" data-cat="${escapeOption(c.key)}" role="button" tabindex="0" aria-pressed="${active}" title="${escapeOption(active ? "Show all seats" : `Show ${c.label} seats on the map`)}"><span class="sl-dot" style="background:${escapeOption(c.color)}"></span><span class="sl-price-label">${escapeOption(c.label)}` + (offer?.offerName ? `<small class="sl-price-offer">${escapeOption(offer.offerName)}</small>` : "") + `</span><span class="sl-price-left">${left[c.key] ?? 0} left</span>` + (previous != null ? `<span class="sl-price-was">${escapeOption(this.money(previous))}</span>` : "") + (price != null ? `<span class="sl-price-amt">${this.money(price)}</span>` : "") + `</div>`;
|
|
8941
9220
|
}).join("") + (overflow > 1 ? `<button type="button" class="sl-price-more" aria-expanded="${!collapsed}">` + (collapsed ? `Show all ${doc.categories.length} ticket types` : "Show fewer") + `</button>` : "") + `<div class="sl-status-key" aria-label="Seat status legend"><span class="sl-status-item"><i class="sl-status-icon" aria-hidden="true"><svg viewBox="0 0 24 24"><rect x="5" y="10" width="14" height="10" rx="2"/><path d="M8 10V7a4 4 0 0 1 8 0v3"/></svg></i>Temporarily held</span><span class="sl-status-item"><i class="sl-status-icon sold" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="M7 17L17 7"/></svg></i>Sold</span></div>`;
|
|
8942
9221
|
this.els.prices.querySelectorAll(".sl-price-row").forEach((row) => {
|
|
8943
9222
|
row.addEventListener("mouseenter", () => this.controller.getRenderer()?.setCategoryHighlight?.(row.dataset.cat ?? null));
|
|
@@ -9593,6 +9872,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
9593
9872
|
}
|
|
9594
9873
|
emitHoldChange() {
|
|
9595
9874
|
const hold = this.hold;
|
|
9875
|
+
this.scheduleOfferRefresh(true);
|
|
9596
9876
|
this.opts.onHoldChange?.(
|
|
9597
9877
|
hold,
|
|
9598
9878
|
hold?.seats ?? [],
|
|
@@ -10322,6 +10602,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
10322
10602
|
flashOnLiveChange: true,
|
|
10323
10603
|
onStatusChange: () => {
|
|
10324
10604
|
this.syncPrices();
|
|
10605
|
+
this.scheduleOfferRefresh(true);
|
|
10325
10606
|
this.detectBooked();
|
|
10326
10607
|
this.refreshMinimap();
|
|
10327
10608
|
this.pushAvailabilityTo3d();
|
|
@@ -10457,6 +10738,14 @@ var SeatPicker = class _SeatPicker {
|
|
|
10457
10738
|
this.stopHoldTimer();
|
|
10458
10739
|
if (this.toastTimer) clearTimeout(this.toastTimer);
|
|
10459
10740
|
if (this.liveTimer) clearTimeout(this.liveTimer);
|
|
10741
|
+
if (this.offerRefreshTimer) clearTimeout(this.offerRefreshTimer);
|
|
10742
|
+
if (this.offerBoundaryTimer) clearTimeout(this.offerBoundaryTimer);
|
|
10743
|
+
this.offerRefreshTimer = null;
|
|
10744
|
+
this.offerBoundaryTimer = null;
|
|
10745
|
+
if (this.offerVisibilityHandler) {
|
|
10746
|
+
document.removeEventListener("visibilitychange", this.offerVisibilityHandler);
|
|
10747
|
+
this.offerVisibilityHandler = null;
|
|
10748
|
+
}
|
|
10460
10749
|
for (const timer of this.motionTimers) clearTimeout(timer);
|
|
10461
10750
|
this.motionTimers.clear();
|
|
10462
10751
|
this.ro?.disconnect();
|
|
@@ -13131,10 +13420,12 @@ init_manageApi();
|
|
|
13131
13420
|
markerOf,
|
|
13132
13421
|
mutationCount,
|
|
13133
13422
|
needsMoveConfirmation,
|
|
13423
|
+
parseTicketOfferAvailability,
|
|
13134
13424
|
planAssignment,
|
|
13135
13425
|
retryAfterCopy,
|
|
13136
13426
|
selectionSources,
|
|
13137
13427
|
stateBadge,
|
|
13138
|
-
suggestMarker
|
|
13428
|
+
suggestMarker,
|
|
13429
|
+
ticketOfferPrices
|
|
13139
13430
|
});
|
|
13140
13431
|
//# sourceMappingURL=index.cjs.map
|