@seatlayer/js 0.54.0 → 0.55.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/README.md +19 -5
- package/dist/{channelsMode-BX7s9bUl.d.cts → channelsMode-1b6xBWKM.d.cts} +132 -5
- package/dist/{channelsMode-BX7s9bUl.d.ts → channelsMode-1b6xBWKM.d.ts} +132 -5
- package/dist/{channelsMode-RUNPEHKD.js → channelsMode-LDIT556C.js} +1 -1
- package/dist/{chunk-7F6RSJRL.js → chunk-MZAMI4UR.js} +8 -13
- package/dist/index.cjs +4 -4
- package/dist/index.d.cts +88 -8
- package/dist/index.d.ts +88 -8
- package/dist/index.js +4 -4
- package/dist/manager.cjs +187 -116
- package/dist/manager.d.cts +2 -2
- package/dist/manager.d.ts +2 -2
- package/dist/manager.js +124 -48
- package/package.json +2 -2
package/dist/manager.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{A as he,C as pe,a as
|
|
1
|
+
import{A as he,C as pe,a as g,b as L,c as V,d as N,e as q,f as _,g as U,h as K,i as W,j as G,k as Z,l as Y,m as X,n as Q,o as J,p as ee,q as te,r as se,s as ie,t as oe,u as ae,v as le,w as ne,x as re,y as ce,z as de}from"./chunk-MZAMI4UR.js";import"./chunk-OSZ7DOEH.js";import{SeatmapRenderer as ye,expandChart as Se,computeSections as ke,floorsOf as we,gaAreasOf as xe,gaUnitLabels as Le,UNGROUPED_ID as f}from"@seatlayer/core";var me=/^[a-zA-Z0-9._-]+$/;function ue(c){let e;try{e=new URL(c,"https://seatlayer.invalid")}catch{return null}if(e.search||e.hash)return null;let t=/^\/v1\/events\/([^/]+)\/assets\/([^/]+)$/.exec(e.pathname);if(!t)return null;try{let s=decodeURIComponent(t[1]),i=decodeURIComponent(t[2]);return!s||!me.test(i)?null:{eventKey:s,asset:i}}catch{return null}}function be(c){try{return/^\/v1\/events\/[^/]+\/assets(?:\/|$)/.test(new URL(c,"https://seatlayer.invalid").pathname)}catch{return!1}}var S=class{constructor(e,t){this.eventKey=e;this.load=t;this.pending=new Map;this.created=new Set;this.disposed=!1}resolve(e){let t=ue(e);if(!t)return Promise.resolve(be(e)?null:e);if(t.eventKey!==this.eventKey||this.disposed)return Promise.resolve(null);let s=`${t.eventKey}/${t.asset}`,i=this.pending.get(s);if(i)return i;let o=this.load(t.eventKey,t.asset).then(a=>{let l=URL.createObjectURL(a);return this.disposed?(URL.revokeObjectURL(l),null):(this.created.add(l),l)}).catch(a=>{throw this.pending.delete(s),a});return this.pending.set(s,o),o}async prepareRendererChart(e){let t=async o=>{if(!o?.url)return;let a=await this.resolve(o.url);if(!a)throw new Error("organizer_event_asset_scope_mismatch");o.url=a},s=async o=>{for(let a of o){if(a.type!=="decorImage")continue;let l=a,r=await this.resolve(l.href);if(!r)throw new Error("organizer_event_asset_scope_mismatch");l.href=r}},i=async o=>{await t(o.backgroundImage),await s(o.objects)};await i(e);for(let o of e.floors??[])await i(o);return e}dispose(){if(!this.disposed){this.disposed=!0;for(let e of this.created)URL.revokeObjectURL(e);this.created.clear(),this.pending.clear()}}};var A="seatlayer-manager-style",$="seatlayer-manager-channels-style",fe=`
|
|
2
2
|
/* The floor was 480px, which trapped the cockpit in any host shorter than that:
|
|
3
3
|
every ancestor here is overflow:hidden, so the bottom of the rail (Create
|
|
4
4
|
channel, Show archived) was rendered but clipped away by the host and could
|
|
@@ -33,6 +33,7 @@ import{A as he,C as pe,a as y,b as L,c as j,d as _,e as V,f as q,g as U,h as K,i
|
|
|
33
33
|
.slm-bar{display:grid;grid-template-columns:auto auto auto minmax(0,1fr);align-items:center;column-gap:14px;row-gap:10px;
|
|
34
34
|
padding:10px 16px;border-bottom:1px solid var(--slm-line);flex:none}
|
|
35
35
|
.slm-modes{display:inline-flex;background:var(--slm-surface);border:1px solid var(--slm-line);border-radius:999px;padding:3px}
|
|
36
|
+
.slm-tools{display:none}
|
|
36
37
|
.slm-mode{padding:6px 16px;border-radius:999px;font-weight:700;font-size:13px;color:var(--slm-muted)}
|
|
37
38
|
.slm-mode.on{background:var(--slm-accent);color:var(--slm-accent-ink)}
|
|
38
39
|
.slm-live{display:inline-flex;align-items:center;gap:6px;font-size:11px;letter-spacing:.12em;font-weight:800;color:var(--slm-muted)}
|
|
@@ -229,13 +230,26 @@ import{A as he,C as pe,a as y,b as L,c as j,d as _,e as V,f as q,g as U,h as K,i
|
|
|
229
230
|
.slm-inspect-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:16px 20px;margin-top:18px}
|
|
230
231
|
.slm-inspect-grid>div{min-width:0}.slm-inspect-grid span{display:block;color:var(--slm-muted);font-size:10px;
|
|
231
232
|
text-transform:uppercase;letter-spacing:.08em}.slm-inspect-grid b{display:block;margin-top:4px;font-size:13px;line-height:1.35;overflow-wrap:anywhere}
|
|
233
|
+
.slm-categoryassign{display:flex;flex-direction:column;gap:8px}
|
|
234
|
+
.slm-categoryrow{display:grid;grid-template-columns:auto minmax(0,1fr) auto auto;align-items:center;gap:8px;
|
|
235
|
+
padding:9px;border:1px solid var(--slm-line);border-radius:10px;background:var(--slm-surface)}
|
|
236
|
+
.slm-categoryrow>.dot{width:10px;height:10px;border-radius:50%}
|
|
237
|
+
.slm-categorycopy{display:flex;min-width:0;flex-direction:column;gap:2px}.slm-categorycopy b{font-size:12px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
238
|
+
.slm-categorycopy small{font-size:10px;color:var(--slm-muted)}.slm-categoryrow .slm-btn{width:auto;padding:6px 9px}
|
|
239
|
+
.slm-tablelist{display:flex;flex-direction:column;gap:8px;margin-top:12px}
|
|
240
|
+
.slm-tablerow{display:grid;grid-template-columns:minmax(0,1fr) minmax(140px,auto);align-items:center;gap:10px;
|
|
241
|
+
padding:10px;border:1px solid var(--slm-line);border-radius:10px;background:var(--slm-surface)}
|
|
242
|
+
.slm-tablecopy{display:flex;min-width:0;flex-direction:column;gap:2px}.slm-tablecopy b{font-size:12px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
243
|
+
.slm-tablecopy small{font-size:10px;color:var(--slm-muted)}.slm-tablerow .slm-select{min-width:140px}
|
|
232
244
|
.slm:fullscreen{border-radius:0;min-height:100vh;background:var(--slm-bg)}
|
|
233
245
|
.slm:fullscreen .slm-bar{padding:14px 22px}.slm:fullscreen .slm-kpi b{font-size:21px}.slm:fullscreen .slm-rail{width:360px}
|
|
234
246
|
|
|
235
247
|
.slm.compact .slm-rail{width:100%;border-left:0;border-top:1px solid var(--slm-line);height:44%}
|
|
236
248
|
.slm.compact .slm-body{flex-direction:column}
|
|
237
249
|
.slm.compact .slm-bar{grid-template-columns:minmax(0,1fr) auto;gap:8px;padding:8px}
|
|
238
|
-
.slm.compact .slm-modes{
|
|
250
|
+
.slm.compact .slm-modes{display:none}
|
|
251
|
+
.slm.compact .slm-tools{display:block;width:100%;padding:9px 13px;min-height:44px;border:1px solid var(--slm-line);
|
|
252
|
+
border-radius:10px;background:var(--slm-surface);color:var(--slm-text);font-size:13px;font-weight:800}
|
|
239
253
|
.slm.compact .slm-live{justify-self:end}.slm.compact .slm-bar-actions{grid-column:1/-1;justify-self:stretch}
|
|
240
254
|
.slm.compact .slm-barbtn{flex:1;padding:6px 9px}.slm.compact .slm-kpis{grid-template-columns:repeat(4,minmax(0,1fr));gap:8px}
|
|
241
255
|
.slm.compact .slm-kpi[data-kpi="viewing-map"],.slm.compact .slm-kpi[data-kpi="active-holds"],
|
|
@@ -257,37 +271,41 @@ import{A as he,C as pe,a as y,b as L,c as j,d as _,e as V,f as q,g as U,h as K,i
|
|
|
257
271
|
transition:none!important;
|
|
258
272
|
scroll-behavior:auto!important}
|
|
259
273
|
}
|
|
260
|
-
`;function
|
|
261
|
-
<select class="slm-select slm-availmode${t!=="open"?" on":""}" data-avail-id="${h(
|
|
274
|
+
`;function B(){if(typeof document>"u"||document.getElementById(A))return;let c=document.createElement("style");c.id=A,c.textContent=fe,document.head.appendChild(c)}function I(c){if(typeof document>"u"||document.getElementById($))return;let e=document.createElement("style");e.id=$,e.textContent=c,document.head.appendChild(e)}import{formatMoney as ve}from"@seatlayer/core";function H(c){return c?c.mode:"open"}function O(c,e,t){switch(c){case"open":return null;case"hidden":return{mode:"hidden",labels:e};case"closed":return{mode:"closed",labels:e};case"timed":return{mode:"timed",revealAt:t?.revealAt??Date.now()+36e5,labels:e};case"threshold":return{mode:"threshold",thresholdPct:t?.thresholdPct??80,labels:e}}}function F(c){return new Date(c-new Date().getTimezoneOffset()*6e4).toISOString().slice(0,16)}function z(c){if(typeof c=="string"){let e=document.querySelector(c);if(!e)throw new Error(`seatmanager: container "${c}" not found`);return e}if(!(c instanceof HTMLElement))throw new Error("seatmanager: container must be a CSS selector or an HTMLElement");return c}function T(c){if(!c.startsWith("mse_"))throw new Error("seatmanager: token must be a short-lived event-scoped mse_ grant minted by your backend; tenant secret keys are unsupported in browsers")}function k(c){return c==="blocked"?"not_for_sale":c}var j=[{key:"free",label:"Free",color:"#6e7bff"},{key:"held",label:"Held",color:"#f4b740"},{key:"booked",label:"Booked",color:"#22a06b"},{key:"blocked",label:"Blocked",color:"#8b94ac"}];function C(c){let e=c??{};return{"--slm-bg":e.background??"#0e1017","--slm-surface":"#181b24","--slm-text":"#eef1f7","--slm-muted":"#8b93a7","--slm-line":"rgba(255,255,255,.09)","--slm-accent":e.accent??"#6e7bff","--slm-accent-ink":e.accentInk??"#ffffff","--slm-font":"-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif","--slm-radius":"14px"}}function M(c,e){let t=Math.max(0,Math.round((e-c)/1e3));if(t<5)return"just now";if(t<60)return`${t}s ago`;let s=Math.round(t/60);return s<60?`${s}m ago`:`${Math.round(s/60)}h ago`}function y(c,e){return ve(c,e,{maximumFractionDigits:0,fallback:(t,s)=>`${s} ${Math.round(t).toLocaleString()}`})}function h(c){return String(c??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}import{UNGROUPED_ID as ge}from"@seatlayer/core";function R(c){return Object.entries(c).filter(([,e])=>e.mode==="closed").map(([e])=>e)}function w(c){let e=c.sectionsBase;if(!e)return{rows:[],hiddenSections:0,closedSections:0};let t=c.doc?.zones??[],s=new Map,i=[];for(let d of e.sections)if(d.zone&&t.some(n=>n.id===d.zone)){let n=s.get(d.zone)??[];n.push(d),s.set(d.zone,n)}else i.push(d);let o=[],a=0,l=0,r=(d,n,p,m=!1)=>{let b=c.availabilityRules[n.id]??null,u=c.effectiveClosed.has(n.id)||m,v=c.effectiveHidden.has(n.id)||p&&!u;d==="section"&&v&&(a+=1),d==="section"&&u&&(l+=1),o.push({kind:d,id:n.id,label:n.label,seatCount:n.seatCount,seatLabels:n.seatLabels,rule:b,hidden:v,closed:u,followsZone:d==="section"&&p})};for(let d of t){let n=s.get(d.id);if(!n||!n.length)continue;let p={id:d.id,label:d.label||"Zone",seatCount:n.reduce((u,v)=>u+v.seatCount,0),seatLabels:n.flatMap(u=>u.seatLabels)},m=!!c.availabilityRules[d.id],b=c.availabilityRules[d.id]?.mode==="closed";r("zone",p,!1);for(let u of n)r("section",u,m,b)}for(let d of i)r("section",d,!1);if(e.ungrouped){let d=e.ungrouped;r("section",{id:ge,label:d.label,seatCount:d.seatCount,seatLabels:d.seatLabels},!1)}return{rows:o,hiddenSections:a,closedSections:l}}function P(c,e){let t=H(c.rule),s=`slm-availrow${c.kind==="zone"?" zone":""}${c.hidden?" hidden":""}${c.closed?" closed":""}`,i=e?" disabled":"",o=(n,p)=>`<option value="${n}"${t===n?" selected":""}>${p}</option>`,a=c.followsZone?'<span class="slm-availfollows">Follows zone</span>':`<span class="slm-availselwrap">
|
|
275
|
+
<select class="slm-select slm-availmode${t!=="open"?" on":""}" data-avail-id="${h(c.id)}"${i} aria-label="Availability for ${h(c.label)}">
|
|
262
276
|
${o("open","Open \u2014 on sale")}
|
|
263
277
|
${o("closed","Closed \u2014 visible, not on sale")}
|
|
264
278
|
${o("hidden","Hidden \u2014 off the buyer map")}
|
|
265
279
|
${o("timed","Reveal at a time")}
|
|
266
280
|
${o("threshold","Auto-reveal at % sold")}
|
|
267
281
|
</select>
|
|
268
|
-
</span>`,
|
|
269
|
-
<input type="datetime-local" class="slm-input" data-avail-reveal="${h(
|
|
270
|
-
</div>`}else if(!
|
|
282
|
+
</span>`,l="";if(!c.followsZone&&t==="timed"){let n=c.rule?.revealAt?h(F(c.rule.revealAt)):"";l=`<div class="slm-availdetail">
|
|
283
|
+
<input type="datetime-local" class="slm-input" data-avail-reveal="${h(c.id)}" value="${n}"${i} aria-label="Reveal time for ${h(c.label)}" />
|
|
284
|
+
</div>`}else if(!c.followsZone&&t==="threshold"){let n=c.rule?.thresholdPct??80;l=`<div class="slm-availdetail">
|
|
271
285
|
<span class="slm-availpctlabel">Reveal at</span>
|
|
272
|
-
<input type="number" min="1" max="100" class="slm-input slm-availpct" data-avail-pct="${h(
|
|
286
|
+
<input type="number" min="1" max="100" class="slm-input slm-availpct" data-avail-pct="${h(c.id)}" value="${h(n)}"${i} aria-label="Percent sold to reveal ${h(c.label)}" />
|
|
273
287
|
<span class="slm-availpctlabel">% sold</span>
|
|
274
|
-
</div>`}let c
|
|
288
|
+
</div>`}let r=c.closed?'<span class="slm-availbadge closed">Closed</span>':c.hidden?'<span class="slm-availbadge hidden">Hidden</span>':"",d=c.kind==="zone"?`<span class="slm-availcaret" aria-hidden="true">${c.hidden?"\u25B8":"\u25BE"}</span>`:"";return`<div class="${s}">
|
|
275
289
|
<div class="slm-availhead">
|
|
276
|
-
<span class="slm-availlabel">${d}${h(
|
|
277
|
-
${
|
|
278
|
-
<span class="slm-availcount">${
|
|
279
|
-
${
|
|
290
|
+
<span class="slm-availlabel">${d}${h(c.label)}</span>
|
|
291
|
+
${r}
|
|
292
|
+
<span class="slm-availcount">${c.seatCount.toLocaleString()}</span>
|
|
293
|
+
${a}
|
|
280
294
|
</div>
|
|
281
|
-
${
|
|
282
|
-
</div>`}var Le="https://api.seatlayer.io",x=80,Te=16,N=4,A=class{constructor(e){this.els={};this.renderer=null;this.doc=null;this.labelToId=new Map;this.labelToSeat=new Map;this.allIds=[];this.gaUnitLabelSet=new Set;this.status=new Map;this.counts={held:0,booked:0,blocked:0};this.modelVersion=0;this.currency="USD";this.authoritativeCurrency=null;this.authoritativeGrossRevenue=0;this.revenueStatus="loading";this.revenueRequest=0;this.controlRoomSnapshot=null;this.serverBaseline=null;this.livePresence=null;this.liveGross=null;this.paintHandle=null;this.trendWindowMinutes=15;this.heatEnabled=!1;this.lastKpiValues=new Map;this.activeKpiDeltas=new Map;this.ws=null;this.reconnectTimer=null;this.attempt=0;this.closed=!1;this.connectionStatus="reconnecting";this.lastMessageAt=null;this.ready=!1;this.feed=[];this.feedTimer=null;this.toastTimer=null;this.liveEventTimer=null;this.kpiCleanupTimer=null;this.followLiveTimer=null;this.followSeatTimer=null;this.releaseAt=null;this.layoutObserver=null;this.tokenExpiresAt=null;this.tokenRefreshTimer=null;this.tokenRefreshInFlight=!1;this.sectionByObject=new Map;this.sectionLabelById=new Map;this.sectionsBase=null;this.availabilityRules={};this.effectiveHidden=new Set;this.effectiveClosed=new Set;this.availabilitySaving=!1;this.lastSyncedAt=null;this.blockedQuery="";this.blockedSection="";this.blockedResultLimit=100;this.unblockAllConfirmTimer=null;this.channels=null;this.channelCaps={view:!1,manage:!1};this.channelCapabilityResolution=0;this.channelsLoading=null;this.onFullscreenChange=()=>{this.paintFullscreenButton(),this.updateContainerLayout(),this.renderer?.forceDraw()};this.onKeyDown=e=>{if(e.metaKey||e.ctrlKey||e.altKey||e.target?.matches('input,select,textarea,[contenteditable="true"]'))return;let s=e.key.toLowerCase();if(s==="m")this.setMode("view");else if(s==="i")this.setMode("inspect");else if(s==="b")this.setMode("block");else if(s==="s")this.setMode("sections");else if(s==="c"){if(!this.channelCaps.view)return;this.setMode("channels")}else if(s==="f")this.toggleFullscreen();else if(s==="escape"){if(!this.channels?.handleBack())return}else return;e.preventDefault()};this.onRailClick=e=>{let t=e.target,s=t?.closest("[data-section-focus]");if(s?.dataset.sectionFocus){this.locateSection(s.dataset.sectionFocus);return}let i=t?.closest("[data-feed-id]");i?.dataset.feedId&&this.locateActivity(i.dataset.feedId)};this.sectionOptions=[];T(e.token),this.opts=e,this.key=e.eventKey,this.mode=e.mode??"view",this.keepLive=e.keepLiveWhileHidden??!0,this.followLive=e.followLive??!1,this.currency=e.currency??"USD",this.tokenExpiresAt=e.tokenExpiresAt??null,this.api=new L(e.apiBase??Le,e.token),this.organizerAssetUrls=new k(this.key,(t,s)=>this.withAuthRetry(()=>this.api.asset(t,s))),this.host=D(e.container)}async render(){I(),this.buildChrome();try{let e=await this.withAuthRetry(()=>this.api.chart(this.key));this.doc=await this.organizerAssetUrls.prepareRendererChart(e.doc),this.authoritativeCurrency=e.event.currency??null,this.currency=this.authoritativeCurrency??this.opts.currency??this.currency,this.buildUnitUniverse(this.doc),this.buildRenderer(),this.buildSectionOptions();let[,t]=await Promise.all([this.resnapshot(),this.refreshControlRoom().catch(s=>this.opts.onError?.(s)),this.refreshAvailability()]);t?.activity?this.seedFeed(t.activity):this.api.log(this.key,{limit:24}).then(s=>this.seedFeed(s.entries)).catch(()=>{}),this.connect(),this.startFeedClock(),this.ready=!0,await this.resolveChannelCapabilities(),this.setMode(this.mode),this.scheduleTokenRefresh(),this.opts.onReady?.()}catch(e){this.fail(e)}return this}setMode(e){e==="channels"&&!this.channelCaps.view&&(e="view"),e==="channels"&&!this.channels&&this.ensureChannels();let t=e!==this.mode,s=this.mode==="channels";this.mode=e,!this.renderer&&this.doc?this.buildRenderer():this.updateRendererInteraction(),t&&this.renderer?.clearSelection(),s&&e!=="channels"&&this.channels?.leave(),this.paintModeTabs(),this.paintRail(),this.applySectionCanvasTreatment(),e==="channels"&&this.channels?.enter(),t&&this.opts.onModeChange?.(e)}async resolveChannelCapabilities(){let e=++this.channelCapabilityResolution,t=this.opts.capabilities,s;if(t){let i=new Set(t);s={view:i.has("event:channels:view"),manage:i.has("event:channels:view")&&i.has("event:channels:manage")}}else s={view:!1,manage:!1};if(!s.view&&!t)try{await this.api.channels(this.key),s={view:!0,manage:!1}}catch{s={view:!1,manage:!1}}if(e===this.channelCapabilityResolution){if(this.channelCaps=s,!this.channelCaps.view){this.channels?.destroy(),this.channels=null,this.mode==="channels"?this.setMode("view"):this.paintModeTabs();return}this.channels?.setCapabilities(this.channelCaps),this.paintModeTabs()}}ensureChannels(){if(this.channelsLoading)return this.channelsLoading;if(this.channels)return Promise.resolve();let e=(async()=>{try{let t=await import("./channelsMode-RUNPEHKD.js");if(this.closed||!this.channelCaps.view||(this.channels||(B(t.CHANNELS_CSS),this.channels=new t.ChannelsMode(this.buildChannelsHost(),this.channelCaps),this.channels.onInteractionChange=()=>this.updateRendererInteraction()),this.mode!=="channels"))return;this.updateRendererInteraction(),this.paintRail(),this.channels.enter()}catch(t){if(this.closed)return;this.channelsLoading=null,this.mode==="channels"&&this.paintRail(),this.opts.onError?.(t)}})();return this.channelsLoading=e,e}buildChannelsHost(){return{eventKey:this.key,api:this.api,rail:this.els.rail,mapLayer:this.root.querySelector(".slm-map"),root:this.root,seats:()=>[...this.labelToSeat.values()].map(e=>({id:e.id,label:e.label,x:e.x,y:e.y})),statusOf:e=>this.status.get(e)??(this.labelToSeat.has(e)?"free":void 0),selectionLabels:()=>this.selectionLabels(),selectByLabels:e=>{this.selectByLabels(e)},clearSelection:()=>this.clearSelection(),selectSection:e=>{this.selectSection(e)},sections:()=>this.sectionOptions,labelsInSection:e=>this.renderer?.getSelectableInSection(e).map(t=>t.label)??[],rows:()=>{let e=new Map((this.doc?.objects??[]).map(s=>[s.id,s])),t=new Map;for(let s of this.labelToSeat.values()){let i=e.get(s.rowId);if(!i||i.type!=="row"&&i.type!=="table")continue;let o=s.logicalRowId??s.rowId,n=this.sectionByObject.get(s.rowId)??f,a=t.get(o)??{id:o,label:i.type==="row"?i.segmentedRow?.displayLabel??i.displayLabel??i.label:i.displayLabel??i.label,sectionId:n,sectionLabel:this.sectionLabelById.get(n)??"Other seats",labels:[]};a.labels.push(s.label),t.set(o,a)}return[...t.values()].sort((s,i)=>s.sectionLabel.localeCompare(i.sectionLabel,void 0,{numeric:!0,sensitivity:"base"})||s.label.localeCompare(i.label,void 0,{numeric:!0,sensitivity:"base"}))},categories:()=>(this.doc?.categories??[]).map(e=>({key:e.key,label:e.label??e.key,color:e.color})),labelsInCategory:e=>[...this.labelToSeat.entries()].filter(([,t])=>t.categoryKey===e).map(([t])=>t),sectionOfLabel:e=>{let t=this.labelToSeat.get(e);if(!t)return null;let s=this.sectionByObject.get(t.rowId)??f;return{id:s,label:this.sectionLabelById.get(s)??"Other seats"}},worldToScreen:e=>this.renderer?.worldToScreen(e)??null,seatPixelSize:()=>this.seatPixelSize(),isSeatDetail:()=>this.renderer?.getRung?.()==="seats",showSectionOverview:()=>{this.renderer?.clearSectionFocus(),this.renderer?.setRung?.("sections")},focusSection:e=>this.renderer?.focusSection(e),isCompact:()=>!!this.root?.classList.contains("compact"),setMapInert:e=>{this.mapHost.toggleAttribute("inert",e),this.mapHost.setAttribute("aria-hidden",String(e))},toast:(e,t)=>this.toast(e,t),onError:e=>this.opts.onError?.(e)}}seatPixelSize(){let e=this.renderer?.getVisibleWorldRect?.(),t=this.mapHost?.clientWidth??0;return!e?.width||!t?6:Math.max(3,t/e.width*18)}setHeatOverlay(e){this.heatEnabled=e,this.applyHeatOverlay(),this.paintHeatButton()}setFollowLive(e){let t=this.followLive!==e;this.followLive=e,e||(this.followLiveTimer&&clearTimeout(this.followLiveTimer),this.followSeatTimer&&clearTimeout(this.followSeatTimer),this.followLiveTimer=null,this.followSeatTimer=null),this.paintFollowLiveButton(),t&&this.opts.onFollowLiveChange?.(e)}setKeepLiveWhileHidden(e){this.keepLive=e??!0,this.opts.keepLiveWhileHidden=e}setCurrency(e){this.opts.currency=e,!this.authoritativeCurrency&&(this.currency=e??"USD",this.ready&&this.recomputeTallies())}setTheme(e){if(this.opts.theme=e,!!this.root)for(let[t,s]of Object.entries(R(e)))this.root.style.setProperty(t,s)}setCapabilities(e){this.opts.capabilities=e,this.ready&&this.resolveChannelCapabilities()}setTokenRefresh(e){this.opts.onTokenRefresh=e,this.scheduleTokenRefresh()}setTrendWindow(e){let t=Number.isFinite(e)?Math.floor(e):15;return this.trendWindowMinutes=Math.max(5,Math.min(60,t)),this.paintTrendWindow(),this.refreshControlRoom()}async enterFullscreen(){!this.root?.requestFullscreen||this.isFullscreen()||(await this.root.requestFullscreen(),this.root.focus({preventScroll:!0}))}async exitFullscreen(){typeof document>"u"||!this.isFullscreen()||await document.exitFullscreen()}isFullscreen(){return typeof document<"u"&&document.fullscreenElement===this.root}toggleFullscreen(){(this.isFullscreen()?this.exitFullscreen():this.enterFullscreen()).catch(t=>this.opts.onError?.(t))}setToken(e,t){T(e),this.api.setToken(e),this.opts.token=e,this.opts.tokenExpiresAt=t,this.tokenExpiresAt=t??null,this.scheduleTokenRefresh(),this.ready&&this.resolveChannelCapabilities()}scheduleTokenRefresh(){this.tokenRefreshTimer&&clearTimeout(this.tokenRefreshTimer),this.tokenRefreshTimer=null;let e=this.opts.onTokenRefresh,t=this.tokenExpiresAt;if(this.closed||!e||!t||!Number.isFinite(t))return;let s=t-Date.now(),i=Math.min(12e4,Math.max(3e4,s*.2)),o=Math.max(0,s-i);this.tokenRefreshTimer=setTimeout(()=>{this.tokenRefreshTimer=null,this.rotateToken()},o)}async rotateToken(){if(!(this.closed||this.tokenRefreshInFlight||!this.opts.onTokenRefresh)){this.tokenRefreshInFlight=!0;try{let e=await this.opts.onTokenRefresh();if(!e?.token||!Number.isFinite(e.expiresAt))throw new Error("invalid_token_refresh_result");this.setToken(e.token,e.expiresAt)}catch(e){this.opts.onError?.(e),this.closed||(this.tokenRefreshTimer=setTimeout(()=>{this.tokenRefreshTimer=null,this.rotateToken()},3e4))}finally{this.tokenRefreshInFlight=!1}}}async block(e,t={}){let s=(e??this.selectionLabels()).filter(o=>this.status.get(o)==="free");if(!s.length)return;let i=t.releaseAt??this.releaseAt??void 0;this.setSeatsLocal(s,"blocked");try{await this.api.block(this.key,s,{...t,releaseAt:i}),this.clearSelection(),this.done("block",s,i?`Blocked ${s.length} \u2014 auto-release ${new Date(i).toLocaleString()}.`:`Blocked ${s.length} seat${s.length===1?"":"s"}.`)}catch(o){this.setSeatsLocal(s,"free"),this.toastErr(o instanceof y&&o.status===409?"Some seats were just taken. Try again.":"Couldn't block those seats."),this.opts.onError?.(o)}}async unblock(e){let t=(e??this.selectionLabels()).filter(s=>this.status.get(s)==="blocked");if(t.length){this.setSeatsLocal(t,"free");try{await this.api.unblock(this.key,t),this.clearSelection(),this.done("unblock",t,`Unblocked ${t.length} seat${t.length===1?"":"s"}.`)}catch(s){this.setSeatsLocal(t,"blocked"),this.toastErr("Couldn't unblock those seats."),this.opts.onError?.(s)}}}async unblockAll(){let e=[...this.status.entries()].filter(([,t])=>t==="blocked").map(([t])=>t);if(e.length){this.setSeatsLocal(e,"free");try{let t=await this.api.unblockAll(this.key);this.clearSelection(),this.done("unblockAll",e,`Unblocked ${t.freed} seat${t.freed===1?"":"s"}.`)}catch(t){await this.resnapshot(),this.toastErr("Couldn't mark everything for sale."),this.opts.onError?.(t)}}}async cancelBooking(e,t){let s=e.filter(i=>this.status.get(i)==="booked");if(!(!s.length||!t)){this.setSeatsLocal(s,"free");try{await this.api.unbook(this.key,s,t),this.clearSelection(),this.done("cancelBooking",s,`Released ${s.length} booked unit${s.length===1?"":"s"}.`)}catch(i){this.setSeatsLocal(s,"booked"),this.toastErr("Couldn't release that booked inventory. Check the booking reference."),this.opts.onError?.(i)}}}selectAll(){let e=this.renderer?.selectAllSelectable()??[];return this.syncSelection(),e}selectSection(e){if(!this.renderer)return[];let t=this.renderer.getSelectableInSection(e);return this.renderer.selectByLabels(t.map(s=>s.label)),this.syncSelection(),this.renderer.getSelection()}selectByLabels(e){let t=this.renderer?.selectByLabels(e)??[];return this.syncSelection(),t}clearSelection(){this.renderer?.clearSelection(),this.syncSelection()}getSelection(){return this.renderer?.getSelection()??[]}getReport(){return this.api.report(this.key).then(e=>(this.applyReportRevenue(e),e))}getControlRoomSnapshot(e=this.trendWindowMinutes){return this.setTrendWindow(e)}getConnection(){return{status:this.connectionStatus,lastMessageAt:this.lastMessageAt}}getLog(e={}){return this.api.log(this.key,e)}async setHoldTtl(e){try{await this.api.setHoldTtl(this.key,e),this.done("setHoldTtl",[],e?`Hold window set to ${Math.round(e/6e4)} min.`:"Hold window reset.")}catch(t){this.toastErr("Couldn't update the hold window."),this.opts.onError?.(t)}}zoomToFit(){this.renderer?.clearSectionFocus(),this.renderer?.zoomToFit()}destroy(){if(this.closed=!0,this.reconnectTimer&&clearTimeout(this.reconnectTimer),this.feedTimer&&clearInterval(this.feedTimer),this.toastTimer&&clearTimeout(this.toastTimer),this.liveEventTimer&&clearTimeout(this.liveEventTimer),this.kpiCleanupTimer&&clearTimeout(this.kpiCleanupTimer),this.followLiveTimer&&clearTimeout(this.followLiveTimer),this.followSeatTimer&&clearTimeout(this.followSeatTimer),this.unblockAllConfirmTimer&&clearTimeout(this.unblockAllConfirmTimer),this.paintHandle!==null&&typeof cancelAnimationFrame=="function"&&cancelAnimationFrame(this.paintHandle),this.paintHandle=null,this.channels?.destroy(),this.channels=null,this.channelsLoading=null,this.tokenRefreshTimer&&clearTimeout(this.tokenRefreshTimer),this.layoutObserver?.disconnect(),this.layoutObserver=null,this.root?.removeEventListener("keydown",this.onKeyDown),this.els.rail?.removeEventListener("click",this.onRailClick),typeof document<"u"&&document.removeEventListener("fullscreenchange",this.onFullscreenChange),this.ws){try{this.ws.close()}catch{}this.ws=null}this.renderer?.destroy(),this.renderer=null,this.organizerAssetUrls.dispose(),this.root&&this.root.parentNode===this.host&&this.host.removeChild(this.root)}buildRenderer(){if(!this.doc)return;let e=this.isBulkSelectMode();this.renderer=new ye(this.mapHost,{manageMode:!0,marqueeSelect:e,maxSelection:1e6,selectableStatuses:this.selectableStatuses(),currency:this.currency,onSelect:t=>this.handleSeatSelect(t),onDeselect:()=>this.syncSelection(),onMarquee:()=>this.syncSelection(),onSectionTap:t=>{this.renderer?.focusSection(t),this.channels?.handleSectionFocus(t)},onViewChange:()=>{this.updateZoomHint(),this.channels?.handleViewChange()}}),this.renderer.setChart(this.doc),this.repaintAll(),this.applyHeatOverlay(),this.updateZoomHint()}isBulkSelectMode(){return this.mode==="block"||this.mode==="channels"&&this.channels?.usesMarqueeSelection()===!0}selectableStatuses(){return this.mode==="block"?["free","not_for_sale"]:this.mode==="inspect"||this.isBulkSelectMode()||this.mode==="channels"&&this.channels?.canSelect()===!0?["free","held","booked","not_for_sale"]:[]}updateRendererInteraction(){let e=this.isBulkSelectMode();this.renderer?.setManageInteraction({manageMode:!0,marqueeSelect:e,maxSelection:1e6,selectableStatuses:this.selectableStatuses()}),this.updateZoomHint()}handleSeatSelect(e){if(this.mode==="inspect"){let t=this.getSelection().filter(s=>s.id!==e.id).map(s=>s.id);t.length&&this.renderer?.deselect(t)}this.syncSelection()}buildUnitUniverse(e){for(let t of ke(e))this.labelToId.set(t.label,t.id),this.labelToSeat.set(t.label,t),this.allIds.push(t.id);for(let t of we(e))for(let s of xe(t))this.labelToId.has(s)||this.gaUnitLabelSet.add(s)}unitTotal(){return this.allIds.length+this.gaUnitLabelSet.size}knownLabels(){return[...this.labelToId.keys(),...this.gaUnitLabelSet]}repaintAll(){let e=this.renderer;if(!e)return;this.allIds.length&&e.setStatus(this.allIds,"free");let t={free:[],held:[],booked:[],not_for_sale:[]};for(let[s,i]of this.status.entries()){let o=this.labelToId.get(s);o&&t[S(i)].push(o)}["held","booked","not_for_sale"].forEach(s=>{t[s].length&&e.setStatus(t[s],s)})}async connect(){if(this.closed)return;let e;try{if(e=(await this.withAuthRetry(()=>this.api.subscribeTicket(this.key))).protocols,!e.length)throw new Error("manage_subscribe_ticket_missing")}catch(s){this.setLive(!1),this.opts.onError?.(s),this.scheduleReconnect();return}if(this.closed)return;let t;try{t=new WebSocket(this.api.socketUrl(this.key),e)}catch(s){this.opts.onError?.(s),this.scheduleReconnect();return}this.ws=t,t.onopen=()=>{this.attempt=0,this.setLive(!0),this.resnapshot().then(()=>this.refreshControlRoom()).catch(s=>this.opts.onError?.(s)),this.refreshAvailability()},t.onmessage=s=>this.onMessage(s),t.onclose=()=>{this.ws===t&&(this.ws=null),this.setLive(!1),this.scheduleReconnect()},t.onerror=()=>{try{t.close()}catch{}}}scheduleReconnect(){if(this.closed||this.reconnectTimer)return;let e=Math.min(1e3*2**Math.min(this.attempt++,5),15e3);this.reconnectTimer=setTimeout(()=>{this.reconnectTimer=null,this.connect()},e)}onMessage(e){let t;try{t=JSON.parse(typeof e.data=="string"?e.data:"")}catch{return}if(!t||typeof t!="object")return;this.lastMessageAt=Date.now();let s=t;if((Array.isArray(s.hidden)||Array.isArray(s.closed))&&this.updateEffectiveAvailability(s.hidden,s.closed),s.type==="presence"){typeof s.shoppingSessions=="number"&&typeof s.activeHolds=="number"&&(this.livePresence={at:Date.now(),value:{shoppingSessions:s.shoppingSessions,activeHolds:s.activeHolds}},this.controlRoomSnapshot&&(this.controlRoomSnapshot={...this.controlRoomSnapshot,presence:this.livePresence.value},this.opts.onControlRoom?.(this.controlRoomSnapshot)),this.lastSyncedAt=Date.now(),this.recomputeTallies(),this.paintMonitorInsights());return}if(s.type!=="hidden"){if(s.seats&&typeof s.seats=="object")this.applySnapshot(s.seats,typeof s.default=="string"?s.default:void 0);else if(Array.isArray(s.changes)){let i=[],o=new Map;for(let a of s.changes){let c=["free","held","booked","blocked"].includes(a.status)?a.status:"free",d=this.status.get(a.label)??"free";if(d===c)continue;this.setStatusLabel(a.label,c,d);let l=this.labelToId.get(a.label);l&&(this.renderer?.setStatus([l],S(c)),i.push(l));let p=this.verbFor(d,c),m=`${p}:${c}`,b=o.get(m)??{labels:[],verb:p,status:c};b.labels.push(a.label),o.set(m,b)}for(let a of o.values()){let c=this.pushActivity(a.labels,a.verb,a.status);c&&this.paintSpatialActivity(c)}i.length&&(this.lastSyncedAt=Date.now(),this.afterPaint());let n=typeof s.bookedValue?.gross=="number"?s.bookedValue.gross:s.revenue?.gross;typeof n=="number"&&Number.isFinite(n)&&this.applyLiveGross(n),this.recomputeTallies()}}}applyLiveGross(e){if(this.liveGross={at:Date.now(),value:e},this.authoritativeGrossRevenue=e,this.revenueStatus="current",this.controlRoomSnapshot){let s={...this.controlRoomSnapshot.bookedValue??this.controlRoomSnapshot.revenue,gross:e};this.controlRoomSnapshot={...this.controlRoomSnapshot,bookedValue:s,revenue:s},this.opts.onControlRoom?.(this.controlRoomSnapshot)}}setStatusLabel(e,t,s=this.status.get(e)??"free"){this.status.set(e,t),s!==t&&(s!=="free"&&(this.counts[s]-=1),t!=="free"&&(this.counts[t]+=1))}async resnapshot(){try{let e=await this.api.objects(this.key);this.applySnapshot(e.seats),this.updateEffectiveAvailability(e.hidden,e.closed),this.lastMessageAt=Date.now()}catch{}}applySnapshot(e,t){let s=o=>["free","held","booked","blocked"].includes(o)?o:"free",i=new Map;if(t!==void 0){let o=s(t);for(let n of this.knownLabels())i.set(n,o)}for(let[o,n]of Object.entries(e))i.set(o,s(n));this.status=i,this.modelVersion+=1,this.recountAll(),this.lastSyncedAt=Date.now(),this.repaintAll(),this.afterPaint(),this.recomputeTallies()}recountAll(){let e={held:0,booked:0,blocked:0};for(let t of this.status.values())t!=="free"&&(e[t]+=1);this.counts=e}setSeatsLocal(e,t){let s=[];for(let i of e){this.setStatusLabel(i,t);let o=this.labelToId.get(i);o&&s.push(o)}s.length&&this.renderer?.setStatus(s,S(t)),this.afterPaint(),this.recomputeTallies()}afterPaint(){this.keepLive&&typeof document<"u"&&document.hidden&&this.renderer?.forceDraw()}activityColor(e){return e==="held"?"#f4b740":e==="booked"?"#22a06b":e==="blocked"?"#8b94ac":"#6e7bff"}sectionsForLabels(e){let t=new Set;for(let i of e){let o=this.labelToSeat.get(i);if(!o)continue;let n=this.sectionByObject.get(o.rowId);n&&n!==f&&t.add(n)}let s=[...t];return{ids:s,labels:s.map(i=>this.sectionLabelById.get(i)??i)}}pulseSeatLabels(e,t){let s=this.activityColor(t);for(let i of e.slice(0,Te)){let o=this.labelToId.get(i);o&&this.renderer?.flashSeat(o,s)}}paintSpatialActivity(e){let t=e.sectionIds??this.sectionsForLabels(e.labels).ids,s=this.renderer?.getFocusedSection()??null,i=this.followLive&&t.length===1&&(e.status==="held"||e.status==="booked");if(i&&s===t[0]){this.pulseSeatLabels(e.labels,e.status);return}if(i){this.followLiveTimer&&clearTimeout(this.followLiveTimer),this.followSeatTimer&&clearTimeout(this.followSeatTimer),this.followLiveTimer=setTimeout(()=>{this.followLiveTimer=null,this.renderer?.focusSection(t[0]),this.followSeatTimer=setTimeout(()=>{this.followSeatTimer=null,this.pulseSeatLabels(e.labels,e.status)},520)},220);return}if(!s&&t.length){let o=this.activityColor(e.status);for(let n of t.slice(0,N))this.renderer?.flashSection(n,o);return}(!t.length||s&&t.includes(s))&&this.pulseSeatLabels(e.labels,e.status)}locateSection(e){this.renderer?.focusSection(e)}locateActivity(e){let t=this.feed.find(i=>i.id===e);if(!t)return;let s=t.sectionIds??this.sectionsForLabels(t.labels).ids;if(this.followSeatTimer&&clearTimeout(this.followSeatTimer),s.length===1){this.locateSection(s[0]),this.followSeatTimer=setTimeout(()=>{this.followSeatTimer=null,this.pulseSeatLabels(t.labels,t.status)},520);return}this.zoomToFit(),this.followSeatTimer=setTimeout(()=>{if(this.followSeatTimer=null,s.length){let i=this.activityColor(t.status);for(let o of s.slice(0,N))this.renderer?.flashSection(o,i)}else this.pulseSeatLabels(t.labels,t.status)},280)}showLiveEvent(e){let t=this.els.liveevent;if(!t)return;let s=e.sectionLabels??[],i=s.length===1?s[0]:s.length>1?`${s.length} sections`:e.label,o=e.count===1?"seat":"seats";t.innerHTML=`<span class="slm-liveeventdot" style="background:${this.activityColor(e.status)}"></span>
|
|
295
|
+
${l}
|
|
296
|
+
</div>`}var Te="https://api.seatlayer.io",x=80,Ce=16,D=4,E=class{constructor(e){this.els={};this.renderer=null;this.doc=null;this.inventoryModelVersion=1;this.selectableObjectLabels=new Set;this.lastSelectionLabels=new Set;this.selectionWasValid=null;this.initialSelectionApplied=!1;this.labelToId=new Map;this.labelToSeat=new Map;this.allIds=[];this.gaUnitLabelSet=new Set;this.status=new Map;this.counts={held:0,booked:0,blocked:0};this.modelVersion=0;this.currency="USD";this.authoritativeCurrency=null;this.authoritativeGrossRevenue=0;this.revenueStatus="loading";this.revenueRequest=0;this.controlRoomSnapshot=null;this.serverBaseline=null;this.livePresence=null;this.liveGross=null;this.paintHandle=null;this.trendWindowMinutes=15;this.heatEnabled=!1;this.lastKpiValues=new Map;this.activeKpiDeltas=new Map;this.ws=null;this.reconnectTimer=null;this.attempt=0;this.closed=!1;this.connectionStatus="reconnecting";this.lastMessageAt=null;this.ready=!1;this.feed=[];this.feedTimer=null;this.toastTimer=null;this.liveEventTimer=null;this.kpiCleanupTimer=null;this.followLiveTimer=null;this.followSeatTimer=null;this.releaseAt=null;this.layoutObserver=null;this.tokenExpiresAt=null;this.tokenRefreshTimer=null;this.tokenRefreshInFlight=!1;this.sectionByObject=new Map;this.sectionLabelById=new Map;this.sectionsBase=null;this.filteredSectionLabel=null;this.availabilityRules={};this.effectiveHidden=new Set;this.effectiveClosed=new Set;this.availabilitySaving=!1;this.lastSyncedAt=null;this.blockedQuery="";this.blockedSection="";this.blockedResultLimit=100;this.unblockAllConfirmTimer=null;this.channels=null;this.channelCaps={view:!1,manage:!1};this.categoryCanManage=!1;this.tableCanManage=!1;this.tableBookingSaving=!1;this.channelCapabilityResolution=0;this.channelsLoading=null;this.onFullscreenChange=()=>{this.paintFullscreenButton(),this.updateContainerLayout(),this.renderer?.forceDraw()};this.onKeyDown=e=>{if(e.metaKey||e.ctrlKey||e.altKey||e.target?.matches('input,select,textarea,[contenteditable="true"]'))return;let s=e.key.toLowerCase();if(s==="m")this.setMode("view");else if(s==="i")this.setMode("inspect");else if(s==="x")this.setMode("select");else if(s==="l")this.setMode("filterSections");else if(s==="b")this.setMode("block");else if(s==="s")this.setMode("sections");else if(s==="g")if(this.categoryCanManage)this.setMode("categories");else return;else if(s==="t")if(this.tableCanManage)this.setMode("tables");else return;else if(s==="c"){if(!this.channelCaps.view)return;this.setMode("channels")}else if(s==="f")this.toggleFullscreen();else if(s==="escape"){if(!this.channels?.handleBack())return}else return;e.preventDefault()};this.onRailClick=e=>{let t=e.target,s=t?.closest("[data-section-focus]");if(s?.dataset.sectionFocus){this.locateSection(s.dataset.sectionFocus);return}let i=t?.closest("[data-feed-id]");i?.dataset.feedId&&this.locateActivity(i.dataset.feedId)};this.sectionOptions=[];T(e.token),this.assertSelectionOptions(e.maxSelectedObjects,e.numberOfPlacesToSelect),this.opts=e,this.key=e.eventKey,this.mode=e.mode??"view",this.selectableObjectLabels=new Set(e.selectableObjects??[]),this.categoryCanManage=new Set(e.capabilities??[]).has("event:categories:manage"),this.tableCanManage=new Set(e.capabilities??[]).has("event:tables:manage"),this.keepLive=e.keepLiveWhileHidden??!0,this.followLive=e.followLive??!1,this.currency=e.currency??"USD",this.tokenExpiresAt=e.tokenExpiresAt??null,this.api=new L(e.apiBase??Te,e.token),this.organizerAssetUrls=new S(this.key,(t,s)=>this.withAuthRetry(()=>this.api.asset(t,s))),this.host=z(e.container)}assertSelectionOptions(e,t){let s=i=>i===void 0||Number.isInteger(i)&&i>0;if(!s(e))throw new TypeError("maxSelectedObjects must be a positive integer");if(!s(t))throw new TypeError("numberOfPlacesToSelect must be a positive integer");if(e!==void 0&&t!==void 0&&e<t)throw new TypeError("maxSelectedObjects cannot be lower than numberOfPlacesToSelect")}async render(){B(),this.buildChrome();try{let e=await this.withAuthRetry(()=>this.api.chart(this.key));this.inventoryModelVersion=e.event.inventoryModelVersion===2?2:1,this.doc=await this.organizerAssetUrls.prepareRendererChart(e.doc),this.authoritativeCurrency=e.event.currency??null,this.currency=this.authoritativeCurrency??this.opts.currency??this.currency,this.buildUnitUniverse(this.doc),this.buildRenderer(),this.buildSectionOptions();let[,t]=await Promise.all([this.resnapshot(),this.refreshControlRoom().catch(s=>this.opts.onError?.(s)),this.refreshAvailability()]);t?.activity?this.seedFeed(t.activity):this.api.log(this.key,{limit:24}).then(s=>this.seedFeed(s.entries)).catch(()=>{}),this.connect(),this.startFeedClock(),this.ready=!0,await this.resolveChannelCapabilities(),this.setMode(this.mode),this.applyInitialSelection(),this.scheduleTokenRefresh(),this.opts.onReady?.()}catch(e){this.fail(e)}return this}setMode(e){e==="channels"&&!this.channelCaps.view&&(e="view"),e==="categories"&&!this.categoryCanManage&&(e="view"),e==="tables"&&!this.tableCanManage&&(e="view"),e==="channels"&&!this.channels&&this.ensureChannels();let t=e!==this.mode,s=this.mode==="channels",i=this.mode==="filterSections";this.mode=e,!this.renderer&&this.doc?this.buildRenderer():this.updateRendererInteraction(),t&&(this.renderer?.clearSelection(),this.syncSelection()),s&&e!=="channels"&&this.channels?.leave(),this.paintModeTabs(),this.paintRail(),this.applySectionCanvasTreatment(),i&&e!=="filterSections"&&this.renderer?.zoomToFit(),e==="channels"&&this.channels?.enter(),e==="select"&&this.applyInitialSelection(),t&&this.opts.onModeChange?.(e)}async resolveChannelCapabilities(){let e=++this.channelCapabilityResolution,t=this.opts.capabilities,s;if(t){let i=new Set(t);s={view:i.has("event:channels:view"),manage:i.has("event:channels:view")&&i.has("event:channels:manage")}}else s={view:!1,manage:!1};if(!s.view&&!t)try{await this.api.channels(this.key),s={view:!0,manage:!1}}catch{s={view:!1,manage:!1}}if(e===this.channelCapabilityResolution){if(this.channelCaps=s,!this.channelCaps.view){this.channels?.destroy(),this.channels=null,this.mode==="channels"?this.setMode("view"):this.paintModeTabs();return}this.channels?.setCapabilities(this.channelCaps),this.paintModeTabs()}}ensureChannels(){if(this.channelsLoading)return this.channelsLoading;if(this.channels)return Promise.resolve();let e=(async()=>{try{let t=await import("./channelsMode-LDIT556C.js");if(this.closed||!this.channelCaps.view||(this.channels||(I(t.CHANNELS_CSS),this.channels=new t.ChannelsMode(this.buildChannelsHost(),this.channelCaps),this.channels.onInteractionChange=()=>this.updateRendererInteraction()),this.mode!=="channels"))return;this.updateRendererInteraction(),this.paintRail(),this.channels.enter()}catch(t){if(this.closed)return;this.channelsLoading=null,this.mode==="channels"&&this.paintRail(),this.opts.onError?.(t)}})();return this.channelsLoading=e,e}buildChannelsHost(){return{eventKey:this.key,api:this.api,rail:this.els.rail,mapLayer:this.root.querySelector(".slm-map"),root:this.root,seats:()=>[...this.labelToSeat.values()].map(e=>({id:e.id,label:e.label,x:e.x,y:e.y})),statusOf:e=>this.status.get(e)??(this.labelToSeat.has(e)?"free":void 0),selectionLabels:()=>this.selectionLabels(),selectByLabels:e=>{this.selectByLabels(e)},clearSelection:()=>this.clearSelection(),selectSection:e=>{this.selectSection(e)},sections:()=>this.sectionOptions,labelsInSection:e=>this.renderer?.getSelectableInSection(e).map(t=>t.label)??[],rows:()=>{let e=new Map((this.doc?.objects??[]).map(s=>[s.id,s])),t=new Map;for(let s of this.labelToSeat.values()){let i=e.get(s.rowId);if(!i||i.type!=="row"&&i.type!=="table")continue;let o=s.logicalRowId??s.rowId,a=this.sectionByObject.get(s.rowId)??f,l=t.get(o)??{id:o,label:i.type==="row"?i.segmentedRow?.displayLabel??i.displayLabel??i.label:i.displayLabel??i.label,sectionId:a,sectionLabel:this.sectionLabelById.get(a)??"Other seats",labels:[]};l.labels.push(s.label),t.set(o,l)}return[...t.values()].sort((s,i)=>s.sectionLabel.localeCompare(i.sectionLabel,void 0,{numeric:!0,sensitivity:"base"})||s.label.localeCompare(i.label,void 0,{numeric:!0,sensitivity:"base"}))},categories:()=>(this.doc?.categories??[]).map(e=>({key:e.key,label:e.label??e.key,color:e.color})),labelsInCategory:e=>[...this.labelToSeat.entries()].filter(([,t])=>t.categoryKey===e).map(([t])=>t),sectionOfLabel:e=>{let t=this.labelToSeat.get(e);if(!t)return null;let s=this.sectionByObject.get(t.rowId)??f;return{id:s,label:this.sectionLabelById.get(s)??"Other seats"}},worldToScreen:e=>this.renderer?.worldToScreen(e)??null,seatPixelSize:()=>this.seatPixelSize(),isSeatDetail:()=>this.renderer?.getRung?.()==="seats",showSectionOverview:()=>{this.renderer?.clearSectionFocus(),this.renderer?.setRung?.("sections")},focusSection:e=>this.renderer?.focusSection(e),isCompact:()=>!!this.root?.classList.contains("compact"),setMapInert:e=>{this.mapHost.toggleAttribute("inert",e),this.mapHost.setAttribute("aria-hidden",String(e))},toast:(e,t)=>this.toast(e,t),onError:e=>this.opts.onError?.(e)}}seatPixelSize(){let e=this.renderer?.getVisibleWorldRect?.(),t=this.mapHost?.clientWidth??0;return!e?.width||!t?6:Math.max(3,t/e.width*18)}setHeatOverlay(e){this.heatEnabled=e,this.applyHeatOverlay(),this.paintHeatButton()}setFollowLive(e){let t=this.followLive!==e;this.followLive=e,e||(this.followLiveTimer&&clearTimeout(this.followLiveTimer),this.followSeatTimer&&clearTimeout(this.followSeatTimer),this.followLiveTimer=null,this.followSeatTimer=null),this.paintFollowLiveButton(),t&&this.opts.onFollowLiveChange?.(e)}setKeepLiveWhileHidden(e){this.keepLive=e??!0,this.opts.keepLiveWhileHidden=e}setCurrency(e){this.opts.currency=e,!this.authoritativeCurrency&&(this.currency=e??"USD",this.ready&&this.recomputeTallies())}setTheme(e){if(this.opts.theme=e,!!this.root)for(let[t,s]of Object.entries(C(e)))this.root.style.setProperty(t,s)}setCapabilities(e){this.opts.capabilities=e,this.categoryCanManage=new Set(e??[]).has("event:categories:manage"),this.tableCanManage=new Set(e??[]).has("event:tables:manage"),!this.categoryCanManage&&this.mode==="categories"||!this.tableCanManage&&this.mode==="tables"?this.setMode("view"):this.paintModeTabs(),this.ready&&this.resolveChannelCapabilities()}setTokenRefresh(e){this.opts.onTokenRefresh=e,this.scheduleTokenRefresh()}setTrendWindow(e){let t=Number.isFinite(e)?Math.floor(e):15;return this.trendWindowMinutes=Math.max(5,Math.min(60,t)),this.paintTrendWindow(),this.refreshControlRoom()}async enterFullscreen(){!this.root?.requestFullscreen||this.isFullscreen()||(await this.root.requestFullscreen(),this.root.focus({preventScroll:!0}))}async exitFullscreen(){typeof document>"u"||!this.isFullscreen()||await document.exitFullscreen()}isFullscreen(){return typeof document<"u"&&document.fullscreenElement===this.root}toggleFullscreen(){(this.isFullscreen()?this.exitFullscreen():this.enterFullscreen()).catch(t=>this.opts.onError?.(t))}setToken(e,t){T(e),this.api.setToken(e),this.opts.token=e,this.opts.tokenExpiresAt=t,this.tokenExpiresAt=t??null,this.scheduleTokenRefresh(),this.ready&&this.resolveChannelCapabilities()}scheduleTokenRefresh(){this.tokenRefreshTimer&&clearTimeout(this.tokenRefreshTimer),this.tokenRefreshTimer=null;let e=this.opts.onTokenRefresh,t=this.tokenExpiresAt;if(this.closed||!e||!t||!Number.isFinite(t))return;let s=t-Date.now(),i=Math.min(12e4,Math.max(3e4,s*.2)),o=Math.max(0,s-i);this.tokenRefreshTimer=setTimeout(()=>{this.tokenRefreshTimer=null,this.rotateToken()},o)}async rotateToken(){if(!(this.closed||this.tokenRefreshInFlight||!this.opts.onTokenRefresh)){this.tokenRefreshInFlight=!0;try{let e=await this.opts.onTokenRefresh();if(!e?.token||!Number.isFinite(e.expiresAt))throw new Error("invalid_token_refresh_result");this.setToken(e.token,e.expiresAt)}catch(e){this.opts.onError?.(e),this.closed||(this.tokenRefreshTimer=setTimeout(()=>{this.tokenRefreshTimer=null,this.rotateToken()},3e4))}finally{this.tokenRefreshInFlight=!1}}}async block(e,t={}){let s=(e??this.selectionLabels()).filter(o=>this.status.get(o)==="free");if(!s.length)return;let i=t.releaseAt??this.releaseAt??void 0;this.setSeatsLocal(s,"blocked");try{await this.api.block(this.key,s,{...t,releaseAt:i}),this.clearSelection(),this.done("block",s,i?`Blocked ${s.length} \u2014 auto-release ${new Date(i).toLocaleString()}.`:`Blocked ${s.length} seat${s.length===1?"":"s"}.`)}catch(o){this.setSeatsLocal(s,"free"),this.toastErr(o instanceof g&&o.status===409?"Some seats were just taken. Try again.":"Couldn't block those seats."),this.opts.onError?.(o)}}async unblock(e){let t=(e??this.selectionLabels()).filter(s=>this.status.get(s)==="blocked");if(t.length){this.setSeatsLocal(t,"free");try{await this.api.unblock(this.key,t),this.clearSelection(),this.done("unblock",t,`Unblocked ${t.length} seat${t.length===1?"":"s"}.`)}catch(s){this.setSeatsLocal(t,"blocked"),this.toastErr("Couldn't unblock those seats."),this.opts.onError?.(s)}}}async unblockAll(){let e=[...this.status.entries()].filter(([,t])=>t==="blocked").map(([t])=>t);if(e.length){this.setSeatsLocal(e,"free");try{let t=await this.api.unblockAll(this.key);this.clearSelection(),this.done("unblockAll",e,`Unblocked ${t.freed} seat${t.freed===1?"":"s"}.`)}catch(t){await this.resnapshot(),this.toastErr("Couldn't mark everything for sale."),this.opts.onError?.(t)}}}async cancelBooking(e,t){let s=e.filter(i=>this.status.get(i)==="booked");if(!(!s.length||!t)){this.setSeatsLocal(s,"free");try{await this.api.unbook(this.key,s,t),this.clearSelection(),this.done("cancelBooking",s,`Released ${s.length} booked unit${s.length===1?"":"s"}.`)}catch(i){this.setSeatsLocal(s,"booked"),this.toastErr("Couldn't release that booked inventory. Check the booking reference."),this.opts.onError?.(i)}}}async setCategory(e,t){let s=this.doc?.categories.find(o=>o.key===e),i=[...new Set(t??this.selectionLabels())].filter(o=>this.labelToSeat.has(o));if(!(!s||!i.length))try{let o=await this.withAuthRetry(()=>this.api.setCategory(this.key,i,e)),a={};for(let l of o.labels){let r=this.labelToSeat.get(l),d=this.labelToId.get(l);r&&(r.categoryKey=e),d&&(a[d]=e)}this.renderer?.setSeatCategories?.(a),this.clearSelection(),this.done("setCategory",o.labels,`Assigned ${o.labels.length.toLocaleString()} object${o.labels.length===1?"":"s"} to ${s.label??s.key}.`)}catch(o){this.toastErr("Couldn't update those event categories."),this.opts.onError?.(o)}}async setTableBooking(e,t,s={}){let i=[...new Set(e)].filter(Boolean);if(!(!i.length||this.tableBookingSaving)){if(this.inventoryModelVersion!==2){this.toastErr("Table booking controls require inventory model 2 for this event.");return}this.tableBookingSaving=!0,this.mode==="tables"&&this.renderTablesRail();try{let o=await this.withAuthRetry(()=>this.api.setTableBooking(this.key,i,t,s));await this.reloadEventChart(),this.done("setTableBooking",o.tableIds,`Updated ${o.tableIds.length.toLocaleString()} table${o.tableIds.length===1?"":"s"} to ${t==="individual"?"per-chair":t==="whole"?"whole-table":"flexible-group"} booking.`)}catch(o){let a=o instanceof g&&o.code==="table_inventory_in_use";this.toastErr(a?"Release held, booked, or blocked table inventory before changing its booking mode.":"Couldn't update table booking. No table mode was changed."),this.opts.onError?.(o)}finally{this.tableBookingSaving=!1,this.mode==="tables"&&this.renderTablesRail()}}}async reloadEventChart(){let e=await this.withAuthRetry(()=>this.api.chart(this.key));this.inventoryModelVersion=e.event.inventoryModelVersion===2?2:1;let t=await this.organizerAssetUrls.prepareRendererChart(e.doc);this.renderer?.destroy(),this.renderer=null,this.doc=t,this.labelToId.clear(),this.labelToSeat.clear(),this.allIds=[],this.gaUnitLabelSet.clear(),this.status.clear(),this.counts={held:0,booked:0,blocked:0},this.serverBaseline=null,this.lastSelectionLabels.clear(),this.buildUnitUniverse(t),this.buildRenderer(),this.buildSectionOptions(),await Promise.all([this.resnapshot(),this.refreshAvailability()]),this.paintRail()}selectAll(){if(this.mode==="select")return this.selectByLabels([...this.labelToSeat.values()].filter(t=>this.canSelectObject(t)).map(t=>t.label));let e=this.renderer?.selectAllSelectable()??[];return this.syncSelection(),e}selectSection(e){if(!this.renderer)return[];let t=this.renderer.getSelectableInSection(e);return this.renderer.selectByLabels(t.map(s=>s.label)),this.syncSelection(),this.renderer.getSelection()}setFilteredSection(e){let t=e.trim(),s=this.filterableSections().filter(a=>a.label===t);if(!s.length)throw new Error(`Unknown event section: ${e}`);let i=t!==this.filteredSectionLabel;this.filteredSectionLabel=t,this.mode==="filterSections"&&(this.applyFilteredSectionCanvas(s),this.renderFilterSectionsRail());let o=s.map(({id:a,label:l,seatCount:r,zone:d})=>({id:a,label:l,seatCount:r,...d?{zone:d}:{}}));return i&&this.opts.onFilteredSectionChange?.(o),o}clearFilteredSection(){let e=this.filteredSectionLabel!==null;this.filteredSectionLabel=null,this.renderer?.setDimmedSections(null),this.renderer?.clearSectionFocus(),this.renderer?.zoomToFit(),this.mode==="filterSections"&&this.renderFilterSectionsRail(),e&&this.opts.onFilteredSectionChange?.([])}getFilteredSections(){return this.filteredSectionLabel?this.filterableSections().filter(e=>e.label===this.filteredSectionLabel).map(({id:e,label:t,seatCount:s,zone:i})=>({id:e,label:t,seatCount:s,...i?{zone:i}:{}})):[]}selectByLabels(e){let t=e;if(this.mode==="select"){let s=new Set(this.selectionLabels()),i=this.selectionCap(),o=[...new Set(e)].filter(l=>!s.has(l)).map(l=>this.labelToSeat.get(l)).filter(l=>!!l&&this.canSelectObject(l)),a=Math.max(0,i-s.size);o.length>a&&this.opts.onSelectionLimit?.(i),t=o.slice(0,a).map(l=>l.label)}return this.renderer?.selectByLabels(t),this.syncSelection(),this.getSelection()}selectObjects(e){return this.selectByLabels(e)}deselectObjects(e){let t=e.map(s=>this.labelToId.get(s)).filter(s=>!!s);return this.renderer?.deselect(t),this.syncSelection(),this.getSelection()}selectCategories(e){let t=new Set(e);return this.selectByLabels([...this.labelToSeat.values()].filter(s=>t.has(s.categoryKey)).map(s=>s.label))}deselectCategories(e){let t=new Set(e);return this.deselectObjects(this.getSelection().filter(s=>t.has(s.categoryKey)).map(s=>s.label))}setSelectableObjects(e){this.opts.selectableObjects=[...e],this.selectableObjectLabels=new Set(e),this.reconcileSelectPolicy()}setUnavailableObjectsSelectable(e){this.opts.unavailableObjectsSelectable=e,this.reconcileSelectPolicy()}setObjectSelectable(e){this.opts.isObjectSelectable=e,this.reconcileSelectPolicy()}setMaxSelectedObjects(e){this.assertSelectionOptions(e,this.opts.numberOfPlacesToSelect),this.opts.maxSelectedObjects=e,this.trimSelectionToCap(),this.updateRendererInteraction(),this.syncSelection()}setNumberOfPlacesToSelect(e){this.assertSelectionOptions(this.opts.maxSelectedObjects,e),this.opts.numberOfPlacesToSelect=e,this.selectionWasValid=null,this.trimSelectionToCap(),this.updateRendererInteraction(),this.syncSelection()}getSelectionValidity(){let e=this.opts.numberOfPlacesToSelect;if(e===void 0)return null;let t=this.getSelection(),s=t.length;return{isValid:s===e,count:s,required:e,remaining:Math.max(0,e-s),objects:t}}clearSelection(){this.renderer?.clearSelection(),this.syncSelection()}getSelection(){return this.renderer?.getSelection()??[]}getReport(){return this.api.report(this.key).then(e=>(this.applyReportRevenue(e),e))}getControlRoomSnapshot(e=this.trendWindowMinutes){return this.setTrendWindow(e)}getConnection(){return{status:this.connectionStatus,lastMessageAt:this.lastMessageAt}}getLog(e={}){return this.api.log(this.key,e)}async setHoldTtl(e){try{await this.api.setHoldTtl(this.key,e),this.done("setHoldTtl",[],e?`Hold window set to ${Math.round(e/6e4)} min.`:"Hold window reset.")}catch(t){this.toastErr("Couldn't update the hold window."),this.opts.onError?.(t)}}zoomToFit(){if(this.mode==="filterSections"&&this.filteredSectionLabel){this.clearFilteredSection();return}this.renderer?.clearSectionFocus(),this.renderer?.zoomToFit()}destroy(){if(this.closed=!0,this.reconnectTimer&&clearTimeout(this.reconnectTimer),this.feedTimer&&clearInterval(this.feedTimer),this.toastTimer&&clearTimeout(this.toastTimer),this.liveEventTimer&&clearTimeout(this.liveEventTimer),this.kpiCleanupTimer&&clearTimeout(this.kpiCleanupTimer),this.followLiveTimer&&clearTimeout(this.followLiveTimer),this.followSeatTimer&&clearTimeout(this.followSeatTimer),this.unblockAllConfirmTimer&&clearTimeout(this.unblockAllConfirmTimer),this.paintHandle!==null&&typeof cancelAnimationFrame=="function"&&cancelAnimationFrame(this.paintHandle),this.paintHandle=null,this.channels?.destroy(),this.channels=null,this.channelsLoading=null,this.tokenRefreshTimer&&clearTimeout(this.tokenRefreshTimer),this.layoutObserver?.disconnect(),this.layoutObserver=null,this.root?.removeEventListener("keydown",this.onKeyDown),this.els.rail?.removeEventListener("click",this.onRailClick),typeof document<"u"&&document.removeEventListener("fullscreenchange",this.onFullscreenChange),this.ws){try{this.ws.close()}catch{}this.ws=null}this.renderer?.destroy(),this.renderer=null,this.organizerAssetUrls.dispose(),this.root&&this.root.parentNode===this.host&&this.host.removeChild(this.root)}buildRenderer(){if(!this.doc)return;let e=this.isBulkSelectMode();this.renderer=new ye(this.mapHost,{manageMode:!0,marqueeSelect:e,maxSelection:this.selectionCap(),selectableStatuses:this.selectableStatuses(),currency:this.currency,onSelect:t=>this.handleSeatSelect(t),onDeselect:()=>this.syncSelection(),onSelectionLimit:t=>this.opts.onSelectionLimit?.(t),onMarquee:()=>this.syncSelection(),onSectionTap:t=>{if(this.mode==="filterSections"){let s=this.filterableSections().find(i=>i.id===t);s&&this.setFilteredSection(s.label);return}this.renderer?.focusSection(t),this.channels?.handleSectionFocus(t)},onViewChange:()=>{this.updateZoomHint(),this.channels?.handleViewChange()}}),this.renderer.setChart(this.doc),this.repaintAll(),this.applyHeatOverlay(),this.updateZoomHint()}isBulkSelectMode(){return this.mode==="block"||this.mode==="categories"||this.mode==="channels"&&this.channels?.usesMarqueeSelection()===!0}selectableStatuses(){return this.mode==="block"?["free","not_for_sale"]:this.mode==="select"||this.mode==="categories"||this.mode==="inspect"||this.isBulkSelectMode()||this.mode==="channels"&&this.channels?.canSelect()===!0?["free","held","booked","not_for_sale"]:[]}updateRendererInteraction(){let e=this.isBulkSelectMode();this.renderer?.setManageInteraction({manageMode:!0,marqueeSelect:e,maxSelection:this.selectionCap(),selectableStatuses:this.selectableStatuses()}),this.updateZoomHint()}handleSeatSelect(e){if(this.mode==="select"&&!this.canSelectObject(e)){this.renderer?.deselect([e.id]),this.syncSelection();return}if(this.mode==="inspect"){let t=this.getSelection().filter(s=>s.id!==e.id).map(s=>s.id);t.length&&this.renderer?.deselect(t)}this.syncSelection()}buildUnitUniverse(e){for(let t of Se(e))this.labelToId.set(t.label,t.id),this.labelToSeat.set(t.label,t),this.allIds.push(t.id);for(let t of xe(e))for(let s of Le(t))this.labelToId.has(s)||this.gaUnitLabelSet.add(s)}unitTotal(){return this.allIds.length+this.gaUnitLabelSet.size}knownLabels(){return[...this.labelToId.keys(),...this.gaUnitLabelSet]}repaintAll(){let e=this.renderer;if(!e)return;this.allIds.length&&e.setStatus(this.allIds,"free");let t={free:[],held:[],booked:[],not_for_sale:[]};for(let[s,i]of this.status.entries()){let o=this.labelToId.get(s);o&&t[k(i)].push(o)}["held","booked","not_for_sale"].forEach(s=>{t[s].length&&e.setStatus(t[s],s)})}async connect(){if(this.closed)return;let e;try{if(e=(await this.withAuthRetry(()=>this.api.subscribeTicket(this.key))).protocols,!e.length)throw new Error("manage_subscribe_ticket_missing")}catch(s){this.setLive(!1),this.opts.onError?.(s),this.scheduleReconnect();return}if(this.closed)return;let t;try{t=new WebSocket(this.api.socketUrl(this.key),e)}catch(s){this.opts.onError?.(s),this.scheduleReconnect();return}this.ws=t,t.onopen=()=>{this.attempt=0,this.setLive(!0),this.resnapshot().then(()=>this.refreshControlRoom()).catch(s=>this.opts.onError?.(s)),this.refreshAvailability()},t.onmessage=s=>this.onMessage(s),t.onclose=()=>{this.ws===t&&(this.ws=null),this.setLive(!1),this.scheduleReconnect()},t.onerror=()=>{try{t.close()}catch{}}}scheduleReconnect(){if(this.closed||this.reconnectTimer)return;let e=Math.min(1e3*2**Math.min(this.attempt++,5),15e3);this.reconnectTimer=setTimeout(()=>{this.reconnectTimer=null,this.connect()},e)}onMessage(e){let t;try{t=JSON.parse(typeof e.data=="string"?e.data:"")}catch{return}if(!t||typeof t!="object")return;this.lastMessageAt=Date.now();let s=t;if((Array.isArray(s.hidden)||Array.isArray(s.closed))&&this.updateEffectiveAvailability(s.hidden,s.closed),s.type==="presence"){typeof s.shoppingSessions=="number"&&typeof s.activeHolds=="number"&&(this.livePresence={at:Date.now(),value:{shoppingSessions:s.shoppingSessions,activeHolds:s.activeHolds}},this.controlRoomSnapshot&&(this.controlRoomSnapshot={...this.controlRoomSnapshot,presence:this.livePresence.value},this.opts.onControlRoom?.(this.controlRoomSnapshot)),this.lastSyncedAt=Date.now(),this.recomputeTallies(),this.paintMonitorInsights());return}if(s.type!=="hidden"){if(s.seats&&typeof s.seats=="object")this.applySnapshot(s.seats,typeof s.default=="string"?s.default:void 0);else if(Array.isArray(s.changes)){let i=[],o=new Map;for(let l of s.changes){let r=["free","held","booked","blocked"].includes(l.status)?l.status:"free",d=this.status.get(l.label)??"free";if(d===r)continue;this.setStatusLabel(l.label,r,d);let n=this.labelToId.get(l.label);n&&(this.renderer?.setStatus([n],k(r)),i.push(n));let p=this.verbFor(d,r),m=`${p}:${r}`,b=o.get(m)??{labels:[],verb:p,status:r};b.labels.push(l.label),o.set(m,b)}for(let l of o.values()){let r=this.pushActivity(l.labels,l.verb,l.status);r&&this.paintSpatialActivity(r)}i.length&&(this.lastSyncedAt=Date.now(),this.afterPaint());let a=typeof s.bookedValue?.gross=="number"?s.bookedValue.gross:s.revenue?.gross;typeof a=="number"&&Number.isFinite(a)&&this.applyLiveGross(a),this.recomputeTallies()}}}applyLiveGross(e){if(this.liveGross={at:Date.now(),value:e},this.authoritativeGrossRevenue=e,this.revenueStatus="current",this.controlRoomSnapshot){let s={...this.controlRoomSnapshot.bookedValue??this.controlRoomSnapshot.revenue,gross:e};this.controlRoomSnapshot={...this.controlRoomSnapshot,bookedValue:s,revenue:s},this.opts.onControlRoom?.(this.controlRoomSnapshot)}}setStatusLabel(e,t,s=this.status.get(e)??"free"){this.status.set(e,t),s!==t&&(s!=="free"&&(this.counts[s]-=1),t!=="free"&&(this.counts[t]+=1))}async resnapshot(){try{let e=await this.api.objects(this.key);this.applySnapshot(e.seats),this.updateEffectiveAvailability(e.hidden,e.closed),this.lastMessageAt=Date.now()}catch{}}applySnapshot(e,t){let s=o=>["free","held","booked","blocked"].includes(o)?o:"free",i=new Map;if(t!==void 0){let o=s(t);for(let a of this.knownLabels())i.set(a,o)}for(let[o,a]of Object.entries(e))i.set(o,s(a));this.status=i,this.modelVersion+=1,this.recountAll(),this.lastSyncedAt=Date.now(),this.repaintAll(),this.afterPaint(),this.recomputeTallies()}recountAll(){let e={held:0,booked:0,blocked:0};for(let t of this.status.values())t!=="free"&&(e[t]+=1);this.counts=e}setSeatsLocal(e,t){let s=[];for(let i of e){this.setStatusLabel(i,t);let o=this.labelToId.get(i);o&&s.push(o)}s.length&&this.renderer?.setStatus(s,k(t)),this.afterPaint(),this.recomputeTallies()}afterPaint(){this.keepLive&&typeof document<"u"&&document.hidden&&this.renderer?.forceDraw()}activityColor(e){return e==="held"?"#f4b740":e==="booked"?"#22a06b":e==="blocked"?"#8b94ac":"#6e7bff"}sectionsForLabels(e){let t=new Set;for(let i of e){let o=this.labelToSeat.get(i);if(!o)continue;let a=this.sectionByObject.get(o.rowId);a&&a!==f&&t.add(a)}let s=[...t];return{ids:s,labels:s.map(i=>this.sectionLabelById.get(i)??i)}}pulseSeatLabels(e,t){let s=this.activityColor(t);for(let i of e.slice(0,Ce)){let o=this.labelToId.get(i);o&&this.renderer?.flashSeat(o,s)}}paintSpatialActivity(e){let t=e.sectionIds??this.sectionsForLabels(e.labels).ids,s=this.renderer?.getFocusedSection()??null,i=this.followLive&&t.length===1&&(e.status==="held"||e.status==="booked");if(i&&s===t[0]){this.pulseSeatLabels(e.labels,e.status);return}if(i){this.followLiveTimer&&clearTimeout(this.followLiveTimer),this.followSeatTimer&&clearTimeout(this.followSeatTimer),this.followLiveTimer=setTimeout(()=>{this.followLiveTimer=null,this.renderer?.focusSection(t[0]),this.followSeatTimer=setTimeout(()=>{this.followSeatTimer=null,this.pulseSeatLabels(e.labels,e.status)},520)},220);return}if(!s&&t.length){let o=this.activityColor(e.status);for(let a of t.slice(0,D))this.renderer?.flashSection(a,o);return}(!t.length||s&&t.includes(s))&&this.pulseSeatLabels(e.labels,e.status)}locateSection(e){this.renderer?.focusSection(e)}locateActivity(e){let t=this.feed.find(i=>i.id===e);if(!t)return;let s=t.sectionIds??this.sectionsForLabels(t.labels).ids;if(this.followSeatTimer&&clearTimeout(this.followSeatTimer),s.length===1){this.locateSection(s[0]),this.followSeatTimer=setTimeout(()=>{this.followSeatTimer=null,this.pulseSeatLabels(t.labels,t.status)},520);return}this.zoomToFit(),this.followSeatTimer=setTimeout(()=>{if(this.followSeatTimer=null,s.length){let i=this.activityColor(t.status);for(let o of s.slice(0,D))this.renderer?.flashSection(o,i)}else this.pulseSeatLabels(t.labels,t.status)},280)}showLiveEvent(e){let t=this.els.liveevent;if(!t)return;let s=e.sectionLabels??[],i=s.length===1?s[0]:s.length>1?`${s.length} sections`:e.label,o=e.count===1?"seat":"seats";t.innerHTML=`<span class="slm-liveeventdot" style="background:${this.activityColor(e.status)}"></span>
|
|
283
297
|
<span class="slm-liveeventcopy">${h(i)} \xB7 ${e.count.toLocaleString()} ${o} ${h(e.verb)}</span>
|
|
284
|
-
<span class="slm-liveeventhint">Live</span>`,t.classList.add("on"),this.liveEventTimer&&clearTimeout(this.liveEventTimer),this.liveEventTimer=setTimeout(()=>{this.liveEventTimer=null,t.classList.remove("on"),t.innerHTML=""},2800)}applyReportRevenue(e){this.authoritativeGrossRevenue=e.report.byCategory.reduce((t,s)=>{let i=Number.isFinite(s.bookedValue)?s.bookedValue:s.bookedRevenue;return t+(Number.isFinite(i)?i:0)},0),this.revenueStatus="current",this.recomputeTallies()}async refreshControlRoom(){let e=++this.revenueRequest,t=Date.now();try{let s=await this.api.controlRoom(this.key,this.trendWindowMinutes),i=s.bookedValue??s.revenue??{gross:0,bySection:[]},o=(i.bySection??[]).map(
|
|
298
|
+
<span class="slm-liveeventhint">Live</span>`,t.classList.add("on"),this.liveEventTimer&&clearTimeout(this.liveEventTimer),this.liveEventTimer=setTimeout(()=>{this.liveEventTimer=null,t.classList.remove("on"),t.innerHTML=""},2800)}applyReportRevenue(e){this.authoritativeGrossRevenue=e.report.byCategory.reduce((t,s)=>{let i=Number.isFinite(s.bookedValue)?s.bookedValue:s.bookedRevenue;return t+(Number.isFinite(i)?i:0)},0),this.revenueStatus="current",this.recomputeTallies()}async refreshControlRoom(){let e=++this.revenueRequest,t=Date.now();try{let s=await this.api.controlRoom(this.key,this.trendWindowMinutes),i=s.bookedValue??s.revenue??{gross:0,bySection:[]},o=(i.bySection??[]).map(r=>{let d=Number.isFinite(r.bookedValue)?r.bookedValue:r.bookedRevenue;return{...r,bookedValue:d??0,bookedRevenue:d??0}}),a={...i,bySection:o},l={...s,bookedValue:a,revenue:a};if(e===this.revenueRequest){if(this.livePresence&&this.livePresence.at>=t?l={...l,presence:this.livePresence.value}:this.livePresence=null,this.liveGross&&this.liveGross.at>=t){let r={...l.bookedValue,gross:this.liveGross.value};l={...l,bookedValue:r,revenue:r}}else this.liveGross=null;this.controlRoomSnapshot=l,this.rebaseServerTotals(l),this.lastSyncedAt=Date.now(),this.authoritativeGrossRevenue=l.bookedValue.gross,this.authoritativeCurrency=l.currency,this.currency=l.currency,this.revenueStatus="current",this.recomputeTallies(),this.applyHeatOverlay(),this.paintMonitorInsights(),this.opts.onControlRoom?.(l)}return l}catch(s){throw e===this.revenueRequest&&(this.revenueStatus="stale",this.recomputeTallies()),s}}rebaseServerTotals(e){let t=e.totals;if(!t||["free","held","booked","blocked"].some(s=>!Number.isFinite(t[s]))){this.serverBaseline=null;return}this.serverBaseline={model:this.modelVersion,server:{free:t.free,held:t.held,booked:t.booked,blocked:t.blocked},client:this.clientTallies()}}clientTallies(){let{held:e,booked:t,blocked:s}=this.counts;return{held:e,booked:t,blocked:s,free:Math.max(0,this.unitTotal()-e-t-s)}}buildTallies(){let e=this.clientTallies(),t=this.serverBaseline?.model===this.modelVersion?this.serverBaseline:null,s=l=>t?Math.max(0,t.server[l]+(e[l]-t.client[l])):e[l],i=this.controlRoomSnapshot?.event?.seatTotal,o={free:s("free"),held:s("held"),booked:s("booked"),blocked:s("blocked"),total:Number.isFinite(i)?i:this.unitTotal(),capacityPct:0,sellThroughPct:0,bookedValue:this.authoritativeGrossRevenue,grossRevenue:this.authoritativeGrossRevenue,bookedValueStatus:this.revenueStatus,revenueStatus:this.revenueStatus,currency:this.currency};o.capacityPct=o.total?Math.round(o.booked/o.total*100):0;let a=o.total-o.blocked;return o.sellThroughPct=a>0?Math.round(o.booked/a*100):0,o}recomputeTallies(){if(!this.closed){if(typeof requestAnimationFrame!="function"){this.flushTallies();return}this.paintHandle===null&&(this.paintHandle=requestAnimationFrame(()=>{this.paintHandle=null,this.flushTallies()}))}}flushTallies(){if(this.closed)return;let e=this.buildTallies();this.paintKpis(e),this.mode==="view"?(this.paintLegend(e),this.paintMonitorInsights()):this.mode==="inspect"?this.renderInspectRail(this.getSelection()):this.mode==="block"?this.paintSelBar(this.getSelection()):this.mode==="channels"&&this.channels?.handleSelectionChange(),this.opts.onTallies?.(e)}verbFor(e,t){return t==="held"?"held":t==="booked"?"booked":t==="blocked"?"blocked":t==="free"?e==="blocked"?"unblocked":e==="booked"?"cancelled":"released":t}pushActivity(e,t,s,i=Date.now()){let o=e[0];if(!o)return null;let a=this.sectionsForLabels(e),l={id:`${o}:${i}:${Math.random().toString(36).slice(2,6)}`,at:i,label:o,labels:[...e],count:e.length,verb:t,status:s,sectionIds:a.ids,sectionLabels:a.labels};return this.feed.unshift(l),this.feed.length>x&&(this.feed.length=x),this.mode==="view"&&this.paintFeed(),this.showLiveEvent(l),this.opts.onActivity?.(l),l}seedFeed(e){let t={hold:"held",book:"booked",release:"released",expire:"expired",block:"blocked",unblock:"unblocked",unbook:"cancelled"},s={hold:"held",book:"booked",release:"free",expire:"free",block:"blocked",unblock:"free",unbook:"free"};for(let i of e){let o=i.labels[0];if(!o)continue;let a=this.sectionsForLabels(i.labels),l={id:`log:${i.id}`,at:i.at,label:o,labels:[...i.labels],count:i.labels.length,verb:t[i.action]??i.action,status:s[i.action]??"free",sectionIds:a.ids,sectionLabels:a.labels};this.feed.push(l),this.opts.onActivity?.(l)}this.feed.sort((i,o)=>o.at-i.at),this.feed.length>x&&(this.feed.length=x),this.mode==="view"&&this.paintFeed()}startFeedClock(){this.feedTimer=setInterval(()=>{this.mode==="view"&&(this.paintFeed(),this.paintMonitorInsights())},1e4)}selectionCap(){return this.mode!=="select"?1e6:this.opts.numberOfPlacesToSelect??this.opts.maxSelectedObjects??1e6}canSelectObject(e){let t=this.status.get(e.label)??"free",s=this.opts.unavailableObjectsSelectable??!0,i=t==="free"||s||this.selectableObjectLabels.has(e.label);if(!this.opts.isObjectSelectable)return i;try{return this.opts.isObjectSelectable(e,i)===!0}catch(o){return this.opts.onError?.(o),!1}}applyInitialSelection(){this.initialSelectionApplied||this.mode!=="select"||!this.renderer||(this.initialSelectionApplied=!0,this.selectByLabels(this.opts.selectedObjects??[]))}trimSelectionToCap(){if(this.mode!=="select")return;let e=this.getSelection(),t=this.selectionCap();e.length<=t||this.renderer?.deselect(e.slice(t).map(s=>s.id))}reconcileSelectPolicy(){if(this.mode!=="select")return;let e=this.getSelection().filter(t=>!this.canSelectObject(t));e.length&&this.renderer?.deselect(e.map(t=>t.id)),this.trimSelectionToCap(),this.syncSelection()}selectionLabels(){return this.getSelection().map(e=>e.label)}syncSelection(){let e=this.getSelection();this.mode==="block"?this.paintSelBar(e):this.mode==="inspect"?this.renderInspectRail(e):this.mode==="select"?this.paintSelectSelection(e):this.mode==="categories"?this.paintCategorySelection(e):this.mode==="channels"&&this.channels?.handleSelectionChange();let t=new Map(e.map(s=>[s.label,s]));if(this.mode==="select"){for(let[i,o]of t)this.lastSelectionLabels.has(i)||this.opts.onObjectSelected?.(o);for(let i of this.lastSelectionLabels)if(!t.has(i)){let o=this.labelToSeat.get(i);o&&this.opts.onObjectDeselected?.(o)}let s=this.getSelectionValidity();s&&(this.opts.onSelectionValidityChange?.(s),this.selectionWasValid!==s.isValid&&(s.isValid?this.opts.onSelectionValid?.(s):this.opts.onSelectionInvalid?.(s)),this.selectionWasValid=s.isValid)}this.lastSelectionLabels=new Set(t.keys()),this.opts.onSelectionChange?.(e)}buildChrome(){let e=document.createElement("div");e.className="slm",e.tabIndex=0,e.setAttribute("role","region"),e.setAttribute("aria-label","SeatLayer live control room");let t=C(this.opts.theme);for(let[i,o]of Object.entries(t))e.style.setProperty(i,o);e.innerHTML=`
|
|
285
299
|
<div class="slm-bar">
|
|
286
300
|
<div class="slm-modes" data-ref="modes" role="tablist" aria-label="Manager tools">
|
|
287
301
|
<button class="slm-mode" role="tab" data-mode="view" title="Monitor (M)" aria-keyshortcuts="M">Monitor</button>
|
|
288
302
|
<button class="slm-mode" role="tab" data-mode="inspect" title="Inspect (I)" aria-keyshortcuts="I">Inspect</button>
|
|
303
|
+
<button class="slm-mode" role="tab" data-mode="select" title="Select (X)" aria-keyshortcuts="X">Select</button>
|
|
304
|
+
<button class="slm-mode" role="tab" data-mode="filterSections" title="Filter sections (L)" aria-keyshortcuts="L">Filter</button>
|
|
289
305
|
<button class="slm-mode" role="tab" data-mode="block" title="Block (B)" aria-keyshortcuts="B">Block</button>
|
|
290
306
|
<button class="slm-mode" role="tab" data-mode="sections" title="Sections (S)" aria-keyshortcuts="S">Sections</button>
|
|
307
|
+
<button class="slm-mode" role="tab" data-mode="categories" title="Categories (G)" aria-keyshortcuts="G" hidden>Categories</button>
|
|
308
|
+
<button class="slm-mode" role="tab" data-mode="tables" title="Tables (T)" aria-keyshortcuts="T" hidden>Tables</button>
|
|
291
309
|
<button class="slm-mode" role="tab" data-mode="channels" title="Channels (C)" aria-keyshortcuts="C" hidden>Channels</button>
|
|
292
310
|
</div>
|
|
293
311
|
<select class="slm-tools" data-ref="tools" aria-label="Manager tools"></select>
|
|
@@ -312,10 +330,10 @@ import{A as he,C as pe,a as y,b as L,c as j,d as _,e as V,f as q,g as U,h as K,i
|
|
|
312
330
|
<aside class="slm-rail"><div class="slm-railscroll" data-ref="rail"></div></aside>
|
|
313
331
|
</div>
|
|
314
332
|
<div class="slm-toast" data-ref="toast"></div>
|
|
315
|
-
`,this.host.appendChild(e),this.root=e,this.updateContainerLayout(),typeof ResizeObserver<"u"&&(this.layoutObserver=new ResizeObserver(()=>this.updateContainerLayout()),this.layoutObserver.observe(e));let s=i=>e.querySelector(`[data-ref="${i}"]`);this.mapHost=s("maphost"),this.els={modes:s("modes"),tools:s("tools"),livetext:s("livetext"),kpis:s("kpis"),follow:s("follow"),heat:s("heat"),fullscreen:s("fullscreen"),zoomhint:s("zoomhint"),liveevent:s("liveevent"),rail:s("rail"),toast:s("toast"),zfit:s("zfit")},this.els.modes.querySelectorAll("[data-mode]").forEach(i=>i.addEventListener("click",()=>this.setMode(i.dataset.mode))),this.els.tools.addEventListener("change",()=>this.setMode(this.els.tools.value)),this.els.zfit.addEventListener("click",()=>this.zoomToFit()),this.els.follow.addEventListener("click",()=>this.setFollowLive(!this.followLive)),this.els.heat.addEventListener("click",()=>this.setHeatOverlay(!this.heatEnabled)),this.els.fullscreen.addEventListener("click",()=>this.toggleFullscreen()),e.addEventListener("keydown",this.onKeyDown),this.els.rail.addEventListener("click",this.onRailClick),document.addEventListener("fullscreenchange",this.onFullscreenChange),this.paintModeTabs(),this.paintFollowLiveButton(),this.paintHeatButton(),this.paintFullscreenButton()}updateContainerLayout(){let e=this.root?.getBoundingClientRect().width||this.host.clientWidth;this.root?.classList.toggle("compact",e>0&&e<800),this.channels?.handleLayoutChange()}buildSectionOptions(){if(this.doc)try{let e=
|
|
316
|
-
<b>${
|
|
317
|
-
${
|
|
318
|
-
</div>`}).join(""),o&&(this.kpiCleanupTimer&&clearTimeout(this.kpiCleanupTimer),this.kpiCleanupTimer=setTimeout(()=>{this.kpiCleanupTimer=null,this.activeKpiDeltas.clear(),this.els.kpis?.querySelectorAll(".slm-kpidelta").forEach(
|
|
333
|
+
`,this.host.appendChild(e),this.root=e,this.updateContainerLayout(),typeof ResizeObserver<"u"&&(this.layoutObserver=new ResizeObserver(()=>this.updateContainerLayout()),this.layoutObserver.observe(e));let s=i=>e.querySelector(`[data-ref="${i}"]`);this.mapHost=s("maphost"),this.els={modes:s("modes"),tools:s("tools"),livetext:s("livetext"),kpis:s("kpis"),follow:s("follow"),heat:s("heat"),fullscreen:s("fullscreen"),zoomhint:s("zoomhint"),liveevent:s("liveevent"),rail:s("rail"),toast:s("toast"),zfit:s("zfit")},this.els.modes.querySelectorAll("[data-mode]").forEach(i=>i.addEventListener("click",()=>this.setMode(i.dataset.mode))),this.els.tools.addEventListener("change",()=>this.setMode(this.els.tools.value)),this.els.zfit.addEventListener("click",()=>this.zoomToFit()),this.els.follow.addEventListener("click",()=>this.setFollowLive(!this.followLive)),this.els.heat.addEventListener("click",()=>this.setHeatOverlay(!this.heatEnabled)),this.els.fullscreen.addEventListener("click",()=>this.toggleFullscreen()),e.addEventListener("keydown",this.onKeyDown),this.els.rail.addEventListener("click",this.onRailClick),document.addEventListener("fullscreenchange",this.onFullscreenChange),this.paintModeTabs(),this.paintFollowLiveButton(),this.paintHeatButton(),this.paintFullscreenButton()}updateContainerLayout(){let e=this.root?.getBoundingClientRect().width||this.host.clientWidth;this.root?.classList.toggle("compact",e>0&&e<800),this.channels?.handleLayoutChange()}filterableSections(){return this.sectionsBase?[...this.sectionsBase.sections,...this.sectionsBase.ungrouped?[this.sectionsBase.ungrouped]:[]]:[]}applyFilteredSectionCanvas(e=this.filterableSections().filter(t=>t.label===this.filteredSectionLabel)){if(!this.renderer||this.mode!=="filterSections"||!e.length)return;let t=new Set(e.map(n=>n.id)),s=this.filterableSections().filter(n=>!t.has(n.id)).map(n=>n.id);this.renderer.clearSectionFocus(),this.renderer.setClosedSections(null),this.renderer.setDimmedSections(s);let i=e.flatMap(n=>n.seatLabels).map(n=>this.labelToSeat.get(n)).filter(n=>!!n&&Number.isFinite(n.x)&&Number.isFinite(n.y));if(!i.length){this.renderer.focusRegion(e[0].id);return}let o=Math.min(...i.map(n=>n.x)),a=Math.min(...i.map(n=>n.y)),l=Math.max(...i.map(n=>n.x)),r=Math.max(...i.map(n=>n.y)),d=Math.max(40,Math.max(l-o,r-a)*.08);this.renderer.focusRegion({x:o-d,y:a-d,width:Math.max(1,l-o)+d*2,height:Math.max(1,r-a)+d*2})}buildSectionOptions(){if(this.doc)try{let e=ke(this.doc);this.sectionsBase=e,this.sectionOptions=[],this.sectionByObject=new Map(e.objectToSection),this.sectionLabelById.clear();for(let t of e.sections)this.sectionOptions.push({id:t.id,label:t.label}),this.sectionLabelById.set(t.id,t.label);e.ungrouped&&(this.sectionOptions.push({id:f,label:e.ungrouped.label}),this.sectionLabelById.set(f,e.ungrouped.label))}catch{}}paintModeTabs(){let e=[];this.els.modes?.querySelectorAll("[data-mode]").forEach(s=>{let i=s,o=i.dataset.mode,a=o==="channels"?this.channelCaps.view:o==="categories"?this.categoryCanManage:o==="tables"?this.tableCanManage:!0;if(i.hidden=!a,!a)return;e.push({mode:o,label:i.textContent??o});let l=o===this.mode;i.classList.toggle("on",l),i.setAttribute("aria-selected",String(l)),i.tabIndex=l?0:-1});let t=this.els.tools;t&&(t.innerHTML=e.map(s=>`<option value="${s.mode}"${s.mode===this.mode?" selected":""}>${h(s.label)}</option>`).join(""),t.value=this.mode),this.root?.classList.toggle("block-mode",this.mode==="block")}paintFollowLiveButton(){let e=this.els.follow;e&&(e.classList.toggle("on",this.followLive),e.setAttribute("aria-pressed",String(this.followLive)),e.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."))}paintHeatButton(){let e=this.els.heat;e&&(e.classList.toggle("on",this.heatEnabled),e.setAttribute("aria-pressed",String(this.heatEnabled)),e.setAttribute("aria-label",`Booking momentum overlay ${this.heatEnabled?"on":"off"}`),e.setAttribute("title",`${this.heatEnabled?"Hide":"Highlight"} sections booking fastest in the selected time window`),e.textContent="Booking momentum",this.paintMomentumHelp())}paintMomentumHelp(){let e=this.els.rail?.querySelector('[data-ref="momentumhelp"]');if(!e)return;e.hidden=!this.heatEnabled;let t=e.querySelector('[data-ref="momentumcopy"]');if(!t)return;let s=this.controlRoomSnapshot?.velocity.bySection.some(i=>i.netBooked>0);t.textContent=s?"Warmer sections have more completed bookings, adjusted for section size. Holds and viewers are not counted.":`No completed bookings in the last ${this.trendWindowMinutes} minutes.`}paintFullscreenButton(){this.els.fullscreen&&(this.els.fullscreen.textContent=this.isFullscreen()?"Exit full screen":"Full screen")}paintTrendWindow(){this.els.rail?.querySelectorAll("[data-window]").forEach(e=>{let t=Number(e.dataset.window);e.classList.toggle("on",t===this.trendWindowMinutes)})}setLive(e){this.root?.classList.toggle("live",e),this.els.livetext&&(this.els.livetext.textContent=e?"LIVE":"RECONNECTING"),this.paintMonitorInsights();let t=e?"live":"reconnecting";if(t!==this.connectionStatus){this.connectionStatus=t;try{this.opts.onConnectionChange?.(this.getConnection())}catch(s){this.opts.onError?.(s)}}}updateZoomHint(){let e=this.els.zoomhint;if(!e)return;let t=this.mode==="block"&&this.renderer?.getRung?.()!=="seats";e.classList.toggle("on",!!t)}formatKpiDelta(e,t,s){let i=t>0?"+":"\u2212",o=Math.abs(t);return e==="booked-value"?`${i}${y(o,s)}`:e==="booked-pct"?`${i}${o.toLocaleString()}pt`:`${i}${o.toLocaleString()}`}paintKpis(e){if(!this.els.kpis)return;let t=e.bookedValueStatus==="current"?y(e.bookedValue,e.currency):"\u2014",s=this.presenceCounts(),i=[{key:"booked-inventory",raw:e.booked,n:e.booked.toLocaleString(),l:"Booked inventory",dot:"#22a06b",title:"Inventory units booked"},{key:"held-seats",raw:e.held,n:e.held.toLocaleString(),l:"Held inventory",dot:"#f4b740",title:"Inventory currently held"},{key:"free-seats",raw:e.free,n:e.free.toLocaleString(),l:"Available",dot:"#6e7bff",title:"Inventory available to book"},{key:"blocked",raw:e.blocked,n:e.blocked.toLocaleString(),l:"Blocked",dot:"#8b94ac",title:"Inventory withheld from booking"},{key:"viewing-map",raw:s?.shoppingSessions??null,n:s?s.shoppingSessions.toLocaleString():"\u2014",l:"Viewing map",title:"Active map sessions right now"},{key:"active-holds",raw:s?.activeHolds??null,n:s?s.activeHolds.toLocaleString():"\u2014",l:"Active holds",title:"Sessions currently holding inventory"},{key:"booked-pct",raw:e.capacityPct,n:`${e.capacityPct}%`,l:"Booked",title:"Booked inventory as a share of the whole event"},{key:"booked-value",raw:e.bookedValueStatus==="current"?e.bookedValue:null,n:t,l:"Booked value",title:"Configured value attached to booked inventory"}],o=!1;this.els.kpis.innerHTML=i.map(a=>{let l=this.lastKpiValues.get(a.key),r=a.raw!=null&&l!=null&&a.raw!==l,d=r?a.raw-l:0;r&&(o=!0,this.activeKpiDeltas.set(a.key,{text:this.formatKpiDelta(a.key,d,e.currency),down:d<0})),a.raw!=null&&this.lastKpiValues.set(a.key,a.raw);let n=this.activeKpiDeltas.get(a.key);return`<div class="slm-kpi${n?" changed":""}" data-kpi="${a.key}" title="${h(a.title)}">
|
|
334
|
+
<b>${a.dot?`<span class="dot" style="background:${a.dot}"></span>`:""}${a.n}</b><span>${a.l}</span>
|
|
335
|
+
${n?`<span class="slm-kpidelta${n.down?" down":""}">${n.text}</span>`:""}
|
|
336
|
+
</div>`}).join(""),o&&(this.kpiCleanupTimer&&clearTimeout(this.kpiCleanupTimer),this.kpiCleanupTimer=setTimeout(()=>{this.kpiCleanupTimer=null,this.activeKpiDeltas.clear(),this.els.kpis?.querySelectorAll(".slm-kpidelta").forEach(a=>a.remove()),this.els.kpis?.querySelectorAll(".slm-kpi.changed").forEach(a=>a.classList.remove("changed"))},1500))}paintRail(){this.mode==="view"?this.renderViewRail():this.mode==="inspect"?this.renderInspectRail(this.getSelection()):this.mode==="select"?this.renderSelectRail():this.mode==="filterSections"?this.renderFilterSectionsRail():this.mode==="sections"?this.renderSectionsRail():this.mode==="categories"?this.renderCategoriesRail():this.mode==="tables"?this.renderTablesRail():this.mode==="channels"?this.channels?this.channels.paintRail():this.channelsLoading?this.els.rail.innerHTML='<div class="slm-empty" role="status">Loading sales channels\u2026</div>':this.els.rail.innerHTML='<div class="slm-empty" role="alert">Sales channels could not be loaded. Switch away and back to try again.</div>':this.renderBlockRail(),this.updateZoomHint()}renderViewRail(){this.els.rail.innerHTML=`
|
|
319
337
|
<p class="slm-eyebrow">Monitor</p>
|
|
320
338
|
<p class="slm-hint">Read-only. Inventory, map activity and booking movement update on the same live board.</p>
|
|
321
339
|
<div class="slm-health" data-ref="presence"></div>
|
|
@@ -333,56 +351,114 @@ import{A as he,C as pe,a as y,b as L,c as j,d as _,e as V,f as q,g as U,h as K,i
|
|
|
333
351
|
<div class="slm-sectionlist" data-ref="sections"></div>
|
|
334
352
|
<p class="slm-eyebrow">Activity</p>
|
|
335
353
|
<div class="slm-feed" data-ref="feed"></div>
|
|
336
|
-
`,this.els.presence=this.els.rail.querySelector('[data-ref="presence"]'),this.els.legend=this.els.rail.querySelector('[data-ref="legend"]'),this.els.sections=this.els.rail.querySelector('[data-ref="sections"]'),this.els.feed=this.els.rail.querySelector('[data-ref="feed"]'),this.els.rail.querySelectorAll("[data-window]").forEach(e=>e.addEventListener("click",()=>{let t=Number(e.dataset.window);this.setTrendWindow(t).catch(s=>this.opts.onError?.(s))})),this.recomputeTallies(),this.paintMonitorInsights(),this.paintTrendWindow(),this.paintMomentumHelp(),this.paintFeed()}presenceCounts(){return this.livePresence?.value??this.controlRoomSnapshot?.presence??null}paintMonitorInsights(){if(this.mode!=="view")return;let e=this.controlRoomSnapshot;if(this.els.presence){let i=this.root?.classList.contains("live"),o=this.lastSyncedAt?
|
|
337
|
-
<div class="slm-healthitem" title="Active map sessions right now"><b>${
|
|
338
|
-
<div class="slm-healthitem" title="Sessions currently holding inventory"><b>${
|
|
354
|
+
`,this.els.presence=this.els.rail.querySelector('[data-ref="presence"]'),this.els.legend=this.els.rail.querySelector('[data-ref="legend"]'),this.els.sections=this.els.rail.querySelector('[data-ref="sections"]'),this.els.feed=this.els.rail.querySelector('[data-ref="feed"]'),this.els.rail.querySelectorAll("[data-window]").forEach(e=>e.addEventListener("click",()=>{let t=Number(e.dataset.window);this.setTrendWindow(t).catch(s=>this.opts.onError?.(s))})),this.recomputeTallies(),this.paintMonitorInsights(),this.paintTrendWindow(),this.paintMomentumHelp(),this.paintFeed()}presenceCounts(){return this.livePresence?.value??this.controlRoomSnapshot?.presence??null}paintMonitorInsights(){if(this.mode!=="view")return;let e=this.controlRoomSnapshot;if(this.els.presence){let i=this.root?.classList.contains("live"),o=this.lastSyncedAt?M(this.lastSyncedAt,Date.now()):"waiting",a=this.presenceCounts();this.els.presence.innerHTML=`
|
|
355
|
+
<div class="slm-healthitem" title="Active map sessions right now"><b>${a?a.shoppingSessions.toLocaleString():"\u2014"}</b><span>Viewing map</span></div>
|
|
356
|
+
<div class="slm-healthitem" title="Sessions currently holding inventory"><b>${a?a.activeHolds.toLocaleString():"\u2014"}</b><span>Active holds</span></div>
|
|
339
357
|
<div class="slm-healthitem"><b>${i?"Healthy":"Reconnecting"}</b><span>Live connection</span></div>
|
|
340
|
-
<div class="slm-healthitem"><b>${o}</b><span>Last sync</span></div>`}if(!this.els.sections)return;if(!e){this.els.sections.innerHTML='<div class="slm-empty">Loading authoritative section metrics\u2026</div>';return}let t=new Map(e.velocity.bySection.map(i=>[i.sectionId,i])),s=[...e.bookedValue.bySection].sort((i,o)=>{let
|
|
341
|
-
<span class="slm-sectiontop"><span>${h(i.sectionLabel)}</span><span>${
|
|
342
|
-
<span class="slm-sectionmeta"><span>${i.booked.toLocaleString()}/${i.total.toLocaleString()} booked \xB7 ${
|
|
343
|
-
</button>`}).join(""):'<div class="slm-empty">No section metrics are available for this chart.</div>',this.paintTrendWindow(),this.paintMomentumHelp()}applyHeatOverlay(){let e=this.controlRoomSnapshot;if(!this.heatEnabled||!e){this.renderer?.setSectionHeat(null);return}let t=new Map(e.bookedValue.bySection.map(
|
|
358
|
+
<div class="slm-healthitem"><b>${o}</b><span>Last sync</span></div>`}if(!this.els.sections)return;if(!e){this.els.sections.innerHTML='<div class="slm-empty">Loading authoritative section metrics\u2026</div>';return}let t=new Map(e.velocity.bySection.map(i=>[i.sectionId,i])),s=[...e.bookedValue.bySection].sort((i,o)=>{let a=t.get(i.sectionId)?.netBooked??0;return(t.get(o.sectionId)?.netBooked??0)-a||o.bookedValue-i.bookedValue});this.els.sections.innerHTML=s.length?s.map(i=>{let o=t.get(i.sectionId),a=o?.netBooked??0,l=`${a>0?"+":""}${a}`,r=o?.trend==="rising"||o?.trend==="cooling"?o.trend:"steady";return`<button type="button" class="slm-sectionrow" data-section-focus="${h(i.sectionId)}" title="Focus ${h(i.sectionLabel)} on the map">
|
|
359
|
+
<span class="slm-sectiontop"><span>${h(i.sectionLabel)}</span><span>${y(i.bookedValue,e.currency)}</span></span>
|
|
360
|
+
<span class="slm-sectionmeta"><span>${i.booked.toLocaleString()}/${i.total.toLocaleString()} booked \xB7 ${l} in ${e.velocity.windowMinutes}m</span><span class="slm-trend ${r}">${r}</span><span class="slm-sectionlocate">Locate</span></span>
|
|
361
|
+
</button>`}).join(""):'<div class="slm-empty">No section metrics are available for this chart.</div>',this.paintTrendWindow(),this.paintMomentumHelp()}applyHeatOverlay(){let e=this.controlRoomSnapshot;if(!this.heatEnabled||!e){this.renderer?.setSectionHeat(null);return}let t=new Map(e.bookedValue.bySection.map(a=>[a.sectionId,Math.max(1,a.total)])),s=e.velocity.bySection.map(a=>({sectionId:a.sectionId,rate:Math.max(0,a.netBooked)/(t.get(a.sectionId)??1)/e.velocity.windowMinutes})),i=Math.max(0,...s.map(a=>a.rate)),o={};for(let a of s)o[a.sectionId]=i>0?Math.sqrt(a.rate/i):0;this.renderer?.setSectionHeat(o)}renderSelectRail(){let e=this.doc?.categories??[],t=this.opts.numberOfPlacesToSelect,s=this.selectionCap();this.els.rail.innerHTML=`
|
|
362
|
+
<p class="slm-eyebrow">Custom selection</p>
|
|
363
|
+
<p class="slm-hint">Select inventory for a host-defined back-office operation. This tool is read-only and never books, releases, or blocks seats by itself.</p>
|
|
364
|
+
<div class="slm-selbar" aria-live="polite"><span class="slm-selnum" data-ref="selectnum">0</span><span class="slm-sellabel" data-ref="selectmeta">selected</span></div>
|
|
365
|
+
<p class="slm-note" data-ref="selectvalidity">${t?`Select exactly ${t.toLocaleString()} object${t===1?"":"s"}.`:`Up to ${s.toLocaleString()} objects may be selected.`}</p>
|
|
366
|
+
<div class="slm-row">
|
|
367
|
+
<button class="slm-btn ghost" data-ref="selectall">Select all</button>
|
|
368
|
+
<button class="slm-btn ghost" data-ref="selectclear" disabled>Clear</button>
|
|
369
|
+
</div>
|
|
370
|
+
${e.length?`<p class="slm-eyebrow" style="margin-top:8px">Select by category</p>
|
|
371
|
+
<div class="slm-chipgrid">${e.map(i=>`
|
|
372
|
+
<button class="slm-chip" type="button" data-select-cat="${h(i.key)}" aria-pressed="false">
|
|
373
|
+
<span class="dot" style="background:${h(i.color??"#6e7bff")}"></span>
|
|
374
|
+
<span>${h(i.label??i.key)}</span>
|
|
375
|
+
<span class="slm-chipcount" data-cat-count>0</span>
|
|
376
|
+
<span class="slm-chipcheck" aria-hidden="true">\u2713</span>
|
|
377
|
+
</button>`).join("")}</div>`:""}`,this.els.selectnum=this.els.rail.querySelector('[data-ref="selectnum"]'),this.els.selectmeta=this.els.rail.querySelector('[data-ref="selectmeta"]'),this.els.selectvalidity=this.els.rail.querySelector('[data-ref="selectvalidity"]'),this.els.selectclear=this.els.rail.querySelector('[data-ref="selectclear"]'),this.els.rail.querySelector('[data-ref="selectall"]')?.addEventListener("click",()=>this.selectAll()),this.els.selectclear.addEventListener("click",()=>this.clearSelection()),this.els.rail.querySelectorAll("[data-select-cat]").forEach(i=>{i.addEventListener("click",()=>{let o=i.dataset.selectCat,a=[...this.labelToSeat.values()].filter(r=>r.categoryKey===o&&this.canSelectObject(r)),l=new Set(this.selectionLabels());a.length>0&&a.every(r=>l.has(r.label))?this.deselectCategories([o]):this.selectCategories([o])})}),this.paintSelectSelection(this.getSelection())}paintSelectSelection(e){if(!this.els.selectnum||!this.els.selectmeta)return;let t=e.length;this.els.selectnum.textContent=t.toLocaleString(),this.els.selectmeta.textContent=t===1?"object selected":"objects selected",this.els.selectclear.disabled=t===0;let s=this.getSelectionValidity();this.els.selectvalidity&&(this.els.selectvalidity.textContent=s?s.isValid?`Ready \u2014 exactly ${s.required.toLocaleString()} selected.`:`Select ${s.remaining.toLocaleString()} more to continue.`:`${Math.max(0,this.selectionCap()-t).toLocaleString()} selections remaining.`);let i=new Set(e.map(o=>o.label));this.els.rail?.querySelectorAll("[data-select-cat]").forEach(o=>{let a=o.dataset.selectCat,l=[...this.labelToSeat.values()].filter(p=>p.categoryKey===a&&this.canSelectObject(p)),r=l.filter(p=>i.has(p.label)).length,d=l.length>0&&r===l.length;o.classList.toggle("on",d),o.setAttribute("aria-pressed",String(d));let n=o.querySelector("[data-cat-count]");n&&(n.textContent=r?`${r}/${l.length}`:l.length.toLocaleString()),o.disabled=l.length===0})}renderFilterSectionsRail(){let e=new Map;for(let t of this.filterableSections())e.set(t.label,[...e.get(t.label)??[],t]);this.els.rail.innerHTML=`
|
|
378
|
+
<p class="slm-eyebrow">Filter sections</p>
|
|
379
|
+
<p class="slm-hint">Choose a section for host-owned reporting. Matching sections with the same label stay together and the map frames their combined inventory.</p>
|
|
380
|
+
${e.size?`<div class="slm-sectionlist">${[...e].map(([t,s])=>{let i=t===this.filteredSectionLabel,o=s.reduce((a,l)=>a+l.seatCount,0);return`<button type="button" class="slm-sectionrow${i?" on":""}" data-filter-section="${h(t)}" aria-pressed="${i}">
|
|
381
|
+
<span class="slm-sectiontop"><span>${h(t)}</span><span>${o.toLocaleString()}</span></span>
|
|
382
|
+
<span class="slm-sectionmeta"><span>${s.length===1?"One section":`${s.length.toLocaleString()} matching sections`}</span><span class="slm-sectionlocate">${i?"Filtered":"Filter"}</span></span>
|
|
383
|
+
</button>`}).join("")}</div>
|
|
384
|
+
<button type="button" class="slm-btn ghost" data-clear-filter ${this.filteredSectionLabel?"":"disabled"}>Clear filter</button>`:'<div class="slm-empty">This chart has no filterable sections.</div>'}`,this.els.rail.querySelectorAll("[data-filter-section]").forEach(t=>{t.addEventListener("click",()=>this.setFilteredSection(t.dataset.filterSection))}),this.els.rail.querySelector("[data-clear-filter]")?.addEventListener("click",()=>this.clearFilteredSection())}renderCategoriesRail(){let e=this.doc?.categories??[];this.els.rail.innerHTML=`
|
|
385
|
+
<p class="slm-eyebrow">Event categories</p>
|
|
386
|
+
<p class="slm-hint">Override categories for this event only. The reusable chart and existing hold/booking value snapshots do not change.</p>
|
|
387
|
+
<div class="slm-selbar" aria-live="polite"><span class="slm-selnum" data-ref="categorynum">0</span><span class="slm-sellabel">objects selected</span></div>
|
|
388
|
+
<div class="slm-row">
|
|
389
|
+
<button class="slm-btn ghost" data-ref="categoryall">Select all</button>
|
|
390
|
+
<button class="slm-btn ghost" data-ref="categoryclear" disabled>Clear</button>
|
|
391
|
+
</div>
|
|
392
|
+
<p class="slm-eyebrow" style="margin-top:8px">Assign selected objects</p>
|
|
393
|
+
<div class="slm-categoryassign">${e.map(t=>{let s=[...this.labelToSeat.values()].filter(i=>i.categoryKey===t.key).length;return`<div class="slm-categoryrow">
|
|
394
|
+
<span class="dot" style="background:${h(t.color??"#6e7bff")}"></span>
|
|
395
|
+
<span class="slm-categorycopy"><b>${h(t.label??t.key)}</b><small>${s.toLocaleString()} currently</small></span>
|
|
396
|
+
<button type="button" class="slm-linkbtn" data-pick-category="${h(t.key)}">Select current</button>
|
|
397
|
+
<button type="button" class="slm-btn" data-assign-category="${h(t.key)}" disabled>Apply</button>
|
|
398
|
+
</div>`}).join("")}</div>`,this.els.categorynum=this.els.rail.querySelector('[data-ref="categorynum"]'),this.els.categoryclear=this.els.rail.querySelector('[data-ref="categoryclear"]'),this.els.rail.querySelector('[data-ref="categoryall"]')?.addEventListener("click",()=>this.selectAll()),this.els.categoryclear.addEventListener("click",()=>this.clearSelection()),this.els.rail.querySelectorAll("[data-pick-category]").forEach(t=>{t.addEventListener("click",()=>this.selectCategories([t.dataset.pickCategory]))}),this.els.rail.querySelectorAll("[data-assign-category]").forEach(t=>{t.addEventListener("click",()=>{this.setCategory(t.dataset.assignCategory)})}),this.paintCategorySelection(this.getSelection())}paintCategorySelection(e){this.els.categorynum&&(this.els.categorynum.textContent=e.length.toLocaleString(),this.els.categoryclear.disabled=e.length===0,this.els.rail?.querySelectorAll("[data-assign-category]").forEach(t=>{t.disabled=e.length===0,t.textContent=e.length?`Apply to ${e.length.toLocaleString()}`:"Apply"}))}renderTablesRail(){let e=(this.doc?we(this.doc):[]).flatMap(s=>s.objects).filter(s=>s.type==="table");if(this.inventoryModelVersion!==2){this.els.rail.innerHTML=`
|
|
399
|
+
<p class="slm-eyebrow">Table booking</p>
|
|
400
|
+
<p class="slm-hint">Per-event table modes require inventory model 2 so grouped tables have one authoritative inventory identity.</p>
|
|
401
|
+
<div class="slm-empty">Upgrade this event's inventory model before changing table booking.</div>`;return}if(!e.length){this.els.rail.innerHTML=`
|
|
402
|
+
<p class="slm-eyebrow">Table booking</p>
|
|
403
|
+
<p class="slm-hint">Choose whether each event table is sold by chair or as one grouped booking.</p>
|
|
404
|
+
<div class="slm-empty">This chart has no tables.</div>`;return}let t=s=>s.variableOccupancy?"variable":s.bookAsWhole?"whole":"individual";this.els.rail.innerHTML=`
|
|
405
|
+
<p class="slm-eyebrow">Table booking</p>
|
|
406
|
+
<p class="slm-hint">Change this event only. A table with held, booked, or blocked inventory cannot change mode, and channel allocations must survive.</p>
|
|
407
|
+
<div class="slm-row">
|
|
408
|
+
<button class="slm-btn ghost" data-table-all="individual"${this.tableBookingSaving?" disabled":""}>All per chair</button>
|
|
409
|
+
<button class="slm-btn ghost" data-table-all="whole"${this.tableBookingSaving?" disabled":""}>All whole table</button>
|
|
410
|
+
</div>
|
|
411
|
+
<div class="slm-tablelist">${e.map(s=>{let i=t(s);return`<div class="slm-tablerow">
|
|
412
|
+
<span class="slm-tablecopy"><b>${h(s.displayLabel??s.label)}</b><small>${s.seatCount.toLocaleString()} chairs</small></span>
|
|
413
|
+
<select class="slm-select" data-table-mode="${h(s.id)}" ${this.tableBookingSaving?"disabled":""}>
|
|
414
|
+
<option value="individual"${i==="individual"?" selected":""}>Book per chair</option>
|
|
415
|
+
<option value="whole"${i==="whole"?" selected":""}>Book whole table</option>
|
|
416
|
+
${i==="variable"?'<option value="variable" selected>Flexible group</option>':""}
|
|
417
|
+
</select>
|
|
418
|
+
</div>`}).join("")}</div>
|
|
419
|
+
<p class="slm-note">Flexible-group bounds remain available through <code>setTableBooking()</code>; the packaged rail never invents guest limits.</p>`,this.els.rail.querySelectorAll("[data-table-all]").forEach(s=>{s.addEventListener("click",()=>{this.setTableBooking(e.map(i=>i.id),s.dataset.tableAll)})}),this.els.rail.querySelectorAll("[data-table-mode]").forEach(s=>{s.addEventListener("change",()=>{this.setTableBooking([s.dataset.tableMode],s.value)})})}renderInspectRail(e){let t=e[e.length-1];if(!t){this.els.rail.innerHTML=`
|
|
344
420
|
<p class="slm-eyebrow">Inspect seats</p>
|
|
345
421
|
<p class="slm-hint">Select a seat to see its availability and booking context. Nothing changes in this view.</p>
|
|
346
|
-
<div class="slm-empty">Select a seat on the map.</div>`;return}let s=this.status.get(t.label)??"free",i={free:"Free",held:"Held",booked:"Booked",blocked:"Blocked"},o=this.sectionByObject.get(t.rowId)??f,
|
|
422
|
+
<div class="slm-empty">Select a seat on the map.</div>`;return}let s=this.status.get(t.label)??"free",i={free:"Free",held:"Held",booked:"Booked",blocked:"Blocked"},o=this.sectionByObject.get(t.rowId)??f,a=this.sectionLabelById.get(o)??"Other seats",l=this.doc?.categories.find(m=>m.key===t.categoryKey),r=this.controlRoomSnapshot?.bookedValue.bySection.find(m=>m.sectionId===o),d=this.doc?.objects.find(m=>m.id===t.rowId),n=d?.type==="row"?{label:"Row",value:d.label}:d?.type==="table"?{label:"Table",value:d.label}:t.kind==="booth"?{label:"Type",value:"Booth"}:null,p=t.kind==="booth"?"Booth":"Seat";this.els.rail.innerHTML=`
|
|
347
423
|
<p class="slm-eyebrow">${p} details</p>
|
|
348
424
|
<p class="slm-hint">Live availability and section performance.</p>
|
|
349
425
|
<div class="slm-inspect-card">
|
|
350
426
|
<div class="slm-inspect-label">${h(t.label)}</div>
|
|
351
427
|
<div class="slm-inspect-grid">
|
|
352
428
|
<div><span>Status</span><b>${i[s]}</b></div>
|
|
353
|
-
<div><span>Section</span><b>${h(
|
|
354
|
-
${
|
|
355
|
-
<div><span>Category</span><b>${h(
|
|
356
|
-
<div><span>Booked in section</span><b>${
|
|
357
|
-
<div><span>Section booked value</span><b>${
|
|
429
|
+
<div><span>Section</span><b>${h(a)}</b></div>
|
|
430
|
+
${n?`<div><span>${n.label}</span><b>${h(n.value)}</b></div>`:""}
|
|
431
|
+
<div><span>Category</span><b>${h(l?.label??t.categoryKey)}</b></div>
|
|
432
|
+
<div><span>Booked in section</span><b>${r?`${r.booked} of ${r.total}`:"\u2014"}</b></div>
|
|
433
|
+
<div><span>Section booked value</span><b>${r&&this.controlRoomSnapshot?y(r.bookedValue,this.controlRoomSnapshot.currency):"\u2014"}</b></div>
|
|
358
434
|
</div>
|
|
359
|
-
</div>`}async refreshAvailability(){try{let e=await this.withAuthRetry(()=>this.api.availability(this.key));this.availabilityRules=e.rules??{},this.effectiveClosed=new Set(
|
|
435
|
+
</div>`}async refreshAvailability(){try{let e=await this.withAuthRetry(()=>this.api.availability(this.key));this.availabilityRules=e.rules??{},this.effectiveClosed=new Set(R(this.availabilityRules)),this.mode==="sections"&&this.renderSectionsRail(),this.applySectionCanvasTreatment()}catch(e){this.opts.onError?.(e)}}async withAuthRetry(e){try{return await e()}catch(t){if(t instanceof g&&t.status===401&&this.opts.onTokenRefresh&&!this.tokenRefreshInFlight)return await this.rotateToken(),e();throw t}}updateEffectiveAvailability(e,t){let s=!1;Array.isArray(e)&&(this.effectiveHidden=new Set(e.filter(i=>typeof i=="string")),s=!0),Array.isArray(t)&&(this.effectiveClosed=new Set(t.filter(i=>typeof i=="string")),s=!0),s&&(this.mode==="sections"&&this.renderSectionsRail(),this.applySectionCanvasTreatment())}applySectionCanvasTreatment(){this.renderer&&(this.mode==="sections"?(this.renderer.setDimmedSections([...this.effectiveHidden]),this.renderer.setClosedSections([...this.effectiveClosed])):this.mode==="filterSections"&&this.filteredSectionLabel?this.applyFilteredSectionCanvas():(this.renderer.setDimmedSections(null),this.renderer.setClosedSections(null)))}sectionRowsInput(){return{sectionsBase:this.sectionsBase,doc:this.doc,availabilityRules:this.availabilityRules,effectiveHidden:this.effectiveHidden,effectiveClosed:this.effectiveClosed}}renderSectionsRail(){let{rows:e,hiddenSections:t,closedSections:s}=w(this.sectionRowsInput());if(!e.length){this.els.rail.innerHTML=`
|
|
360
436
|
<p class="slm-eyebrow">Availability windows</p>
|
|
361
437
|
<p class="slm-hint">Draw sections or zones in the designer to schedule availability per area. This chart has none yet.</p>
|
|
362
|
-
<div class="slm-empty">No sections on this chart.</div>`;return}let i=[];t&&i.push(`${t} hidden`),s&&i.push(`${s} closed`);let o=i.length?i.join(" \xB7 "):"All sections open and on sale",
|
|
438
|
+
<div class="slm-empty">No sections on this chart.</div>`;return}let i=[];t&&i.push(`${t} hidden`),s&&i.push(`${s} closed`);let o=i.length?i.join(" \xB7 "):"All sections open and on sale",a=t>0||s>0;this.els.rail.innerHTML=`
|
|
363
439
|
<p class="slm-eyebrow">Availability windows</p>
|
|
364
440
|
<p class="slm-hint">Control when each zone or section goes on sale. Keep it hidden, reveal it at a set time, or <b>auto-reveal once the rest sells past a threshold</b>. Hidden seats vanish for buyers; closed seats stay on the map (flat grey) but can't be bought.</p>
|
|
365
|
-
<div class="slm-availlist" data-ref="availlist">${e.map(
|
|
441
|
+
<div class="slm-availlist" data-ref="availlist">${e.map(l=>P(l,this.availabilitySaving)).join("")}</div>
|
|
366
442
|
<div class="slm-availsummary">
|
|
367
|
-
<span class="slm-availdot${
|
|
443
|
+
<span class="slm-availdot${a?" warn":""}"></span>
|
|
368
444
|
<span>${h(o)}</span>
|
|
369
445
|
</div>
|
|
370
446
|
<div class="slm-availcallout">
|
|
371
447
|
<span class="slm-availstar" aria-hidden="true">\u2726</span>
|
|
372
448
|
<p><b>Auto-reveal at % sold</b> is our differentiator \u2014 demand-triggered release: the balcony opens itself the moment the stalls hit the threshold. Neither seats.io nor Ticketmaster ships this.</p>
|
|
373
|
-
</div>`,this.wireSectionRail(),this.applySectionCanvasTreatment()}wireSectionRail(){let e=this.els.rail;e&&(e.querySelectorAll("[data-avail-id]").forEach(t=>{t.addEventListener("change",()=>this.setSectionMode(t.dataset.availId,t.value))}),e.querySelectorAll("[data-avail-reveal]").forEach(t=>{t.addEventListener("change",()=>{let s=new Date(t.value).getTime();Number.isFinite(s)&&this.setSectionRulePatch(t.dataset.availReveal,{revealAt:s})})}),e.querySelectorAll("[data-avail-pct]").forEach(t=>{t.addEventListener("change",()=>{let s=Math.max(1,Math.min(100,Number(t.value)||0));this.setSectionRulePatch(t.dataset.availPct,{thresholdPct:s})})}))}setSectionMode(e,t){let s=w(this.sectionRowsInput()).rows.find(
|
|
374
|
-
<span class="slm-leglabel">${t.label}</span><span class="slm-legcount">${e[t.key].toLocaleString()}</span></div>`).join(""))}paintFeed(){if(!this.els.feed)return;if(!this.feed.length){this.els.feed.innerHTML=`<div class="slm-empty">No activity yet \u2014 it'll stream in live.</div>`;return}let e=Date.now(),t={free:"#6e7bff",held:"#f4b740",booked:"#22a06b",blocked:"#8b94ac"};this.els.feed.innerHTML=this.feed.map(s=>{let i=s.count>1?` +${s.count-1}`:"",o=s.sectionLabels??[],
|
|
449
|
+
</div>`,this.wireSectionRail(),this.applySectionCanvasTreatment()}wireSectionRail(){let e=this.els.rail;e&&(e.querySelectorAll("[data-avail-id]").forEach(t=>{t.addEventListener("change",()=>this.setSectionMode(t.dataset.availId,t.value))}),e.querySelectorAll("[data-avail-reveal]").forEach(t=>{t.addEventListener("change",()=>{let s=new Date(t.value).getTime();Number.isFinite(s)&&this.setSectionRulePatch(t.dataset.availReveal,{revealAt:s})})}),e.querySelectorAll("[data-avail-pct]").forEach(t=>{t.addEventListener("change",()=>{let s=Math.max(1,Math.min(100,Number(t.value)||0));this.setSectionRulePatch(t.dataset.availPct,{thresholdPct:s})})}))}setSectionMode(e,t){let s=w(this.sectionRowsInput()).rows.find(l=>l.id===e),i=s?.seatLabels??this.availabilityRules[e]?.labels??[],o={...this.availabilityRules},a=O(t,i,this.availabilityRules[e]);if(a?o[e]=a:delete o[e],s?.kind==="zone"&&this.sectionsBase)for(let l of this.sectionsBase.sections)l.zone===e&&delete o[l.id];this.persistAvailability(o)}setSectionRulePatch(e,t){let s=this.availabilityRules[e];if(!s)return;let o=w(this.sectionRowsInput()).rows.find(a=>a.id===e)?.seatLabels??s.labels??[];this.persistAvailability({...this.availabilityRules,[e]:{...s,...t,labels:o}})}async persistAvailability(e){let t=this.availabilityRules;this.availabilityRules=e,this.availabilitySaving=!0,this.mode==="sections"&&this.renderSectionsRail();try{let s=await this.withAuthRetry(()=>this.api.setAvailability(this.key,e));this.availabilityRules=s.rules,this.effectiveHidden=new Set(s.hidden),this.effectiveClosed=new Set(R(s.rules)),this.availabilitySaving=!1,this.mode==="sections"&&this.renderSectionsRail(),this.applySectionCanvasTreatment()}catch(s){this.availabilityRules=t,this.availabilitySaving=!1,this.mode==="sections"&&this.renderSectionsRail(),this.toastErr("Couldn't update availability. Try again."),this.opts.onError?.(s)}}paintLegend(e){this.els.legend&&(this.els.legend.innerHTML=j.map(t=>`<div class="slm-legrow"><span class="slm-legdot" style="background:${t.color}"></span>
|
|
450
|
+
<span class="slm-leglabel">${t.label}</span><span class="slm-legcount">${e[t.key].toLocaleString()}</span></div>`).join(""))}paintFeed(){if(!this.els.feed)return;if(!this.feed.length){this.els.feed.innerHTML=`<div class="slm-empty">No activity yet \u2014 it'll stream in live.</div>`;return}let e=Date.now(),t={free:"#6e7bff",held:"#f4b740",booked:"#22a06b",blocked:"#8b94ac"};this.els.feed.innerHTML=this.feed.map(s=>{let i=s.count>1?` +${s.count-1}`:"",o=s.sectionLabels??[],a=o.length===1?o[0]:o.length>1?`${o.length} sections`:"";return`<button type="button" class="slm-feedrow" data-feed-id="${h(s.id)}" title="Locate this activity on the map">
|
|
375
451
|
<span class="slm-feeddot" style="background:${t[s.status]}"></span>
|
|
376
|
-
<span class="slm-feedtext">${
|
|
377
|
-
<span class="slm-feedmeta"><span class="slm-feedtime">${
|
|
378
|
-
</button>`}).join("")}renderBlockRail(){let t=(this.doc?.categories??[]).map(
|
|
379
|
-
<span class="dot" style="background:${h(
|
|
380
|
-
<span>${h(
|
|
452
|
+
<span class="slm-feedtext">${a?`<span class="slm-feedsection">${h(a)}</span>`:""}${s.count===1?"Seat":"Seats"} <b>${h(s.label)}${i}</b> ${h(s.verb)}</span>
|
|
453
|
+
<span class="slm-feedmeta"><span class="slm-feedtime">${M(s.at,e)}</span><span class="slm-feedlocate">Locate</span></span>
|
|
454
|
+
</button>`}).join("")}renderBlockRail(){let t=(this.doc?.categories??[]).map(n=>`<button class="slm-chip" type="button" data-cat="${h(n.key)}" aria-pressed="false">
|
|
455
|
+
<span class="dot" style="background:${h(n.color??"#6e7bff")}"></span>
|
|
456
|
+
<span>${h(n.label??n.key)}</span>
|
|
381
457
|
<span class="slm-chipcount" data-cat-count>0</span>
|
|
382
458
|
<span class="slm-chipcheck" aria-hidden="true">\u2713</span>
|
|
383
459
|
</button>`).join(""),s=this.sectionOptions.length?`<div class="slm-field"><label>Select a whole section</label>
|
|
384
460
|
<select class="slm-select" data-ref="section"><option value="">Choose a section\u2026</option>
|
|
385
|
-
${this.sectionOptions.map(
|
|
461
|
+
${this.sectionOptions.map(n=>`<option value="${h(n.id)}">${h(n.label)}</option>`).join("")}</select></div>`:"",i=this.sectionOptions.map(n=>`<option value="${h(n.id)}">${h(n.label)}</option>`).join("");this.els.rail.innerHTML=`
|
|
386
462
|
<p class="slm-eyebrow">Block & unblock</p>
|
|
387
463
|
<p class="slm-hint">Drag a box on the map to marquee-select, \u2318A for all, or pick a category/section. Booked and held inventory is never actionable here.</p>
|
|
388
464
|
<div class="slm-selbar" aria-live="polite"><span class="slm-selnum" data-ref="selnum">0</span><span class="slm-sellabel" data-ref="selmeta">selected</span></div>
|
|
@@ -425,8 +501,8 @@ import{A as he,C as pe,a as y,b as L,c as j,d as _,e as V,f as q,g as U,h as K,i
|
|
|
425
501
|
<button class="slm-btn ghost" data-ref="markall" style="width:100%" disabled>Put all blocked seats on sale</button>
|
|
426
502
|
<p class="slm-note slm-allnote" data-ref="markallnote">For a full reset only. You will be asked to confirm.</p>
|
|
427
503
|
</div>
|
|
428
|
-
`;let o=
|
|
504
|
+
`;let o=n=>this.els.rail.querySelector(`[data-ref="${n}"]`);this.els.selnum=o("selnum"),this.els.doblock=o("doblock"),this.els.dounblock=o("dounblock"),this.els.selmeta=o("selmeta"),this.els.blockedcount=o("blockedcount"),this.els.blockedshowing=o("blockedshowing"),this.els.blockedlist=o("blockedlist"),this.els.selblocked=o("selblocked"),this.els.markall=o("markall"),this.els.markallnote=o("markallnote"),o("doblock").addEventListener("click",()=>{this.block()}),o("dounblock").addEventListener("click",()=>{this.unblock()}),o("selall").addEventListener("click",()=>this.selectAll()),o("clearsel").addEventListener("click",()=>this.clearSelection()),o("markall").addEventListener("click",()=>this.confirmUnblockAll()),this.els.rail.querySelectorAll("[data-cat]").forEach(n=>n.addEventListener("click",()=>this.toggleCategory(n.dataset.cat)));let a=this.els.rail.querySelector('[data-ref="section"]');a?.addEventListener("change",()=>{a.value&&(this.selectSection(a.value),a.value="")});let l=o("blockedsearch"),r=o("blockedsection");l.value=this.blockedQuery,r.value=this.blockedSection,l.addEventListener("input",()=>{this.blockedQuery=l.value,this.blockedResultLimit=100,this.paintBlockedInventory()}),r.addEventListener("change",()=>{this.blockedSection=r.value,this.blockedResultLimit=100,this.paintBlockedInventory()}),o("selblocked").addEventListener("click",()=>{this.toggleLabels(this.filteredBlockedSeats().map(n=>n.label))}),o("blockedlist").addEventListener("click",n=>{let p=n.target,m=p.closest("[data-blocked-label]");m?.dataset.blockedLabel?this.toggleLabels([m.dataset.blockedLabel]):p.closest("[data-blocked-more]")&&(this.blockedResultLimit+=100,this.paintBlockedInventory())});let d=o("release");d.addEventListener("change",()=>{let n=d.value?new Date(d.value).getTime():NaN;this.releaseAt=Number.isFinite(n)&&n>Date.now()?n:null;let p=o("releasenote");p.textContent=this.releaseAt?`New blocks auto-release ${new Date(this.releaseAt).toLocaleString()}.`:d.value?"Pick a time in the future.":"Leave empty to block permanently."}),this.paintSelBar(this.getSelection())}toggleCategory(e){let t=[];for(let[s,i]of this.labelToSeat.entries())i.categoryKey===e&&this.isBlockSelectable(s)&&t.push(s);this.toggleLabels(t)}toggleLabels(e){if(!this.renderer)return;let t=e.filter(o=>this.labelToSeat.has(o)&&this.isBlockSelectable(o));if(!t.length)return;let s=new Set(this.selectionLabels());if(t.every(o=>s.has(o))){let o=t.map(a=>this.labelToId.get(a)).filter(a=>!!a);this.renderer.deselect(o)}else this.renderer.selectByLabels(t);this.syncSelection()}isBlockSelectable(e){let t=this.status.get(e)??"free";return t==="free"||t==="blocked"}paintSelBar(e){if(!this.els.selnum)return;this.els.selnum.textContent=e.length.toLocaleString();let t=e.filter(a=>(this.status.get(a.label)??"free")==="free").length,s=e.filter(a=>this.status.get(a.label)==="blocked").length;this.els.selmeta.textContent=e.length?`${t.toLocaleString()} available \xB7 ${s.toLocaleString()} blocked`:"selected";let i=this.els.doblock,o=this.els.dounblock;i.disabled=t===0,o.disabled=s===0,i.textContent=t?`Block ${t.toLocaleString()}`:"Block selected",o.textContent=s?`Put ${s.toLocaleString()} on sale`:"Put back on sale",this.paintCategoryControls(e),this.paintBlockedInventory()}paintCategoryControls(e){let t=new Set(e.map(s=>s.label));this.els.rail?.querySelectorAll("[data-cat]").forEach(s=>{let i=s.dataset.cat,o=[];for(let[n,p]of this.labelToSeat.entries())p.categoryKey===i&&this.isBlockSelectable(n)&&o.push(n);let a=o.filter(n=>t.has(n)).length,l=o.length>0&&a===o.length,r=a>0&&!l;s.disabled=o.length===0,s.classList.toggle("on",l),s.classList.toggle("partial",r),s.setAttribute("aria-pressed",l?"true":r?"mixed":"false"),s.setAttribute("title",l?`Remove all ${o.length.toLocaleString()} seats in this category from the selection`:r?`Select the remaining ${(o.length-a).toLocaleString()} seats in this category`:`Select all ${o.length.toLocaleString()} seats in this category`);let d=s.querySelector("[data-cat-count]");d&&(d.textContent=a?`${a.toLocaleString()}/${o.length.toLocaleString()}`:o.length.toLocaleString())})}filteredBlockedSeats(){let e=this.blockedQuery.trim().toLocaleLowerCase(),t=[];for(let[s,i]of this.labelToSeat.entries()){if(this.status.get(s)!=="blocked")continue;let o=this.sectionByObject.get(i.rowId)??f;if(!(this.blockedSection&&o!==this.blockedSection)){if(e){let a=this.doc?.categories.find(p=>p.key===i.categoryKey)?.label??i.categoryKey,l=this.sectionLabelById.get(o)??"Other seats",r=this.doc?.objects.find(p=>p.id===i.rowId),d=r?.type==="row"||r?.type==="table"?r.label:"";if(!`${s} ${a} ${l} ${d}`.toLocaleLowerCase().includes(e))continue}t.push(i)}}return t.sort((s,i)=>s.label.localeCompare(i.label,void 0,{numeric:!0,sensitivity:"base"}))}paintBlockedInventory(){if(!this.els.blockedlist)return;let e=[...this.status.entries()].filter(([,n])=>n==="blocked").length,t=this.filteredBlockedSeats(),s=t.slice(0,this.blockedResultLimit),i=new Set(this.selectionLabels()),o=t.filter(n=>i.has(n.label)).length,a=t.length>0&&o===t.length;this.els.blockedcount.textContent=e.toLocaleString(),this.els.blockedshowing.textContent=t.length?`Showing ${s.length.toLocaleString()} of ${t.length.toLocaleString()}`:e?"No matches":"No blocked seats";let l=this.els.selblocked;l.disabled=t.length===0,l.textContent=a?`Remove ${t.length.toLocaleString()} results`:`Select ${t.length.toLocaleString()} results`,this.els.blockedlist.innerHTML=s.length?s.map(n=>{let p=this.sectionByObject.get(n.rowId)??f,m=this.sectionLabelById.get(p)??"Other seats",b=this.doc?.categories.find(v=>v.key===n.categoryKey)?.label??n.categoryKey,u=i.has(n.label);return`<button type="button" class="slm-blockeditem${u?" on":""}" data-blocked-label="${h(n.label)}" aria-pressed="${u}">
|
|
429
505
|
<span class="slm-blockedcheck" aria-hidden="true">\u2713</span>
|
|
430
|
-
<span class="slm-blockedcopy"><span class="slm-blockedlabel">${h(
|
|
506
|
+
<span class="slm-blockedcopy"><span class="slm-blockedlabel">${h(n.label)}</span>
|
|
431
507
|
<span class="slm-blockedmeta">${h(m)} \xB7 ${h(b)}</span></span>
|
|
432
|
-
</button>`}).join("")+(t.length>s.length?'<button type="button" class="slm-blockedmore" data-blocked-more>Show 100 more</button>':""):`<div class="slm-blockedempty">${e?"No blocked seats match this search or section.":"No seats are blocked. Newly blocked seats will appear here."}</div>`;let
|
|
508
|
+
</button>`}).join("")+(t.length>s.length?'<button type="button" class="slm-blockedmore" data-blocked-more>Show 100 more</button>':""):`<div class="slm-blockedempty">${e?"No blocked seats match this search or section.":"No seats are blocked. Newly blocked seats will appear here."}</div>`;let r=this.els.markall,d=r.dataset.confirm==="true";r.disabled=e===0,r.textContent=d?`Confirm: put all ${e.toLocaleString()} on sale`:`Put all ${e.toLocaleString()} blocked seats on sale`}confirmUnblockAll(){let e=this.els.markall;if(!(!e||e.disabled)){if(e.dataset.confirm==="true"){this.resetUnblockAllConfirm(),this.unblockAll();return}e.dataset.confirm="true",e.classList.add("danger"),this.els.markallnote.textContent="This changes every blocked seat. Click the red button again to confirm.",this.paintBlockedInventory(),this.unblockAllConfirmTimer&&clearTimeout(this.unblockAllConfirmTimer),this.unblockAllConfirmTimer=setTimeout(()=>this.resetUnblockAllConfirm(),6e3)}}resetUnblockAllConfirm(){this.unblockAllConfirmTimer&&clearTimeout(this.unblockAllConfirmTimer),this.unblockAllConfirmTimer=null;let e=this.els.markall;e&&(delete e.dataset.confirm,e.classList.remove("danger"),this.els.markallnote&&(this.els.markallnote.textContent="For a full reset only. You will be asked to confirm."),this.paintBlockedInventory())}done(e,t,s){if(this.toastOk(s),t.length){let i=e==="block"?this.pushActivity(t,"blocked","blocked"):e==="unblock"||e==="unblockAll"?this.pushActivity(t,"unblocked","free"):e==="cancelBooking"?this.pushActivity(t,"cancelled","free"):null;i&&this.paintSpatialActivity(i)}e!=="setHoldTtl"&&this.refreshControlRoom().catch(i=>this.opts.onError?.(i)),this.opts.onActionComplete?.({action:e,labels:t,count:t.length})}toastOk(e){this.toast(e,"ok")}toastErr(e){this.toast(e,"err")}toast(e,t){let s=this.els.toast;s&&(s.textContent=e,s.className=`slm-toast on ${t}`,this.toastTimer&&clearTimeout(this.toastTimer),this.toastTimer=setTimeout(()=>{s.className="slm-toast"},3200))}fail(e){this.opts.onError?.(e),this.els.rail&&(this.els.rail.innerHTML=`<div class="slm-empty">Couldn't load this event. Check the event key and token.</div>`)}};export{oe as ACCESS_LINK_DEFAULTS,pe as ChannelsMode,L as ManageApi,g as ManageApiError,V as PUBLIC_CHANNEL_ID,N as PUBLIC_CHANNEL_NAME,E as SeatManager,te as accessIntentDescription,ee as accessIntentLabel,J as accessLine,ae as accessLinkBadge,re as accessLinkErrorCopy,le as accessLinkIsLive,ne as accessLinkPolicyLines,X as bucketRows,he as bucketRowsHtml,ce as dropReviewRows,se as intentForbidsCopy,ie as intentSwitchBlockedCopy,q as isPublicChannelId,_ as markerLetter,K as markerOf,Z as mutationCount,Y as needsMoveConfirmation,G as planAssignment,Q as retryAfterCopy,W as selectionSources,de as stateBadge,U as suggestMarker};
|