@seatlayer/js 0.47.2 → 0.48.1

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.
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  ManageApi,
3
3
  ManageApiError
4
- } from "./chunk-X2R4AQSZ.js";
4
+ } from "./chunk-H4OJF6LE.js";
5
5
 
6
6
  // src/SeatManager.ts
7
7
  import {
@@ -12,6 +12,108 @@ import {
12
12
  gaUnitLabels,
13
13
  UNGROUPED_ID
14
14
  } from "@seatlayer/core";
15
+
16
+ // src/manageAssets.ts
17
+ var SAFE_ASSET = /^[a-zA-Z0-9._-]+$/;
18
+ function organizerEventAssetReference(value) {
19
+ let url;
20
+ try {
21
+ url = new URL(value, "https://seatlayer.invalid");
22
+ } catch {
23
+ return null;
24
+ }
25
+ if (url.search || url.hash) return null;
26
+ const match = /^\/v1\/events\/([^/]+)\/assets\/([^/]+)$/.exec(url.pathname);
27
+ if (!match) return null;
28
+ try {
29
+ const eventKey = decodeURIComponent(match[1]);
30
+ const asset = decodeURIComponent(match[2]);
31
+ if (!eventKey || !SAFE_ASSET.test(asset)) return null;
32
+ return { eventKey, asset };
33
+ } catch {
34
+ return null;
35
+ }
36
+ }
37
+ function looksLikeOrganizerAsset(value) {
38
+ try {
39
+ return /^\/v1\/events\/[^/]+\/assets(?:\/|$)/.test(
40
+ new URL(value, "https://seatlayer.invalid").pathname
41
+ );
42
+ } catch {
43
+ return false;
44
+ }
45
+ }
46
+ var OrganizerAssetObjectUrls = class {
47
+ constructor(eventKey, load) {
48
+ this.eventKey = eventKey;
49
+ this.load = load;
50
+ this.pending = /* @__PURE__ */ new Map();
51
+ this.created = /* @__PURE__ */ new Set();
52
+ this.disposed = false;
53
+ }
54
+ resolve(reference) {
55
+ const parsed = organizerEventAssetReference(reference);
56
+ if (!parsed) {
57
+ return Promise.resolve(looksLikeOrganizerAsset(reference) ? null : reference);
58
+ }
59
+ if (parsed.eventKey !== this.eventKey || this.disposed) return Promise.resolve(null);
60
+ const cacheKey = `${parsed.eventKey}/${parsed.asset}`;
61
+ const existing = this.pending.get(cacheKey);
62
+ if (existing) return existing;
63
+ const task = this.load(parsed.eventKey, parsed.asset).then((blob) => {
64
+ const objectUrl = URL.createObjectURL(blob);
65
+ if (this.disposed) {
66
+ URL.revokeObjectURL(objectUrl);
67
+ return null;
68
+ }
69
+ this.created.add(objectUrl);
70
+ return objectUrl;
71
+ }).catch((error) => {
72
+ this.pending.delete(cacheKey);
73
+ throw error;
74
+ });
75
+ this.pending.set(cacheKey, task);
76
+ return task;
77
+ }
78
+ /**
79
+ * Resolve the image fields the synchronous map renderer loads immediately.
80
+ * View-from-seat media stays lazy: SeatManager does not open that buyer
81
+ * surface, and eagerly downloading every row panorama would be unbounded.
82
+ */
83
+ async prepareRendererChart(doc) {
84
+ const prepareBackground = async (background) => {
85
+ if (!background?.url) return;
86
+ const resolved = await this.resolve(background.url);
87
+ if (!resolved) throw new Error("organizer_event_asset_scope_mismatch");
88
+ background.url = resolved;
89
+ };
90
+ const prepareObjects = async (objects) => {
91
+ for (const object of objects) {
92
+ if (object.type !== "decorImage") continue;
93
+ const image = object;
94
+ const resolved = await this.resolve(image.href);
95
+ if (!resolved) throw new Error("organizer_event_asset_scope_mismatch");
96
+ image.href = resolved;
97
+ }
98
+ };
99
+ const prepareOwner = async (owner) => {
100
+ await prepareBackground(owner.backgroundImage);
101
+ await prepareObjects(owner.objects);
102
+ };
103
+ await prepareOwner(doc);
104
+ for (const floor of doc.floors ?? []) await prepareOwner(floor);
105
+ return doc;
106
+ }
107
+ dispose() {
108
+ if (this.disposed) return;
109
+ this.disposed = true;
110
+ for (const url of this.created) URL.revokeObjectURL(url);
111
+ this.created.clear();
112
+ this.pending.clear();
113
+ }
114
+ };
115
+
116
+ // src/SeatManager.ts
15
117
  function availabilityModeOf(rule) {
16
118
  return rule ? rule.mode : "open";
17
119
  }
@@ -290,8 +392,8 @@ var MANAGER_CSS = (
290
392
  .slm.compact .slm-modes{min-width:0}.slm.compact .slm-mode{padding-inline:11px}
291
393
  .slm.compact .slm-live{justify-self:end}.slm.compact .slm-bar-actions{grid-column:1/-1;justify-self:stretch}
292
394
  .slm.compact .slm-barbtn{flex:1;padding:6px 9px}.slm.compact .slm-kpis{grid-template-columns:repeat(4,minmax(0,1fr));gap:8px}
293
- .slm.compact .slm-kpi[data-kpi="buyers"],.slm.compact .slm-kpi[data-kpi="active-holds"],
294
- .slm.compact .slm-kpi[data-kpi="sold-pct"],.slm.compact .slm-kpi[data-kpi="gross-sales"]{display:none}
395
+ .slm.compact .slm-kpi[data-kpi="viewing-map"],.slm.compact .slm-kpi[data-kpi="active-holds"],
396
+ .slm.compact .slm-kpi[data-kpi="booked-pct"],.slm.compact .slm-kpi[data-kpi="booked-value"]{display:none}
295
397
  /* Reduced motion, as a BLANKET over the cockpit subtree rather than a list of
296
398
  selectors. The list this replaces named four animations and two transitions,
297
399
  and had silently fallen behind the stylesheet: the zoom hint, the toast and
@@ -501,6 +603,10 @@ var SeatManager = class {
501
603
  this.currency = options.currency ?? "USD";
502
604
  this.tokenExpiresAt = options.tokenExpiresAt ?? null;
503
605
  this.api = new ManageApi(options.apiBase ?? DEFAULT_API_BASE, options.token);
606
+ this.organizerAssetUrls = new OrganizerAssetObjectUrls(
607
+ this.key,
608
+ (key, asset) => this.withAuthRetry(() => this.api.asset(key, asset))
609
+ );
504
610
  this.host = resolveContainer(options.container);
505
611
  }
506
612
  /** Build the DOM, load the chart, subscribe to realtime, mount the board. */
@@ -508,10 +614,10 @@ var SeatManager = class {
508
614
  injectStyle();
509
615
  this.buildChrome();
510
616
  try {
511
- const res = await this.api.chart(this.key);
512
- this.doc = res.doc;
617
+ const res = await this.withAuthRetry(() => this.api.chart(this.key));
618
+ this.doc = await this.organizerAssetUrls.prepareRendererChart(res.doc);
513
619
  this.currency = res.event.currency ?? this.opts.currency ?? this.currency;
514
- this.buildUnitUniverse(res.doc);
620
+ this.buildUnitUniverse(this.doc);
515
621
  this.buildRenderer();
516
622
  this.buildSectionOptions();
517
623
  const [, controlRoom] = await Promise.all([
@@ -615,7 +721,7 @@ var SeatManager = class {
615
721
  if (this.channels) return Promise.resolve();
616
722
  const load = (async () => {
617
723
  try {
618
- const mod = await import("./channelsMode-33ZLORVF.js");
724
+ const mod = await import("./channelsMode-NCH5GYHN.js");
619
725
  if (this.closed) return;
620
726
  if (!this.channelCaps.view) return;
621
727
  if (!this.channels) {
@@ -858,10 +964,10 @@ var SeatManager = class {
858
964
  try {
859
965
  await this.api.unbook(this.key, targets, bookingRef);
860
966
  this.clearSelection();
861
- this.done("cancelBooking", targets, `Cancelled ${targets.length} booking${targets.length === 1 ? "" : "s"}.`);
967
+ this.done("cancelBooking", targets, `Released ${targets.length} booked unit${targets.length === 1 ? "" : "s"}.`);
862
968
  } catch (err) {
863
969
  this.setSeatsLocal(targets, "booked");
864
- this.toastErr("Couldn't cancel that booking. Check the reference.");
970
+ this.toastErr("Couldn't release that booked inventory. Check the booking reference.");
865
971
  this.opts.onError?.(err);
866
972
  }
867
973
  }
@@ -914,9 +1020,9 @@ var SeatManager = class {
914
1020
  async setHoldTtl(ms) {
915
1021
  try {
916
1022
  await this.api.setHoldTtl(this.key, ms);
917
- this.done("setHoldTtl", [], ms ? `Checkout window set to ${Math.round(ms / 6e4)} min.` : "Checkout window reset.");
1023
+ this.done("setHoldTtl", [], ms ? `Hold window set to ${Math.round(ms / 6e4)} min.` : "Hold window reset.");
918
1024
  } catch (err) {
919
- this.toastErr("Couldn't update the checkout window.");
1025
+ this.toastErr("Couldn't update the hold window.");
920
1026
  this.opts.onError?.(err);
921
1027
  }
922
1028
  }
@@ -961,6 +1067,7 @@ var SeatManager = class {
961
1067
  }
962
1068
  this.renderer?.destroy();
963
1069
  this.renderer = null;
1070
+ this.organizerAssetUrls.dispose();
964
1071
  if (this.root && this.root.parentNode === this.host) this.host.removeChild(this.root);
965
1072
  }
966
1073
  // ---- renderer lifecycle ---------------------------------------------------
@@ -1081,24 +1188,28 @@ var SeatManager = class {
1081
1188
  * projects its deltas, so any change inside a private channel allocation is
1082
1189
  * structurally suppressed and the map silently drifts.
1083
1190
  *
1084
- * If the mint fails (an expired token, a worker that predates the route) we
1085
- * still connect unticketed rather than going dark the public-sale stream is
1086
- * worth having, and every `resnapshot()` re-establishes physical truth from
1087
- * the authenticated HTTP read.
1191
+ * If the mint fails, remain reconnecting. An unticketed socket is a buyer
1192
+ * projection, so applying it to organizer state would be worse than staying
1193
+ * visibly offline while the host refreshes authority or upgrades the API.
1088
1194
  */
1089
1195
  async connect() {
1090
1196
  if (this.closed) return;
1091
1197
  let protocols;
1092
1198
  try {
1093
- protocols = (await this.api.subscribeTicket(this.key)).protocols;
1094
- } catch {
1095
- protocols = void 0;
1199
+ protocols = (await this.withAuthRetry(() => this.api.subscribeTicket(this.key))).protocols;
1200
+ if (!protocols.length) throw new Error("manage_subscribe_ticket_missing");
1201
+ } catch (err) {
1202
+ this.setLive(false);
1203
+ this.opts.onError?.(err);
1204
+ this.scheduleReconnect();
1205
+ return;
1096
1206
  }
1097
1207
  if (this.closed) return;
1098
1208
  let ws;
1099
1209
  try {
1100
- ws = protocols ? new WebSocket(this.api.socketUrl(this.key), protocols) : new WebSocket(this.api.socketUrl(this.key));
1101
- } catch {
1210
+ ws = new WebSocket(this.api.socketUrl(this.key), protocols);
1211
+ } catch (err) {
1212
+ this.opts.onError?.(err);
1102
1213
  this.scheduleReconnect();
1103
1214
  return;
1104
1215
  }
@@ -1189,8 +1300,9 @@ var SeatManager = class {
1189
1300
  this.lastSyncedAt = Date.now();
1190
1301
  this.afterPaint();
1191
1302
  }
1192
- if (typeof m.revenue?.gross === "number" && Number.isFinite(m.revenue.gross)) {
1193
- this.applyLiveGross(m.revenue.gross);
1303
+ const liveBookedValue = typeof m.bookedValue?.gross === "number" ? m.bookedValue.gross : m.revenue?.gross;
1304
+ if (typeof liveBookedValue === "number" && Number.isFinite(liveBookedValue)) {
1305
+ this.applyLiveGross(liveBookedValue);
1194
1306
  }
1195
1307
  this.recomputeTallies();
1196
1308
  }
@@ -1207,9 +1319,12 @@ var SeatManager = class {
1207
1319
  this.authoritativeGrossRevenue = gross;
1208
1320
  this.revenueStatus = "current";
1209
1321
  if (this.controlRoomSnapshot) {
1322
+ const current = this.controlRoomSnapshot.bookedValue ?? this.controlRoomSnapshot.revenue;
1323
+ const bookedValue = { ...current, gross };
1210
1324
  this.controlRoomSnapshot = {
1211
1325
  ...this.controlRoomSnapshot,
1212
- revenue: { ...this.controlRoomSnapshot.revenue, gross }
1326
+ bookedValue,
1327
+ revenue: bookedValue
1213
1328
  };
1214
1329
  this.opts.onControlRoom?.(this.controlRoomSnapshot);
1215
1330
  }
@@ -1387,7 +1502,10 @@ var SeatManager = class {
1387
1502
  // ---- tallies + feed -------------------------------------------------------
1388
1503
  applyReportRevenue(report) {
1389
1504
  this.authoritativeGrossRevenue = report.report.byCategory.reduce(
1390
- (sum, row) => sum + (Number.isFinite(row.bookedRevenue) ? row.bookedRevenue : 0),
1505
+ (sum, row) => {
1506
+ const value = Number.isFinite(row.bookedValue) ? row.bookedValue : row.bookedRevenue;
1507
+ return sum + (Number.isFinite(value) ? value : 0);
1508
+ },
1391
1509
  0
1392
1510
  );
1393
1511
  this.revenueStatus = "current";
@@ -1406,7 +1524,13 @@ var SeatManager = class {
1406
1524
  const requestedAt = Date.now();
1407
1525
  try {
1408
1526
  const fetched = await this.api.controlRoom(this.key, this.trendWindowMinutes);
1409
- let snapshot = fetched;
1527
+ const incoming = fetched.bookedValue ?? fetched.revenue ?? { gross: 0, bySection: [] };
1528
+ const normalizedSections = (incoming.bySection ?? []).map((row) => {
1529
+ const value = Number.isFinite(row.bookedValue) ? row.bookedValue : row.bookedRevenue;
1530
+ return { ...row, bookedValue: value ?? 0, bookedRevenue: value ?? 0 };
1531
+ });
1532
+ const canonical = { ...incoming, bySection: normalizedSections };
1533
+ let snapshot = { ...fetched, bookedValue: canonical, revenue: canonical };
1410
1534
  if (request === this.revenueRequest) {
1411
1535
  if (this.livePresence && this.livePresence.at >= requestedAt) {
1412
1536
  snapshot = { ...snapshot, presence: this.livePresence.value };
@@ -1414,14 +1538,15 @@ var SeatManager = class {
1414
1538
  this.livePresence = null;
1415
1539
  }
1416
1540
  if (this.liveGross && this.liveGross.at >= requestedAt) {
1417
- snapshot = { ...snapshot, revenue: { ...snapshot.revenue, gross: this.liveGross.value } };
1541
+ const bookedValue = { ...snapshot.bookedValue, gross: this.liveGross.value };
1542
+ snapshot = { ...snapshot, bookedValue, revenue: bookedValue };
1418
1543
  } else {
1419
1544
  this.liveGross = null;
1420
1545
  }
1421
1546
  this.controlRoomSnapshot = snapshot;
1422
1547
  this.rebaseServerTotals(snapshot);
1423
1548
  this.lastSyncedAt = Date.now();
1424
- this.authoritativeGrossRevenue = snapshot.revenue.gross;
1549
+ this.authoritativeGrossRevenue = snapshot.bookedValue.gross;
1425
1550
  this.currency = snapshot.currency;
1426
1551
  this.revenueStatus = "current";
1427
1552
  this.recomputeTallies();
@@ -1479,7 +1604,9 @@ var SeatManager = class {
1479
1604
  total: Number.isFinite(seatTotal) ? seatTotal : this.unitTotal(),
1480
1605
  capacityPct: 0,
1481
1606
  sellThroughPct: 0,
1607
+ bookedValue: this.authoritativeGrossRevenue,
1482
1608
  grossRevenue: this.authoritativeGrossRevenue,
1609
+ bookedValueStatus: this.revenueStatus,
1483
1610
  revenueStatus: this.revenueStatus,
1484
1611
  currency: this.currency
1485
1612
  };
@@ -1634,8 +1761,8 @@ var SeatManager = class {
1634
1761
  <button class="slm-barbtn follow" data-ref="follow" aria-pressed="false"
1635
1762
  title="Stay on the current map view unless enabled">Follow live</button>
1636
1763
  <button class="slm-barbtn" data-ref="heat" aria-pressed="false"
1637
- aria-label="Sales momentum overlay off"
1638
- title="Highlight sections selling fastest in the selected time window">Sales momentum</button>
1764
+ aria-label="Booking momentum overlay off"
1765
+ title="Highlight sections booking fastest in the selected time window">Booking momentum</button>
1639
1766
  <button class="slm-barbtn" data-ref="fullscreen" title="Full screen (F)" aria-keyshortcuts="F">Full screen</button>
1640
1767
  </div>
1641
1768
  <div class="slm-kpis" data-ref="kpis"></div>
@@ -1738,16 +1865,16 @@ var SeatManager = class {
1738
1865
  if (!button) return;
1739
1866
  button.classList.toggle("on", this.followLive);
1740
1867
  button.setAttribute("aria-pressed", String(this.followLive));
1741
- button.setAttribute("title", this.followLive ? "Following new buyer holds and bookings. Turn off to keep the current view." : "Stay on the current map view. Enable to follow new buyer holds and bookings.");
1868
+ button.setAttribute("title", this.followLive ? "Following new holds and bookings. Turn off to keep the current view." : "Stay on the current map view. Enable to follow new holds and bookings.");
1742
1869
  }
1743
1870
  paintHeatButton() {
1744
1871
  const button = this.els.heat;
1745
1872
  if (!button) return;
1746
1873
  button.classList.toggle("on", this.heatEnabled);
1747
1874
  button.setAttribute("aria-pressed", String(this.heatEnabled));
1748
- button.setAttribute("aria-label", `Sales momentum overlay ${this.heatEnabled ? "on" : "off"}`);
1749
- button.setAttribute("title", `${this.heatEnabled ? "Hide" : "Highlight"} sections selling fastest in the selected time window`);
1750
- button.textContent = "Sales momentum";
1875
+ button.setAttribute("aria-label", `Booking momentum overlay ${this.heatEnabled ? "on" : "off"}`);
1876
+ button.setAttribute("title", `${this.heatEnabled ? "Hide" : "Highlight"} sections booking fastest in the selected time window`);
1877
+ button.textContent = "Booking momentum";
1751
1878
  this.paintMomentumHelp();
1752
1879
  }
1753
1880
  paintMomentumHelp() {
@@ -1791,23 +1918,23 @@ var SeatManager = class {
1791
1918
  formatKpiDelta(key, delta, currency) {
1792
1919
  const sign = delta > 0 ? "+" : "\u2212";
1793
1920
  const absolute = Math.abs(delta);
1794
- if (key === "gross-sales") return `${sign}${fmtMoney(absolute, currency)}`;
1795
- if (key === "sold-pct") return `${sign}${absolute.toLocaleString()}pt`;
1921
+ if (key === "booked-value") return `${sign}${fmtMoney(absolute, currency)}`;
1922
+ if (key === "booked-pct") return `${sign}${absolute.toLocaleString()}pt`;
1796
1923
  return `${sign}${absolute.toLocaleString()}`;
1797
1924
  }
1798
1925
  paintKpis(t) {
1799
1926
  if (!this.els.kpis) return;
1800
- const rev = t.revenueStatus === "current" ? fmtMoney(t.grossRevenue, t.currency) : "\u2014";
1927
+ const bookedValue = t.bookedValueStatus === "current" ? fmtMoney(t.bookedValue, t.currency) : "\u2014";
1801
1928
  const presence = this.presenceCounts();
1802
1929
  const items = [
1803
- { key: "sold-seats", raw: t.booked, n: t.booked.toLocaleString(), l: "Sold seats", dot: "#22a06b", title: "Seats booked" },
1804
- { key: "held-seats", raw: t.held, n: t.held.toLocaleString(), l: "Held seats", dot: "#f4b740", title: "Seats held in a checkout right now" },
1805
- { key: "free-seats", raw: t.free, n: t.free.toLocaleString(), l: "Free seats", dot: "#6e7bff", title: "Seats on sale and unsold" },
1806
- { key: "blocked", raw: t.blocked, n: t.blocked.toLocaleString(), l: "Blocked", dot: "#8b94ac", title: "Seats withheld from sale" },
1807
- { key: "buyers", raw: presence?.shoppingSessions ?? null, n: presence ? presence.shoppingSessions.toLocaleString() : "\u2014", l: "Buyers", title: "People on the map right now" },
1808
- { key: "carts", raw: presence?.activeHolds ?? null, n: presence ? presence.activeHolds.toLocaleString() : "\u2014", l: "Carts", title: "Checkouts holding seats right now \u2014 sessions, not seats" },
1809
- { key: "sold-pct", raw: t.capacityPct, n: `${t.capacityPct}%`, l: "Sold", title: "Sold seats as a share of the whole event" },
1810
- { key: "gross-sales", raw: t.revenueStatus === "current" ? t.grossRevenue : null, n: rev, l: "Gross sales", title: "Exact booked gross" }
1930
+ { key: "booked-inventory", raw: t.booked, n: t.booked.toLocaleString(), l: "Booked inventory", dot: "#22a06b", title: "Inventory units booked" },
1931
+ { key: "held-seats", raw: t.held, n: t.held.toLocaleString(), l: "Held inventory", dot: "#f4b740", title: "Inventory currently held" },
1932
+ { key: "free-seats", raw: t.free, n: t.free.toLocaleString(), l: "Available", dot: "#6e7bff", title: "Inventory available to book" },
1933
+ { key: "blocked", raw: t.blocked, n: t.blocked.toLocaleString(), l: "Blocked", dot: "#8b94ac", title: "Inventory withheld from booking" },
1934
+ { key: "viewing-map", raw: presence?.shoppingSessions ?? null, n: presence ? presence.shoppingSessions.toLocaleString() : "\u2014", l: "Viewing map", title: "Active map sessions right now" },
1935
+ { key: "active-holds", raw: presence?.activeHolds ?? null, n: presence ? presence.activeHolds.toLocaleString() : "\u2014", l: "Active holds", title: "Sessions currently holding inventory" },
1936
+ { key: "booked-pct", raw: t.capacityPct, n: `${t.capacityPct}%`, l: "Booked", title: "Booked inventory as a share of the whole event" },
1937
+ { key: "booked-value", raw: t.bookedValueStatus === "current" ? t.bookedValue : null, n: bookedValue, l: "Booked value", title: "Configured value attached to booked inventory" }
1811
1938
  ];
1812
1939
  let hasChanges = false;
1813
1940
  this.els.kpis.innerHTML = items.map((item) => {
@@ -1856,12 +1983,12 @@ var SeatManager = class {
1856
1983
  renderViewRail() {
1857
1984
  this.els.rail.innerHTML = `
1858
1985
  <p class="slm-eyebrow">Monitor</p>
1859
- <p class="slm-hint">Read-only. Inventory, buyer presence and sales movement update on the same live board.</p>
1986
+ <p class="slm-hint">Read-only. Inventory, map activity and booking movement update on the same live board.</p>
1860
1987
  <div class="slm-health" data-ref="presence"></div>
1861
1988
  <div class="slm-legend" data-ref="legend"></div>
1862
1989
  <div class="slm-sectionhead">
1863
- <div><p class="slm-eyebrow">Section performance</p><p class="slm-note">Exact booked revenue \xB7 net sales velocity</p></div>
1864
- <div class="slm-windows" aria-label="Sales velocity window">
1990
+ <div><p class="slm-eyebrow">Section inventory</p><p class="slm-note">Configured booked value \xB7 booking momentum</p></div>
1991
+ <div class="slm-windows" aria-label="Booking momentum window">
1865
1992
  ${[5, 15, 30, 60].map((window) => `<button class="slm-window" data-window="${window}">${window}m</button>`).join("")}
1866
1993
  </div>
1867
1994
  </div>
@@ -1900,8 +2027,8 @@ var SeatManager = class {
1900
2027
  const sync = this.lastSyncedAt ? relTime(this.lastSyncedAt, Date.now()) : "waiting";
1901
2028
  const presence = this.presenceCounts();
1902
2029
  this.els.presence.innerHTML = `
1903
- <div class="slm-healthitem" title="People on the map right now"><b>${presence ? presence.shoppingSessions.toLocaleString() : "\u2014"}</b><span>Buyers</span></div>
1904
- <div class="slm-healthitem" title="Checkouts holding seats right now \u2014 sessions, not seats"><b>${presence ? presence.activeHolds.toLocaleString() : "\u2014"}</b><span>Carts</span></div>
2030
+ <div class="slm-healthitem" title="Active map sessions right now"><b>${presence ? presence.shoppingSessions.toLocaleString() : "\u2014"}</b><span>Viewing map</span></div>
2031
+ <div class="slm-healthitem" title="Sessions currently holding inventory"><b>${presence ? presence.activeHolds.toLocaleString() : "\u2014"}</b><span>Active holds</span></div>
1905
2032
  <div class="slm-healthitem"><b>${connected ? "Healthy" : "Reconnecting"}</b><span>Live connection</span></div>
1906
2033
  <div class="slm-healthitem"><b>${sync}</b><span>Last sync</span></div>`;
1907
2034
  }
@@ -1911,10 +2038,10 @@ var SeatManager = class {
1911
2038
  return;
1912
2039
  }
1913
2040
  const velocity = new Map(snapshot.velocity.bySection.map((row) => [row.sectionId, row]));
1914
- const rows = [...snapshot.revenue.bySection].sort((a, b) => {
2041
+ const rows = [...snapshot.bookedValue.bySection].sort((a, b) => {
1915
2042
  const av = velocity.get(a.sectionId)?.netBooked ?? 0;
1916
2043
  const bv = velocity.get(b.sectionId)?.netBooked ?? 0;
1917
- return bv - av || b.bookedRevenue - a.bookedRevenue;
2044
+ return bv - av || b.bookedValue - a.bookedValue;
1918
2045
  });
1919
2046
  this.els.sections.innerHTML = rows.length ? rows.map((row) => {
1920
2047
  const speed = velocity.get(row.sectionId);
@@ -1922,8 +2049,8 @@ var SeatManager = class {
1922
2049
  const netLabel = `${net > 0 ? "+" : ""}${net}`;
1923
2050
  const trend = speed?.trend === "rising" || speed?.trend === "cooling" ? speed.trend : "steady";
1924
2051
  return `<button type="button" class="slm-sectionrow" data-section-focus="${esc(row.sectionId)}" title="Focus ${esc(row.sectionLabel)} on the map">
1925
- <span class="slm-sectiontop"><span>${esc(row.sectionLabel)}</span><span>${fmtMoney(row.bookedRevenue, snapshot.currency)}</span></span>
1926
- <span class="slm-sectionmeta"><span>${row.booked.toLocaleString()}/${row.total.toLocaleString()} sold \xB7 ${netLabel} in ${snapshot.velocity.windowMinutes}m</span><span class="slm-trend ${trend}">${trend}</span><span class="slm-sectionlocate">Locate</span></span>
2052
+ <span class="slm-sectiontop"><span>${esc(row.sectionLabel)}</span><span>${fmtMoney(row.bookedValue, snapshot.currency)}</span></span>
2053
+ <span class="slm-sectionmeta"><span>${row.booked.toLocaleString()}/${row.total.toLocaleString()} booked \xB7 ${netLabel} in ${snapshot.velocity.windowMinutes}m</span><span class="slm-trend ${trend}">${trend}</span><span class="slm-sectionlocate">Locate</span></span>
1927
2054
  </button>`;
1928
2055
  }).join("") : '<div class="slm-empty">No section metrics are available for this chart.</div>';
1929
2056
  this.paintTrendWindow();
@@ -1935,7 +2062,7 @@ var SeatManager = class {
1935
2062
  this.renderer?.setSectionHeat(null);
1936
2063
  return;
1937
2064
  }
1938
- const capacity = new Map(snapshot.revenue.bySection.map((row) => [row.sectionId, Math.max(1, row.total)]));
2065
+ const capacity = new Map(snapshot.bookedValue.bySection.map((row) => [row.sectionId, Math.max(1, row.total)]));
1939
2066
  const rates = snapshot.velocity.bySection.map((row) => ({
1940
2067
  sectionId: row.sectionId,
1941
2068
  rate: Math.max(0, row.netBooked) / (capacity.get(row.sectionId) ?? 1) / snapshot.velocity.windowMinutes
@@ -1950,7 +2077,7 @@ var SeatManager = class {
1950
2077
  if (!seat) {
1951
2078
  this.els.rail.innerHTML = `
1952
2079
  <p class="slm-eyebrow">Inspect seats</p>
1953
- <p class="slm-hint">Select a seat to see its availability and sales context. Nothing changes in this view.</p>
2080
+ <p class="slm-hint">Select a seat to see its availability and booking context. Nothing changes in this view.</p>
1954
2081
  <div class="slm-empty">Select a seat on the map.</div>`;
1955
2082
  return;
1956
2083
  }
@@ -1959,7 +2086,7 @@ var SeatManager = class {
1959
2086
  const sectionId = this.sectionByObject.get(seat.rowId) ?? UNGROUPED_ID;
1960
2087
  const sectionLabel = this.sectionLabelById.get(sectionId) ?? "Other seats";
1961
2088
  const category = this.doc?.categories.find((item) => item.key === seat.categoryKey);
1962
- const sectionMetric = this.controlRoomSnapshot?.revenue.bySection.find((row) => row.sectionId === sectionId);
2089
+ const sectionMetric = this.controlRoomSnapshot?.bookedValue.bySection.find((row) => row.sectionId === sectionId);
1963
2090
  const object = this.doc?.objects.find((item) => item.id === seat.rowId);
1964
2091
  const location = object?.type === "row" ? { label: "Row", value: object.label } : object?.type === "table" ? { label: "Table", value: object.label } : seat.kind === "booth" ? { label: "Type", value: "Booth" } : null;
1965
2092
  const itemKind = seat.kind === "booth" ? "Booth" : "Seat";
@@ -1973,8 +2100,8 @@ var SeatManager = class {
1973
2100
  <div><span>Section</span><b>${esc(sectionLabel)}</b></div>
1974
2101
  ${location ? `<div><span>${location.label}</span><b>${esc(location.value)}</b></div>` : ""}
1975
2102
  <div><span>Category</span><b>${esc(category?.label ?? seat.categoryKey)}</b></div>
1976
- <div><span>Sold in section</span><b>${sectionMetric ? `${sectionMetric.booked} of ${sectionMetric.total}` : "\u2014"}</b></div>
1977
- <div><span>Section revenue</span><b>${sectionMetric && this.controlRoomSnapshot ? fmtMoney(sectionMetric.bookedRevenue, this.controlRoomSnapshot.currency) : "\u2014"}</b></div>
2103
+ <div><span>Booked in section</span><b>${sectionMetric ? `${sectionMetric.booked} of ${sectionMetric.total}` : "\u2014"}</b></div>
2104
+ <div><span>Section booked value</span><b>${sectionMetric && this.controlRoomSnapshot ? fmtMoney(sectionMetric.bookedValue, this.controlRoomSnapshot.currency) : "\u2014"}</b></div>
1978
2105
  </div>
1979
2106
  </div>`;
1980
2107
  }
@@ -2540,4 +2667,4 @@ var SeatManager = class {
2540
2667
  export {
2541
2668
  SeatManager
2542
2669
  };
2543
- //# sourceMappingURL=chunk-CF5MS7UR.js.map
2670
+ //# sourceMappingURL=chunk-Q5QT3YLA.js.map