@seatlayer/js 0.53.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/hostedCheckout-3Y24DB65.js +35 -0
- package/dist/index.cjs +29 -20
- package/dist/index.d.cts +96 -8
- package/dist/index.d.ts +96 -8
- package/dist/index.js +28 -19
- package/dist/manager.cjs +190 -119
- 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/hostedCheckout-3NTOVIZG.js +0 -35
package/dist/manager.cjs
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
"use strict";var $e=Object.defineProperty;var vt=Object.getOwnPropertyDescriptor;var ft=Object.getOwnPropertyNames;var yt=Object.prototype.hasOwnProperty;var j=(a,e,t)=>()=>{if(t)throw t[0];try{return a&&(e=a(a=0)),e}catch(s){throw t=[s],s}};var Fe=(a,e)=>{for(var t in e)$e(a,t,{get:e[t],enumerable:!0})},kt=(a,e,t,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of ft(e))!yt.call(a,n)&&n!==t&&$e(a,n,{get:()=>e[n],enumerable:!(s=vt(e,n))||s.enumerable});return a};var wt=a=>kt($e({},"__esModule",{value:!0}),a);async function qe(a){let t=(a.headers.get("content-type")??"").includes("application/json")?await a.json().catch(()=>null):null;if(!a.ok){let s=t;throw new f(a.status,s?.error??`request_failed_${a.status}`,s?.code,s?.conflicts,s?.details,typeof s?.message=="string"?s.message:void 0)}return t}function $(a){return a&&typeof a=="object"?a:{}}function ie(a,e,t=0){return typeof a=="number"&&Number.isFinite(a)?a:typeof e=="number"&&Number.isFinite(e)?e:t}function Ne(a,e){return a===null?null:typeof a=="number"&&Number.isFinite(a)?a:a!==void 0?null:typeof e=="number"&&Number.isFinite(e)?e:null}function Ve(a){let e=$(a),t=ie(e.bookedValue,e.bookedRevenue);return{...e,bookedValue:t,bookedRevenue:t}}function xt(a){let e=$(a),t=$(e.report),s=Array.isArray(t.byCategory)?t.byCategory.map(i=>{let o=$(i),l=ie(o.bookedValue,o.bookedRevenue);return{...o,bookedValue:l,bookedRevenue:l}}):[],n=Array.isArray(t.bySection)?t.bySection.map(Ve):void 0;return{...e,report:{...t,byCategory:s,...n?{bySection:n}:{}}}}function St(a){let e=$(a),t=$(e.bookedValue),s=$(e.revenue),n=Object.keys(t).length?t:s,i=Array.isArray(t.bySection)?t.bySection:Array.isArray(s.bySection)?s.bySection:[],o={...n,gross:ie(t.gross,s.gross),bySection:i.map(Ve)},l=$(e.velocity),r=Array.isArray(l.bySection)?l.bySection.map(d=>{let c=$(d),p=ie(c.bookedValue,c.grossRevenue);return{...c,bookedValue:p,grossRevenue:p}}):[];return{...e,bookedValue:o,revenue:o,velocity:{...l,bySection:r}}}function Lt(a){let e=$(a),t=$(e.report),s=typeof t.includesBookedValue=="boolean"?t.includesBookedValue:t.includesRevenue===!0,n=Array.isArray(t.rows)?t.rows.map(l=>{let r=$(l),d=$(r.attribution),c=Ne(d.bookedValue,d.revenue);return{...r,attribution:{...d,bookedValue:c,revenue:c}}}):[],i=$(t.totals),o=Ne(i.bookedValue,i.revenue);return{...e,report:{...t,includesBookedValue:s,includesRevenue:s,rows:n,totals:{...i,bookedValue:o,revenue:o}}}}function Ae(a){let e=$(a),t=typeof e.includesBookedValue=="boolean"?e.includesBookedValue:e.includesRevenue===!0;return{...e,includesBookedValue:t,includesRevenue:t}}var f,U,ae=j(()=>{"use strict";f=class extends Error{constructor(e,t,s,n,i,o){super(t),this.name="ManageApiError",this.status=e,this.code=s,this.conflicts=n,this.details=i,this.serverMessage=o}};U=class{constructor(e,t){this.base=e.replace(/\/+$/,""),this.token=t}setToken(e){this.token=e}auth(e,t={}){let s=t.method??"GET",n={Authorization:`Bearer ${this.token}`},i;return t.body!==void 0&&(n["Content-Type"]="application/json",i=JSON.stringify(t.body)),fetch(`${this.base}${e}`,{method:s,headers:n,body:i,credentials:"omit"}).then(o=>qe(o))}async authBlob(e){let t=await fetch(`${this.base}${e}`,{method:"GET",headers:{Authorization:`Bearer ${this.token}`},credentials:"omit"});return t.ok||await qe(t),t.blob()}chart(e){return this.auth(`/v1/events/${encodeURIComponent(e)}/chart`)}asset(e,t){return/^[a-zA-Z0-9._-]+$/.test(t)?this.authBlob(`/v1/events/${encodeURIComponent(e)}/assets/${encodeURIComponent(t)}`):Promise.reject(new f(404,"not_found","not_found"))}objects(e){return this.auth(`/v1/events/${encodeURIComponent(e)}/objects`)}subscribeTicket(e){return this.auth(`/v1/events/${encodeURIComponent(e)}/subscribe-tickets`,{method:"POST"})}socketUrl(e){return`${this.base.replace(/^http/,"ws")}/pub/events/${encodeURIComponent(e)}/subscribe?surface=manager`}block(e,t,s={}){let n={labels:t};return typeof s.releaseAt=="number"&&(n.releaseAt=s.releaseAt),s.reason&&(n.reason=s.reason),this.auth(`/v1/events/${encodeURIComponent(e)}/block`,{method:"POST",body:n})}unblock(e,t){return this.auth(`/v1/events/${encodeURIComponent(e)}/unblock`,{method:"POST",body:{labels:t}})}unblockAll(e){return this.auth(`/v1/events/${encodeURIComponent(e)}/unblock-all`,{method:"POST"})}unbook(e,t,s){return this.auth(`/v1/events/${encodeURIComponent(e)}/unbook`,{method:"POST",body:{labels:t,bookingRef:s}})}setHoldTtl(e,t){return this.auth(`/v1/events/${encodeURIComponent(e)}/hold-ttl`,{method:"POST",body:{holdTtlMs:t}})}bookings(e,t={}){let s=new URLSearchParams;t.q&&s.set("q",t.q),t.state&&s.set("state",t.state),t.cursor&&s.set("cursor",t.cursor),t.limit!=null&&s.set("limit",String(t.limit));let n=s.toString();return this.auth(`/v1/events/${encodeURIComponent(e)}/bookings${n?`?${n}`:""}`)}booking(e,t){return this.auth(`/v1/events/${encodeURIComponent(e)}/bookings/${encodeURIComponent(t)}`)}listBookings(e,t={}){return this.bookings(e,t)}retrieveBooking(e,t){return this.booking(e,t)}availability(e){return this.auth(`/v1/events/${encodeURIComponent(e)}/availability`)}setAvailability(e,t){return this.auth(`/v1/events/${encodeURIComponent(e)}/availability`,{method:"POST",body:{rules:t}})}channels(e,t={}){let s=t.includeArchived?"?includeArchived=1":"";return this.auth(`/v1/events/${encodeURIComponent(e)}/channels${s}`)}channelAllocation(e,t={}){let s=new URLSearchParams;t.afterLabel&&s.set("afterLabel",t.afterLabel),t.limit!=null&&s.set("limit",String(t.limit));let n=s.toString();return this.auth(`/v1/events/${encodeURIComponent(e)}/channels/allocation${n?`?${n}`:""}`)}channelAudit(e,t={}){let s=new URLSearchParams;t.limit!=null&&s.set("limit",String(t.limit)),t.before!=null&&s.set("before",String(t.before));let n=s.toString();return this.auth(`/v1/events/${encodeURIComponent(e)}/channels/audit${n?`?${n}`:""}`)}createChannel(e,t){return this.auth(`/v1/events/${encodeURIComponent(e)}/channels`,{method:"POST",body:t})}renameChannel(e,t,s){return this.auth(`/v1/events/${encodeURIComponent(e)}/channels/${encodeURIComponent(t)}`,{method:"PATCH",body:{name:s}})}setChannelPaused(e,t,s){let n=s?"pause":"unpause";return this.auth(`/v1/events/${encodeURIComponent(e)}/channels/${encodeURIComponent(t)}/${n}`,{method:"POST",body:{}})}archiveChannel(e,t,s){return this.auth(`/v1/events/${encodeURIComponent(e)}/channels/${encodeURIComponent(t)}/archive`,{method:"POST",body:{destination:s}})}applyChannelAssignment(e,t){return this.auth(`/v1/events/${encodeURIComponent(e)}/channels/assignments`,{method:"POST",body:{targetChannelId:t.targetChannelId||null,labels:t.labels,assignmentVersion:t.assignmentVersion}})}channelPreview(e,t,s={}){let n=new URLSearchParams;t.length&&n.set("channelIds",t.join(",")),s.includePublic!=null&&n.set("includePublic",s.includePublic?"1":"0");let i=n.toString();return this.auth(`/v1/events/${encodeURIComponent(e)}/channels/preview${i?`?${i}`:""}`)}setChannelAccessIntent(e,t,s,n={}){return this.auth(`/v1/events/${encodeURIComponent(e)}/channels/${encodeURIComponent(t)}`,{method:"PATCH",body:{accessIntent:s,...n.acknowledgeLiveAccess?{acknowledgeLiveAccess:!0}:{},...n.reason?{reason:n.reason}:{}}})}createAccessLink(e,t,s={}){return this.auth(`/v1/events/${encodeURIComponent(e)}/channels/${encodeURIComponent(t)}/access-links`,{method:"POST",body:s})}accessLinks(e,t){return this.auth(`/v1/events/${encodeURIComponent(e)}/channels/${encodeURIComponent(t)}/access-links`)}rotateAccessLink(e,t,s,n){return this.auth(`/v1/events/${encodeURIComponent(e)}/channels/${encodeURIComponent(t)}/access-links/${encodeURIComponent(s)}/rotate`,{method:"POST",body:{endActiveSessions:n}})}revokeAccessLink(e,t,s,n=!1){let i=n?"?endActiveSessions=1":"";return this.auth(`/v1/events/${encodeURIComponent(e)}/channels/${encodeURIComponent(t)}/access-links/${encodeURIComponent(s)}${i}`,{method:"DELETE"})}report(e){return this.auth(`/v1/events/${encodeURIComponent(e)}/report`).then(xt)}controlRoom(e,t=15){return this.auth(`/v1/events/${encodeURIComponent(e)}/control-room?window=${t}`).then(St)}channelReport(e){return this.auth(`/v1/events/${encodeURIComponent(e)}/channels/report`).then(Lt)}createChannelReportLink(e,t,s={}){return this.auth(`/v1/events/${encodeURIComponent(e)}/channels/${encodeURIComponent(t)}/report-links`,{method:"POST",body:s}).then(n=>{let i=$(n);return{...i,link:Ae(i.link)}})}channelReportLinks(e,t){return this.auth(`/v1/events/${encodeURIComponent(e)}/channels/${encodeURIComponent(t)}/report-links`).then(s=>{let n=$(s);return{links:Array.isArray(n.links)?n.links.map(Ae):[]}})}revokeChannelReportLink(e,t,s){return this.auth(`/v1/events/${encodeURIComponent(e)}/channels/${encodeURIComponent(t)}/report-links/${encodeURIComponent(s)}`,{method:"DELETE"}).then(n=>{let i=$(n);return{...i,link:Ae(i.link)}})}log(e,t={}){let s=new URLSearchParams;t.limit!=null&&s.set("limit",String(t.limit)),t.before!=null&&s.set("before",String(t.before));let n=s.toString();return this.auth(`/v1/events/${encodeURIComponent(e)}/log${n?`?${n}`:""}`)}async reportCsv(e){let t=await fetch(`${this.base}/v1/events/${encodeURIComponent(e)}/report.csv`,{headers:{Authorization:`Bearer ${this.token}`},credentials:"omit"});if(!t.ok)throw new f(t.status,`request_failed_${t.status}`);return t.blob()}}});function P(a){return a==null||a===""||a===L}function z(a,e){let t=(a??"").trim();return((/\p{L}/u.exec(t)?.[0]??t[0]??"")||e).toUpperCase().slice(0,1)}function J(a,e){let t=new Set([...e].map(i=>z(i,"")).filter(Boolean)),s=z(a,"");return{letter:tt.includes(s)&&!t.has(s)?s:[...tt].find(i=>!t.has(i))??(s||"X"),color:he[t.size%he.length]}}function pe(a,e=0){return P(a.id)?{letter:z(a.marker,"P"),color:a.color||Tt}:{letter:z(a.marker||a.name,"?"),color:a.color||he[e%he.length]}}function ue(a,e,t){let s=new Map;for(let o of a){let l=He(e.get(o));s.set(l,(s.get(l)??0)+1)}let n=[{id:L,name:t?.publicSale?.name??A},...(t?.channels??[]).map(o=>({id:o.id,name:o.name}))],i=[];for(let o of n){let l=s.get(o.id);l&&i.push({channelId:o.id,name:o.name,count:l}),s.delete(o.id)}for(let[o,l]of s)i.push({channelId:o,name:P(o)?A:"Another channel",count:l});return i}function He(a){return P(a)?L:a}function Pe(a){return{count:a.length,labels:a.slice(0,st),truncated:a.length>st}}function me(a){let{labels:e,allocation:t,statusOf:s,nameOf:n}=a,i=He(a.targetChannelId),o=new Set,l=0,r=0,d=new Map,c=[],p=[],g=[];for(let m of e){if(o.has(m))continue;o.add(m);let u=s(m);if(!u){g.push(m);continue}let v=He(t.get(m));if(v===i){r+=1;continue}if(u==="held"){c.push(m);continue}if(u==="booked"){p.push(m);continue}v===L?l+=1:d.set(v,(d.get(v)??0)+1)}let w=[...d.entries()].map(([m,u])=>({channelId:m,name:n(m),count:u}));return{changedFromPublic:{count:l},movedFromOtherChannel:{count:w.reduce((m,u)=>m+u.count,0),channels:w},alreadyInTarget:{count:r},skippedHeld:Pe(c),skippedBooked:Pe(p),notFound:Pe(g)}}function K(a){return a.changedFromPublic.count+a.movedFromOtherChannel.count}function be(a){return a.movedFromOtherChannel.count>0}function ge(a,e){let t=[];a.changedFromPublic.count&&t.push({kind:"add",icon:"+",count:a.changedFromPublic.count,text:`${a.changedFromPublic.count.toLocaleString()} from ${A}`});for(let s of a.movedFromOtherChannel.channels)t.push({kind:"move",icon:"\u21C4",count:s.count,text:`${s.count.toLocaleString()} moved out of ${s.name??"another channel"}`,why:"needs this confirmation"});return a.alreadyInTarget.count&&t.push({kind:"same",icon:"=",count:a.alreadyInTarget.count,text:`${a.alreadyInTarget.count.toLocaleString()} already in ${e}`,why:"unchanged"}),a.skippedHeld.count&&t.push({kind:"skip",icon:"\u23F8",count:a.skippedHeld.count,text:`${a.skippedHeld.count.toLocaleString()} in a buyer's checkout`,why:"can't move while held",peek:de(a.skippedHeld)}),a.skippedBooked.count&&t.push({kind:"skip",icon:"\u{1F512}",count:a.skippedBooked.count,text:`${a.skippedBooked.count.toLocaleString()} already sold`,why:"sales are never rewritten",peek:de(a.skippedBooked)}),a.notFound.count&&t.push({kind:"skip",icon:"?",count:a.notFound.count,text:`${a.notFound.count.toLocaleString()} not on this map`,why:"these seats are no longer part of the event",peek:de(a.notFound)}),t}function de(a){if(!a.labels.length)return;let e=a.labels.slice(0,4).join(", ");return a.truncated||a.labels.length>4?`${e}\u2026`:e}function ve(a){let e=a?.retryAfterMs??(a?.latestHoldExpiresAt?Math.max(0,a.latestHoldExpiresAt-Date.now()):0);if(!e)return"in a moment";let t=Math.ceil(e/6e4);return t<=1?"in about a minute":`in about ${t} minutes`}function fe(a){if(!a||!a.intent)return"\u2014";let e=a.intent==="server"?"Website integration":a.intent==="hosted_link"?"Buyer link":a.intent==="internal"?"Your staff sell these":"Protected reserve",t=a.hasActiveGrants?"in use now":a.lastMintAt?`last used ${new Date(a.lastMintAt).toLocaleDateString()}`:null,s=a.detail??t;return s?`${e} \xB7 ${s}`:e}function O(a){return a==="internal"?"Sell through your own staff":a==="server"?"Integrate a website or app":a==="hosted_link"?"Sell with a buyer link":"Keep as protected reserve"}function ye(a){switch(a){case"internal":return"Only your own box office can sell these seats, through your secret key. Buyer links and website integrations are refused.";case"server":return"Your website's backend mints each buyer a short-lived session for these seats. Buyer links are refused; the code lives on the Embed page.";case"hosted_link":return"SeatLayer makes a link you send to a named group. They open it and buy only these seats. No other route can sell them.";default:return"Nobody can buy these seats. Every way of letting a buyer in \u2014 a buyer link, your website, even your own staff \u2014 is refused while this is the route. The seats stay out of public sale."}}function It(a){return a==="hosted_link"?"hosted_link":a==="server"?"server":a==="staff"?"internal":null}function Y(a){let e=Be(a?.accessIntent),t=It(a?.route),s=`This channel is set to "${O(e)}"`;return t?`${s}, so it cannot do that. Switch it to "${O(t)}" first.`:`${s}, so it cannot do that. Choose a different route for this channel first.`}function Be(a){return a==="internal"||a==="server"||a==="hosted_link"||a==="none"?a:"none"}function ce(a,e,t){return`${a.toLocaleString()} ${a===1?e:t}`}function ke(a){let e=Math.max(0,a?.liveAccessLinks??0),t=Math.max(0,a?.activeSessions??0),s=Be(a?.from),n=Be(a?.to),o=`${[e?ce(e,"buyer link is live","buyer links are live"):null,t?ce(t,"buyer is in a checkout","buyers are in a checkout"):null].filter(Boolean).join(", and ")||"Buyers are inside this channel"} on "${O(s)}". Moving it to "${O(n)}" changes what happens to them.`,l=[];return e&&l.push(`${ce(e,"buyer link closes","buyer links close")} immediately. Anyone who has not opened it yet never will \u2014 send a new link if you still need one.`),t&&l.push(`${ce(t,"buyer who is already in a checkout keeps","buyers who are already in a checkout keep")} their seats and can finish paying. Nobody is thrown out. No new buyers come in this way, so the old route empties on its own within 12 hours.`),{headline:o,consequences:l}}function we(a){switch(a.status??a.state){case"active":return{text:"Active",kind:"active"};case"expired":return{text:"Expired",kind:"archived"};case"exhausted":return{text:"All used",kind:"paused"};case"rotated":return{text:"Replaced",kind:"archived"};default:return{text:"Revoked",kind:"archived"}}}function te(a){return a.state==="active"&&a.status==="active"}function Et(a){return Number.isFinite(a)?new Date(a).toLocaleString(void 0,{day:"numeric",month:"short",year:"numeric",hour:"numeric",minute:"2-digit"}):"\u2014"}function se(a){return[{k:"Expires",v:Et(a.expiresAt)},{k:"Redemptions",v:`${a.redemptions.toLocaleString()} of ${a.maxRedemptions.toLocaleString()} used`},{k:"Seats per buyer",v:`${a.maxQuantity.toLocaleString()} seat${a.maxQuantity===1?"":"s"} maximum`},{k:"Covers",v:a.includePublic?"This channel's allocation and Public sale seats":"This channel's allocation only"}]}function q(a){let e=a?.serverMessage?.trim();switch(a?.code){case"invalid_expiry":case"invalid_max_redemptions":case"invalid_max_quantity":case"invalid_session_ttl":case"invalid_label":return e||"That setting is outside what a hosted link allows. Adjust it and try again.";case"too_many_access_links":return e||"This channel already has as many live links as it can hold. Revoke one before creating another.";case"access_link_not_active":return"That link is no longer active, so it cannot be rotated or revoked.";case"channel_unavailable":return"This channel is paused or archived, so it cannot let new buyers in. Resume it first.";case"channel_access_intent_forbids":return Y(a?.details);case"end_active_sessions_required":return"Choose what happens to the buyers who already came in through this link.";case"not_found":return"That link is no longer here. Refresh and try again.";default:return a?.status===403?"Hosted access links need channel-management permission.":e||"That did not go through. Try again."}}function nt(a){return(a?.channels??[]).map(e=>({kind:"skip",icon:"\u26A0",count:e.count,text:`${e.count.toLocaleString()} would leave ${e.name??"a channel"}`,why:"the new chart no longer has these seats",peek:e.labels?.length?de({count:e.count,labels:e.labels,truncated:e.truncated??!1}):void 0}))}function xe(a){return a==="builtin"?"Built-in":a==="active"?"Active":a==="paused"?"Paused":"Archived"}var L,A,he,Tt,tt,st,ee,Se=j(()=>{"use strict";L="public",A="Public sale";he=["#a78bfa","#2dd4bf","#fb923c","#60a5fa","#f472b6","#a3e635","#f87171","#38bdf8","#c084fc","#facc15"],Tt="#f4b740",tt="ABCDEFGHJKLMNPQRSTUVWXYZ";st=12;ee={maxRedemptions:100,maxQuantity:4}});function W(a){return a.map((e,t)=>`
|
|
1
|
+
"use strict";var $e=Object.defineProperty;var ft=Object.getOwnPropertyDescriptor;var yt=Object.getOwnPropertyNames;var kt=Object.prototype.hasOwnProperty;var N=(o,e,t)=>()=>{if(t)throw t[0];try{return o&&(e=o(o=0)),e}catch(s){throw t=[s],s}};var qe=(o,e)=>{for(var t in e)$e(o,t,{get:e[t],enumerable:!0})},wt=(o,e,t,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of yt(e))!kt.call(o,n)&&n!==t&&$e(o,n,{get:()=>e[n],enumerable:!(s=ft(e,n))||s.enumerable});return o};var St=o=>wt($e({},"__esModule",{value:!0}),o);async function _e(o){let t=(o.headers.get("content-type")??"").includes("application/json")?await o.json().catch(()=>null):null;if(!o.ok){let s=t;throw new f(o.status,s?.error??`request_failed_${o.status}`,s?.code,s?.conflicts,s?.details,typeof s?.message=="string"?s.message:void 0)}return t}function $(o){return o&&typeof o=="object"?o:{}}function ie(o,e,t=0){return typeof o=="number"&&Number.isFinite(o)?o:typeof e=="number"&&Number.isFinite(e)?e:t}function je(o,e){return o===null?null:typeof o=="number"&&Number.isFinite(o)?o:o!==void 0?null:typeof e=="number"&&Number.isFinite(e)?e:null}function Ve(o){let e=$(o),t=ie(e.bookedValue,e.bookedRevenue);return{...e,bookedValue:t,bookedRevenue:t}}function xt(o){let e=$(o),t=$(e.report),s=Array.isArray(t.byCategory)?t.byCategory.map(i=>{let a=$(i),l=ie(a.bookedValue,a.bookedRevenue);return{...a,bookedValue:l,bookedRevenue:l}}):[],n=Array.isArray(t.bySection)?t.bySection.map(Ve):void 0;return{...e,report:{...t,byCategory:s,...n?{bySection:n}:{}}}}function Lt(o){let e=$(o),t=$(e.bookedValue),s=$(e.revenue),n=Object.keys(t).length?t:s,i=Array.isArray(t.bySection)?t.bySection:Array.isArray(s.bySection)?s.bySection:[],a={...n,gross:ie(t.gross,s.gross),bySection:i.map(Ve)},l=$(e.velocity),r=Array.isArray(l.bySection)?l.bySection.map(d=>{let c=$(d),p=ie(c.bookedValue,c.grossRevenue);return{...c,bookedValue:p,grossRevenue:p}}):[];return{...e,bookedValue:a,revenue:a,velocity:{...l,bySection:r}}}function Ct(o){let e=$(o),t=$(e.report),s=typeof t.includesBookedValue=="boolean"?t.includesBookedValue:t.includesRevenue===!0,n=Array.isArray(t.rows)?t.rows.map(l=>{let r=$(l),d=$(r.attribution),c=je(d.bookedValue,d.revenue);return{...r,attribution:{...d,bookedValue:c,revenue:c}}}):[],i=$(t.totals),a=je(i.bookedValue,i.revenue);return{...e,report:{...t,includesBookedValue:s,includesRevenue:s,rows:n,totals:{...i,bookedValue:a,revenue:a}}}}function Te(o){let e=$(o),t=typeof e.includesBookedValue=="boolean"?e.includesBookedValue:e.includesRevenue===!0;return{...e,includesBookedValue:t,includesRevenue:t}}var f,U,ae=N(()=>{"use strict";f=class extends Error{constructor(e,t,s,n,i,a){super(t),this.name="ManageApiError",this.status=e,this.code=s,this.conflicts=n,this.details=i,this.serverMessage=a}};U=class{constructor(e,t){this.base=e.replace(/\/+$/,""),this.token=t}setToken(e){this.token=e}auth(e,t={}){let s=t.method??"GET",n={Authorization:`Bearer ${this.token}`},i;return t.body!==void 0&&(n["Content-Type"]="application/json",i=JSON.stringify(t.body)),fetch(`${this.base}${e}`,{method:s,headers:n,body:i,credentials:"omit"}).then(a=>_e(a))}async authBlob(e){let t=await fetch(`${this.base}${e}`,{method:"GET",headers:{Authorization:`Bearer ${this.token}`},credentials:"omit"});return t.ok||await _e(t),t.blob()}chart(e){return this.auth(`/v1/events/${encodeURIComponent(e)}/chart`)}asset(e,t){return/^[a-zA-Z0-9._-]+$/.test(t)?this.authBlob(`/v1/events/${encodeURIComponent(e)}/assets/${encodeURIComponent(t)}`):Promise.reject(new f(404,"not_found","not_found"))}objects(e){return this.auth(`/v1/events/${encodeURIComponent(e)}/objects`)}subscribeTicket(e){return this.auth(`/v1/events/${encodeURIComponent(e)}/subscribe-tickets`,{method:"POST"})}socketUrl(e){return`${this.base.replace(/^http/,"ws")}/pub/events/${encodeURIComponent(e)}/subscribe?surface=manager`}block(e,t,s={}){let n={labels:t};return typeof s.releaseAt=="number"&&(n.releaseAt=s.releaseAt),s.reason&&(n.reason=s.reason),this.auth(`/v1/events/${encodeURIComponent(e)}/block`,{method:"POST",body:n})}unblock(e,t){return this.auth(`/v1/events/${encodeURIComponent(e)}/unblock`,{method:"POST",body:{labels:t}})}unblockAll(e){return this.auth(`/v1/events/${encodeURIComponent(e)}/unblock-all`,{method:"POST"})}unbook(e,t,s){return this.auth(`/v1/events/${encodeURIComponent(e)}/unbook`,{method:"POST",body:{labels:t,bookingRef:s}})}setHoldTtl(e,t){return this.auth(`/v1/events/${encodeURIComponent(e)}/hold-ttl`,{method:"POST",body:{holdTtlMs:t}})}setCategory(e,t,s){return this.auth(`/v1/events/${encodeURIComponent(e)}/category-assignments`,{method:"POST",body:{labels:t,categoryKey:s}})}setTableBooking(e,t,s,n={}){return this.auth(`/v1/events/${encodeURIComponent(e)}/table-booking`,{method:"POST",body:{tableIds:t,mode:s,...n}})}bookings(e,t={}){let s=new URLSearchParams;t.q&&s.set("q",t.q),t.state&&s.set("state",t.state),t.cursor&&s.set("cursor",t.cursor),t.limit!=null&&s.set("limit",String(t.limit));let n=s.toString();return this.auth(`/v1/events/${encodeURIComponent(e)}/bookings${n?`?${n}`:""}`)}booking(e,t){return this.auth(`/v1/events/${encodeURIComponent(e)}/bookings/${encodeURIComponent(t)}`)}listBookings(e,t={}){return this.bookings(e,t)}retrieveBooking(e,t){return this.booking(e,t)}availability(e){return this.auth(`/v1/events/${encodeURIComponent(e)}/availability`)}setAvailability(e,t){return this.auth(`/v1/events/${encodeURIComponent(e)}/availability`,{method:"POST",body:{rules:t}})}channels(e,t={}){let s=t.includeArchived?"?includeArchived=1":"";return this.auth(`/v1/events/${encodeURIComponent(e)}/channels${s}`)}channelAllocation(e,t={}){let s=new URLSearchParams;t.afterLabel&&s.set("afterLabel",t.afterLabel),t.limit!=null&&s.set("limit",String(t.limit));let n=s.toString();return this.auth(`/v1/events/${encodeURIComponent(e)}/channels/allocation${n?`?${n}`:""}`)}channelAudit(e,t={}){let s=new URLSearchParams;t.limit!=null&&s.set("limit",String(t.limit)),t.before!=null&&s.set("before",String(t.before));let n=s.toString();return this.auth(`/v1/events/${encodeURIComponent(e)}/channels/audit${n?`?${n}`:""}`)}createChannel(e,t){return this.auth(`/v1/events/${encodeURIComponent(e)}/channels`,{method:"POST",body:t})}renameChannel(e,t,s){return this.auth(`/v1/events/${encodeURIComponent(e)}/channels/${encodeURIComponent(t)}`,{method:"PATCH",body:{name:s}})}setChannelPaused(e,t,s){let n=s?"pause":"unpause";return this.auth(`/v1/events/${encodeURIComponent(e)}/channels/${encodeURIComponent(t)}/${n}`,{method:"POST",body:{}})}archiveChannel(e,t,s){return this.auth(`/v1/events/${encodeURIComponent(e)}/channels/${encodeURIComponent(t)}/archive`,{method:"POST",body:{destination:s}})}applyChannelAssignment(e,t){return this.auth(`/v1/events/${encodeURIComponent(e)}/channels/assignments`,{method:"POST",body:{targetChannelId:t.targetChannelId||null,labels:t.labels,assignmentVersion:t.assignmentVersion}})}channelPreview(e,t,s={}){let n=new URLSearchParams;t.length&&n.set("channelIds",t.join(",")),s.includePublic!=null&&n.set("includePublic",s.includePublic?"1":"0");let i=n.toString();return this.auth(`/v1/events/${encodeURIComponent(e)}/channels/preview${i?`?${i}`:""}`)}setChannelAccessIntent(e,t,s,n={}){return this.auth(`/v1/events/${encodeURIComponent(e)}/channels/${encodeURIComponent(t)}`,{method:"PATCH",body:{accessIntent:s,...n.acknowledgeLiveAccess?{acknowledgeLiveAccess:!0}:{},...n.reason?{reason:n.reason}:{}}})}createAccessLink(e,t,s={}){return this.auth(`/v1/events/${encodeURIComponent(e)}/channels/${encodeURIComponent(t)}/access-links`,{method:"POST",body:s})}accessLinks(e,t){return this.auth(`/v1/events/${encodeURIComponent(e)}/channels/${encodeURIComponent(t)}/access-links`)}rotateAccessLink(e,t,s,n){return this.auth(`/v1/events/${encodeURIComponent(e)}/channels/${encodeURIComponent(t)}/access-links/${encodeURIComponent(s)}/rotate`,{method:"POST",body:{endActiveSessions:n}})}revokeAccessLink(e,t,s,n=!1){let i=n?"?endActiveSessions=1":"";return this.auth(`/v1/events/${encodeURIComponent(e)}/channels/${encodeURIComponent(t)}/access-links/${encodeURIComponent(s)}${i}`,{method:"DELETE"})}report(e){return this.auth(`/v1/events/${encodeURIComponent(e)}/report`).then(xt)}controlRoom(e,t=15){return this.auth(`/v1/events/${encodeURIComponent(e)}/control-room?window=${t}`).then(Lt)}channelReport(e){return this.auth(`/v1/events/${encodeURIComponent(e)}/channels/report`).then(Ct)}createChannelReportLink(e,t,s={}){return this.auth(`/v1/events/${encodeURIComponent(e)}/channels/${encodeURIComponent(t)}/report-links`,{method:"POST",body:s}).then(n=>{let i=$(n);return{...i,link:Te(i.link)}})}channelReportLinks(e,t){return this.auth(`/v1/events/${encodeURIComponent(e)}/channels/${encodeURIComponent(t)}/report-links`).then(s=>{let n=$(s);return{links:Array.isArray(n.links)?n.links.map(Te):[]}})}revokeChannelReportLink(e,t,s){return this.auth(`/v1/events/${encodeURIComponent(e)}/channels/${encodeURIComponent(t)}/report-links/${encodeURIComponent(s)}`,{method:"DELETE"}).then(n=>{let i=$(n);return{...i,link:Te(i.link)}})}log(e,t={}){let s=new URLSearchParams;t.limit!=null&&s.set("limit",String(t.limit)),t.before!=null&&s.set("before",String(t.before));let n=s.toString();return this.auth(`/v1/events/${encodeURIComponent(e)}/log${n?`?${n}`:""}`)}async reportCsv(e){let t=await fetch(`${this.base}/v1/events/${encodeURIComponent(e)}/report.csv`,{headers:{Authorization:`Bearer ${this.token}`},credentials:"omit"});if(!t.ok)throw new f(t.status,`request_failed_${t.status}`);return t.blob()}}});function B(o){return o==null||o===""||o===C}function O(o,e){let t=(o??"").trim();return((/\p{L}/u.exec(t)?.[0]??t[0]??"")||e).toUpperCase().slice(0,1)}function J(o,e){let t=new Set([...e].map(i=>O(i,"")).filter(Boolean)),s=O(o,"");return{letter:st.includes(s)&&!t.has(s)?s:[...st].find(i=>!t.has(i))??(s||"X"),color:he[t.size%he.length]}}function pe(o,e=0){return B(o.id)?{letter:O(o.marker,"P"),color:o.color||Et}:{letter:O(o.marker||o.name,"?"),color:o.color||he[e%he.length]}}function ue(o,e,t){let s=new Map;for(let a of o){let l=Pe(e.get(a));s.set(l,(s.get(l)??0)+1)}let n=[{id:C,name:t?.publicSale?.name??T},...(t?.channels??[]).map(a=>({id:a.id,name:a.name}))],i=[];for(let a of n){let l=s.get(a.id);l&&i.push({channelId:a.id,name:a.name,count:l}),s.delete(a.id)}for(let[a,l]of s)i.push({channelId:a,name:B(a)?T:"Another channel",count:l});return i}function Pe(o){return B(o)?C:o}function Be(o){return{count:o.length,labels:o.slice(0,nt),truncated:o.length>nt}}function me(o){let{labels:e,allocation:t,statusOf:s,nameOf:n}=o,i=Pe(o.targetChannelId),a=new Set,l=0,r=0,d=new Map,c=[],p=[],g=[];for(let b of e){if(a.has(b))continue;a.add(b);let m=s(b);if(!m){g.push(b);continue}let v=Pe(t.get(b));if(v===i){r+=1;continue}if(m==="held"){c.push(b);continue}if(m==="booked"){p.push(b);continue}v===C?l+=1:d.set(v,(d.get(v)??0)+1)}let w=[...d.entries()].map(([b,m])=>({channelId:b,name:n(b),count:m}));return{changedFromPublic:{count:l},movedFromOtherChannel:{count:w.reduce((b,m)=>b+m.count,0),channels:w},alreadyInTarget:{count:r},skippedHeld:Be(c),skippedBooked:Be(p),notFound:Be(g)}}function K(o){return o.changedFromPublic.count+o.movedFromOtherChannel.count}function be(o){return o.movedFromOtherChannel.count>0}function ge(o,e){let t=[];o.changedFromPublic.count&&t.push({kind:"add",icon:"+",count:o.changedFromPublic.count,text:`${o.changedFromPublic.count.toLocaleString()} from ${T}`});for(let s of o.movedFromOtherChannel.channels)t.push({kind:"move",icon:"\u21C4",count:s.count,text:`${s.count.toLocaleString()} moved out of ${s.name??"another channel"}`,why:"needs this confirmation"});return o.alreadyInTarget.count&&t.push({kind:"same",icon:"=",count:o.alreadyInTarget.count,text:`${o.alreadyInTarget.count.toLocaleString()} already in ${e}`,why:"unchanged"}),o.skippedHeld.count&&t.push({kind:"skip",icon:"\u23F8",count:o.skippedHeld.count,text:`${o.skippedHeld.count.toLocaleString()} in a buyer's checkout`,why:"can't move while held",peek:de(o.skippedHeld)}),o.skippedBooked.count&&t.push({kind:"skip",icon:"\u{1F512}",count:o.skippedBooked.count,text:`${o.skippedBooked.count.toLocaleString()} already sold`,why:"sales are never rewritten",peek:de(o.skippedBooked)}),o.notFound.count&&t.push({kind:"skip",icon:"?",count:o.notFound.count,text:`${o.notFound.count.toLocaleString()} not on this map`,why:"these seats are no longer part of the event",peek:de(o.notFound)}),t}function de(o){if(!o.labels.length)return;let e=o.labels.slice(0,4).join(", ");return o.truncated||o.labels.length>4?`${e}\u2026`:e}function ve(o){let e=o?.retryAfterMs??(o?.latestHoldExpiresAt?Math.max(0,o.latestHoldExpiresAt-Date.now()):0);if(!e)return"in a moment";let t=Math.ceil(e/6e4);return t<=1?"in about a minute":`in about ${t} minutes`}function fe(o){if(!o||!o.intent)return"\u2014";let e=o.intent==="server"?"Website integration":o.intent==="hosted_link"?"Buyer link":o.intent==="internal"?"Your staff sell these":"Protected reserve",t=o.hasActiveGrants?"in use now":o.lastMintAt?`last used ${new Date(o.lastMintAt).toLocaleDateString()}`:null,s=o.detail??t;return s?`${e} \xB7 ${s}`:e}function z(o){return o==="internal"?"Sell through your own staff":o==="server"?"Integrate a website or app":o==="hosted_link"?"Sell with a buyer link":"Keep as protected reserve"}function ye(o){switch(o){case"internal":return"Only your own box office can sell these seats, through your secret key. Buyer links and website integrations are refused.";case"server":return"Your website's backend mints each buyer a short-lived session for these seats. Buyer links are refused; the code lives on the Embed page.";case"hosted_link":return"SeatLayer makes a link you send to a named group. They open it and buy only these seats. No other route can sell them.";default:return"Nobody can buy these seats. Every way of letting a buyer in \u2014 a buyer link, your website, even your own staff \u2014 is refused while this is the route. The seats stay out of public sale."}}function Mt(o){return o==="hosted_link"?"hosted_link":o==="server"?"server":o==="staff"?"internal":null}function Y(o){let e=He(o?.accessIntent),t=Mt(o?.route),s=`This channel is set to "${z(e)}"`;return t?`${s}, so it cannot do that. Switch it to "${z(t)}" first.`:`${s}, so it cannot do that. Choose a different route for this channel first.`}function He(o){return o==="internal"||o==="server"||o==="hosted_link"||o==="none"?o:"none"}function ce(o,e,t){return`${o.toLocaleString()} ${o===1?e:t}`}function ke(o){let e=Math.max(0,o?.liveAccessLinks??0),t=Math.max(0,o?.activeSessions??0),s=He(o?.from),n=He(o?.to),a=`${[e?ce(e,"buyer link is live","buyer links are live"):null,t?ce(t,"buyer is in a checkout","buyers are in a checkout"):null].filter(Boolean).join(", and ")||"Buyers are inside this channel"} on "${z(s)}". Moving it to "${z(n)}" changes what happens to them.`,l=[];return e&&l.push(`${ce(e,"buyer link closes","buyer links close")} immediately. Anyone who has not opened it yet never will \u2014 send a new link if you still need one.`),t&&l.push(`${ce(t,"buyer who is already in a checkout keeps","buyers who are already in a checkout keep")} their seats and can finish paying. Nobody is thrown out. No new buyers come in this way, so the old route empties on its own within 12 hours.`),{headline:a,consequences:l}}function we(o){switch(o.status??o.state){case"active":return{text:"Active",kind:"active"};case"expired":return{text:"Expired",kind:"archived"};case"exhausted":return{text:"All used",kind:"paused"};case"rotated":return{text:"Replaced",kind:"archived"};default:return{text:"Revoked",kind:"archived"}}}function te(o){return o.state==="active"&&o.status==="active"}function It(o){return Number.isFinite(o)?new Date(o).toLocaleString(void 0,{day:"numeric",month:"short",year:"numeric",hour:"numeric",minute:"2-digit"}):"\u2014"}function se(o){return[{k:"Expires",v:It(o.expiresAt)},{k:"Redemptions",v:`${o.redemptions.toLocaleString()} of ${o.maxRedemptions.toLocaleString()} used`},{k:"Seats per buyer",v:`${o.maxQuantity.toLocaleString()} seat${o.maxQuantity===1?"":"s"} maximum`},{k:"Covers",v:o.includePublic?"This channel's allocation and Public sale seats":"This channel's allocation only"}]}function _(o){let e=o?.serverMessage?.trim();switch(o?.code){case"invalid_expiry":case"invalid_max_redemptions":case"invalid_max_quantity":case"invalid_session_ttl":case"invalid_label":return e||"That setting is outside what a hosted link allows. Adjust it and try again.";case"too_many_access_links":return e||"This channel already has as many live links as it can hold. Revoke one before creating another.";case"access_link_not_active":return"That link is no longer active, so it cannot be rotated or revoked.";case"channel_unavailable":return"This channel is paused or archived, so it cannot let new buyers in. Resume it first.";case"channel_access_intent_forbids":return Y(o?.details);case"end_active_sessions_required":return"Choose what happens to the buyers who already came in through this link.";case"not_found":return"That link is no longer here. Refresh and try again.";default:return o?.status===403?"Hosted access links need channel-management permission.":e||"That did not go through. Try again."}}function it(o){return(o?.channels??[]).map(e=>({kind:"skip",icon:"\u26A0",count:e.count,text:`${e.count.toLocaleString()} would leave ${e.name??"a channel"}`,why:"the new chart no longer has these seats",peek:e.labels?.length?de({count:e.count,labels:e.labels,truncated:e.truncated??!1}):void 0}))}function Se(o){return o==="builtin"?"Built-in":o==="active"?"Active":o==="paused"?"Paused":"Archived"}var C,T,he,Et,st,nt,ee,xe=N(()=>{"use strict";C="public",T="Public sale";he=["#a78bfa","#2dd4bf","#fb923c","#60a5fa","#f472b6","#a3e635","#f87171","#38bdf8","#c084fc","#facc15"],Et="#f4b740",st="ABCDEFGHJKLMNPQRSTUVWXYZ";nt=12;ee={maxRedemptions:100,maxQuantity:4}});function W(o){return o.map((e,t)=>`
|
|
2
2
|
<div class="slm-ch-bucket" style="animation-delay:${Math.min(t,4)*30}ms">
|
|
3
3
|
<span class="ico ${e.kind}" aria-hidden="true">${h(e.icon)}</span>
|
|
4
4
|
<span><b>${e.count.toLocaleString()}</b> ${h(e.text.replace(/^[\d,.\s]+/,""))}
|
|
5
5
|
${e.why?`<span class="why">\u2014 ${h(e.why)}</span>`:""}</span>
|
|
6
6
|
${e.peek?`<span class="peek">${h(e.peek)}</span>`:"<span></span>"}
|
|
7
|
-
</div>`).join("")}function h(
|
|
7
|
+
</div>`).join("")}function h(o){return String(o??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}function ot(o){return new Date(o-new Date(o).getTimezoneOffset()*6e4).toISOString().slice(0,16)}function De(o,e){let t=o.querySelector(e)?.value.trim()??"",s=Number(t);return t!==""&&Number.isInteger(s)?s:null}function Oe(o){let e=o.querySelector("[data-ch-lk-url]");if(e)try{let t=document.createRange();t.selectNodeContents(e);let s=window.getSelection();s?.removeAllRanges(),s?.addRange(t)}catch{}}var at,Le=N(()=>{"use strict";at=`
|
|
8
8
|
<p class="slm-eyebrow" style="margin-top:18px">On your website</p>
|
|
9
9
|
<p class="slm-hint">There is nothing more to set up on this screen. Your own server mints a short-lived buyer
|
|
10
10
|
access session for this channel with the SeatLayer server SDK and hands it to the widget. A channel name
|
|
11
11
|
on its own never grants access.</p>
|
|
12
12
|
<p class="slm-note"><a class="slm-linkbtn" href="https://docs.seatlayer.io/server-api/channels"
|
|
13
|
-
target="_blank" rel="noreferrer noopener">Read the website integration guide \u2192</a></p>`});function
|
|
13
|
+
target="_blank" rel="noreferrer noopener">Read the website integration guide \u2192</a></p>`});function lt(o){let e=o.canvas,t=o.layer;if(!e||!t)return;let s=o.host.mapLayer.getBoundingClientRect(),n=Math.max(1,Math.round(s.width)),i=Math.max(1,Math.round(s.height)),a=typeof devicePixelRatio=="number"?Math.min(3,Math.max(1,devicePixelRatio)):1;(e.width!==n*a||e.height!==i*a)&&(e.width=n*a,e.height=i*a);let l=o.resolveCtx();if(!l)return;l.setTransform(a,0,0,a,0,0),l.clearRect(0,0,n,i);let r=o.host.isSeatDetail(),d=Math.max(3,o.host.seatPixelSize()),c=d/2,p=o.view==="preview"?o.previewProjection:null,g=p?new Set(p.available===!1?[]:p.eligible??[]):null,w=new Map,b=!r&&o.host.sections().length>1?new Map:null,m=o.view==="preview"&&r&&d<=15?new Map:null;for(let v of o.host.seats()){let P=o.host.statusOf(v.label)??"free",F=o.allocation.get(v.label)??C;if(F!==C){let S=w.get(F)??{x:0,y:0,n:0};S.x+=v.x,S.y+=v.y,S.n+=1,w.set(F,S)}if(!r){let S=o.host.sectionOfLabel(v.label),k=S?o.host.worldToScreen({x:v.x,y:v.y}):null;if(b&&S&&k){let y=b.get(S.id)??{label:S.label,minX:k.x,minY:k.y,maxX:k.x,maxY:k.y};y.minX=Math.min(y.minX,k.x),y.minY=Math.min(y.minY,k.y),y.maxX=Math.max(y.maxX,k.x),y.maxY=Math.max(y.maxY,k.y),b.set(S.id,y)}continue}if(P!=="free")continue;let q=null,E=null;if(o.view==="preview"?g?.has(v.label)?(q=Pt,E=Ht):(q=Dt,E=Ot):F!==C&&(q=o.markerFor(F).color,E=zt),!q)continue;let x=o.host.worldToScreen({x:v.x,y:v.y});if(x&&!(x.x<-d||x.y<-d||x.x>n+d||x.y>i+d)){if(m){let S=o.host.sectionOfLabel(v.label);if(S){let k=m.get(S.id)??{label:S.label,minX:x.x,minY:x.y,maxX:x.x,maxY:x.y};k.minX=Math.min(k.minX,x.x),k.minY=Math.min(k.minY,x.y),k.maxX=Math.max(k.maxX,x.x),k.maxY=Math.max(k.maxY,x.y),m.set(S.id,k)}}if(l.fillStyle=q,o.view==="preview"||F!==C){let S=Math.max(2,c+Math.min(1.5,c*.06));l.globalAlpha=1,l.beginPath(),l.arc(x.x,x.y,S,0,Math.PI*2),l.fill(),l.strokeStyle=E??q,l.lineWidth=o.view==="preview"?Math.max(1,Math.min(1.75,d*.13)):Math.max(1,Math.min(1.5,d*.1)),l.stroke(),o.view==="preview"&&g?.has(v.label)&&d>=22&&Ft(l,v.label,x.x,x.y,S)}else l.globalAlpha=.85,l.fillRect(x.x-c,x.y-c,d,d)}}l.globalAlpha=1,m&&_t(l,m),qt(b,o),jt(w,o,l)}function Ft(o,e,t,s,n){let i=n*1.55,a=Math.min(13,Math.max(7,n*.55)),l=6;for(;a>=l&&(o.font=`800 ${a}px var(--slm-font, system-ui, sans-serif)`,!(o.measureText(e).width<=i));)a-=.5;a<l||(o.fillStyle="#ffffff",o.textAlign="center",o.textBaseline="middle",o.fillText(e,t,s))}function qt(o,e){let t=e.layer;if(t&&(t.querySelectorAll(".slm-ch-section-target").forEach(s=>s.remove()),!!o))for(let[s,n]of o){let i=n.maxX-n.minX,a=n.maxY-n.minY;if(i<20||a<20)continue;let l=document.createElement("button");l.type="button",l.className="slm-ch-section-target",l.style.left=`${n.minX-8}px`,l.style.top=`${n.minY-8}px`,l.style.width=`${i+16}px`,l.style.height=`${a+16}px`,l.setAttribute("aria-label",`Open ${n.label} seats`),l.addEventListener("click",()=>e.onSectionTargetClick(s)),t.appendChild(l)}}function _t(o,e){for(let t of e.values()){let s=t.maxX-t.minX,n=t.maxY-t.minY;if(s<52||n<26)continue;let i=(t.minX+t.maxX)/2,a=(t.minY+t.maxY)/2,l=Math.max(11,Math.min(15,n*.16));o.font=`800 ${l}px var(--slm-font, system-ui, sans-serif)`;let r=Math.min(s-8,o.measureText(t.label).width+18),d=l+10;o.fillStyle="rgba(11, 16, 28, .88)",o.fillRect(i-r/2,a-d/2,r,d),o.fillStyle="#f8fafc",o.textAlign="center",o.textBaseline="middle",o.fillText(t.label,i,a)}}function jt(o,e,t){let s=e.layer;if(!s||(s.querySelectorAll(".slm-ch-flag").forEach(i=>i.remove()),e.view==="preview"))return;let n=[...o.entries()].sort((i,a)=>a[1].n-i[1].n).slice(0,Bt);for(let[i,a]of n){let l=e.list?.channels.find(p=>p.id===i);if(!l)continue;let r=e.host.worldToScreen({x:a.x/a.n,y:a.y/a.n});if(!r)continue;let d=e.markerFor(i),c=document.createElement("span");c.className="slm-ch-flag",c.style.left=`${r.x}px`,c.style.top=`${r.y}px`,c.innerHTML=`<span class="mk" style="background:${h(d.color)}">${h(d.letter)}</span>${h(l.name)}${l.state==="paused"?" \xB7 Paused":""}`,s.appendChild(c)}}var Bt,Pt,Ht,Dt,Ot,zt,rt=N(()=>{"use strict";xe();Le();Bt=8,Pt="#6e7bff",Ht="#b9c0ff",Dt="#303846",Ot="#4b5669",zt="#101723"});var ct,dt=N(()=>{"use strict";ct=`
|
|
14
14
|
.slm{--slm-mo-instant:80ms;--slm-mo-quick:140ms;--slm-mo-base:200ms;--slm-mo-slow:320ms;--slm-mo-ambient:2000ms;
|
|
15
15
|
--slm-mo-out:cubic-bezier(.2,.8,.2,1);--slm-mo-in-out:cubic-bezier(.4,0,.2,1);--slm-mo-exit:cubic-bezier(.4,0,1,1);
|
|
16
16
|
--slm-mo-spring:cubic-bezier(.34,1.3,.64,1)}
|
|
@@ -232,11 +232,6 @@
|
|
|
232
232
|
.slm-ch-grabbar{width:42px;height:4px;border-radius:2px;background:rgba(255,255,255,.22);margin:0 auto}
|
|
233
233
|
.slm.compact .slm-ch-staged{bottom:auto;top:8px}
|
|
234
234
|
.slm.compact .slm-btn,.slm.compact .slm-ch-row,.slm.compact .slm-ch-viewseg button{min-height:44px}
|
|
235
|
-
.slm-tools{display:none}
|
|
236
|
-
.slm.compact .slm-tools{display:block;width:100%;padding:9px 13px;min-height:44px;border:1px solid var(--slm-line);
|
|
237
|
-
border-radius:10px;background:var(--slm-surface);color:var(--slm-text);font-size:13px;font-weight:800}
|
|
238
|
-
.slm.compact .slm-modes{display:none}
|
|
239
|
-
|
|
240
235
|
@media (prefers-reduced-motion:reduce){
|
|
241
236
|
.slm-ch-layer,.slm-ch-banner,.slm-ch-staged,.slm-ch-row,.slm.compact.ch-sheet .slm-rail,
|
|
242
237
|
.slm-ch-meter i,.slm-ch-radio{transition:none!important}
|
|
@@ -246,19 +241,19 @@
|
|
|
246
241
|
.slm-ch-staged.shake{outline:2px solid #e5484d;outline-offset:2px}
|
|
247
242
|
.slm-ch-staged.done{outline:2px solid #5bd39b;outline-offset:2px}
|
|
248
243
|
}
|
|
249
|
-
`});var
|
|
244
|
+
`});var pt={};qe(pt,{CHANNELS_CSS:()=>ct,ChannelsMode:()=>ne,bucketRowsHtml:()=>W});var Vt,ht,A,ne,ze=N(()=>{"use strict";xe();ae();rt();Le();dt();Le();Vt=3e4,ht=300,A=5e3,ne=class{constructor(e,t){this.active=!1;this.list=null;this.allocation=new Map;this.assignmentVersion=0;this.loadError=null;this.loading=!0;this.view="inspect";this.mapIntent="pan";this.focusedSectionId=null;this.showArchived=!1;this.detailChannelId=null;this.assignOpen=!1;this.targetChannelId="";this.conflict=!1;this.dialog=null;this.detent="medium";this.seatListLimit=ht;this.links=[];this.linksChannelId=null;this.linksState="idle";this.listSeq=0;this.linksSeq=0;this.previewAudience=[];this.previewIncludePublic=!1;this.previewProjection=null;this.previewState="idle";this.pollTimer=null;this.layer=null;this.canvas=null;this.ctx=void 0;this.bannerEl=null;this.stagedEl=null;this.liveEl=null;this.scrimEl=null;this.lastFocus=null;this.stagedDoneTimer=null;this.lastSelectionCount=0;this.lastCounts=new Map;this.assignmentRowsCache=null;this.railHtml=null;this.allocationVersion=null;this.onVisibility=null;this.host=e,this.caps=t}enter(){this.active||(this.active=!0,this.mapIntent="pan",this.assignOpen=!1,this.focusedSectionId=null,this.assignmentRowsCache=null,this.railHtml=null,this.ensureLayer(),this.host.root.classList.add("ch-mode"),this.applySheetClasses(),this.host.sections().length>1&&this.host.showSectionOverview(),this.paintRail(),this.onInteractionChange?.(),this.refresh(),this.pollTimer=setInterval(()=>{typeof document<"u"&&document.hidden||this.refresh({quiet:!0})},Vt),typeof document<"u"&&typeof document.addEventListener=="function"&&(this.onVisibility=()=>{this.active&&!document.hidden&&this.refresh({quiet:!0})},document.addEventListener("visibilitychange",this.onVisibility)))}leave(){this.active&&(this.active=!1,this.pollTimer&&clearInterval(this.pollTimer),this.pollTimer=null,this.onVisibility&&typeof document<"u"&&document.removeEventListener("visibilitychange",this.onVisibility),this.onVisibility=null,this.railHtml=null,this.closeDialog({restoreFocus:!1}),this.links=[],this.linksChannelId=null,this.linksState="idle",this.layer?.classList.remove("on"),this.host.root.classList.remove("ch-mode","ch-preview","ch-sheet","detent-collapsed","detent-medium","detent-full"),this.setBanner(!1),this.setStaged(null))}destroy(){this.leave(),this.stagedDoneTimer&&clearTimeout(this.stagedDoneTimer),this.layer?.remove(),this.layer=null}setCapabilities(e){this.caps=e,this.active&&this.paintRail()}isActive(){return this.active}canSelect(){return this.caps.manage&&this.view==="inspect"}usesMarqueeSelection(){return this.canSelect()&&this.mapIntent==="assign"}handleSectionFocus(e){this.active&&(this.focusedSectionId=e,this.paintRail())}applyRealtimeHint(){this.active&&this.refresh({quiet:!0})}handleSelectionChange(){this.active&&(this.paintSelection(),this.paintStagedBar())}handleViewChange(){this.active&&this.paintOverlay()}handleLayoutChange(){this.active&&(this.applySheetClasses(),this.paintOverlay())}async refresh(e={}){if(!this.caps.view)return;let t=++this.listSeq,s=()=>t!==this.listSeq;try{let n=await this.host.api.channels(this.host.eventKey,{includeArchived:this.showArchived});if(s())return;if(this.list=n,this.assignmentVersion=n.assignmentVersion,this.loadError=null,this.targetChannelId||(this.targetChannelId=n.channels.find(i=>i.state==="active")?.id??C),this.allocationVersion!==n.assignmentVersion){if(await this.loadAllocation(t),s())return;this.allocationVersion=n.assignmentVersion}if(this.detailChannelId&&await this.loadLinks(this.detailChannelId),s())return;this.loading=!1,this.active&&(this.paintRail(),this.paintOverlay())}catch(n){if(s())return;this.loading=!1,n instanceof f&&n.status===403&&(this.caps={view:!1,manage:!1}),this.loadError=n,e.quiet||this.host.onError(n),this.active&&this.paintRail()}}async loadAllocation(e){let t=new Map,s;for(let n=0;n<200;n+=1){if(e!==void 0&&e!==this.listSeq)return;let i=await this.host.api.channelAllocation(this.host.eventKey,{afterLabel:s,limit:1e3});for(let a of i.allocations)B(a.channelId)||t.set(a.label,a.channelId);if(this.assignmentVersion=i.assignmentVersion,!i.nextAfterLabel)break;s=i.nextAfterLabel}e!==void 0&&e!==this.listSeq||(this.allocation=t)}channelById(e){if(B(e))return{id:C,name:this.list?.publicSale?.name??T,marker:"P",color:null};let t=this.list?.channels.find(s=>s.id===e);return t?{id:e,name:t.name,marker:t.marker,color:t.color}:null}nameOf(e){return this.channelById(e)?.name??null}markerFor(e){let t=Math.max(0,this.list?.channels.findIndex(n=>n.id===e)??0),s=this.channelById(e);return pe(s??{id:e,name:"?",marker:null,color:null},t)}assignableChannels(){return[{id:C,name:this.list?.publicSale.name??T},...(this.list?.channels??[]).filter(e=>e.state!=="archived").map(e=>({id:e.id,name:e.name}))]}currentPlan(){let e=this.host.selectionLabels(),t=me({labels:e,targetChannelId:this.targetChannelId,allocation:this.allocation,statusOf:s=>this.host.statusOf(s),nameOf:s=>this.nameOf(s)});return{labels:e,buckets:t,target:this.targetChannelId}}ensureLayer(){if(this.layer)return;let e=document.createElement("div");e.className="slm-ch-layer",e.innerHTML=`
|
|
250
245
|
<canvas class="slm-ch-canvas" data-ch="canvas" aria-hidden="true"></canvas>
|
|
251
246
|
<div class="slm-ch-banner" data-ch="banner" role="status"></div>
|
|
252
247
|
<div class="slm-ch-staged" data-ch="staged" role="group" aria-label="Staged channel changes"></div>
|
|
253
|
-
<div class="slm-ch-live" data-ch="live" role="status" aria-live="polite"></div>`,this.host.mapLayer.appendChild(e),this.layer=e,this.canvas=e.querySelector('[data-ch="canvas"]'),this.bannerEl=e.querySelector('[data-ch="banner"]'),this.stagedEl=e.querySelector('[data-ch="staged"]'),this.liveEl=e.querySelector('[data-ch="live"]'),requestAnimationFrame(()=>e.classList.add("on"))}announce(e){this.liveEl&&(this.liveEl.textContent=e)}paintOverlay(){let e=this.canvas,t=this.layer;!e||!t||
|
|
248
|
+
<div class="slm-ch-live" data-ch="live" role="status" aria-live="polite"></div>`,this.host.mapLayer.appendChild(e),this.layer=e,this.canvas=e.querySelector('[data-ch="canvas"]'),this.bannerEl=e.querySelector('[data-ch="banner"]'),this.stagedEl=e.querySelector('[data-ch="staged"]'),this.liveEl=e.querySelector('[data-ch="live"]'),requestAnimationFrame(()=>e.classList.add("on"))}announce(e){this.liveEl&&(this.liveEl.textContent=e)}paintOverlay(){let e=this.canvas,t=this.layer;!e||!t||lt({layer:t,canvas:e,resolveCtx:()=>{if(this.ctx===void 0)try{this.ctx=e.getContext("2d")}catch{this.ctx=null}return this.ctx},host:this.host,view:this.view,previewProjection:this.previewProjection,allocation:this.allocation,focusedSectionId:this.focusedSectionId,list:this.list,markerFor:s=>this.markerFor(s),onSectionTargetClick:s=>{this.focusedSectionId=s,this.host.focusSection(s),this.paintRail()}})}setStaged(e,t=""){let s=this.stagedEl;if(s){if(!e){s.classList.remove("on","done","shake"),s.innerHTML="";return}s.innerHTML=e,s.className=`slm-ch-staged on${t?` ${t}`:""}`}}paintStagedBar(){if(!this.active||this.view==="preview"||!this.caps.manage){this.setStaged(null);return}let{labels:e,buckets:t}=this.currentPlan();if(this.host.onStagedChange?.(K(t)),!e.length){this.setStaged(null);return}let s=this.nameOf(this.targetChannelId)??T,n=K(t),i=e.length>A,a=t.skippedHeld.count+t.skippedBooked.count,l=[`<b>${e.length.toLocaleString()}</b> selected`,`<b>+${n.toLocaleString()}</b> to ${h(s)}`];t.alreadyInTarget.count&&l.push(`<b>${t.alreadyInTarget.count.toLocaleString()}</b> already in`),a&&l.push(`<b>${a.toLocaleString()}</b> can't move now`),this.setStaged(`
|
|
254
249
|
<span>${l.join(" \xB7 ")}</span>
|
|
255
250
|
<span class="grow"></span>
|
|
256
251
|
<button type="button" class="drop" data-ch-act="discard">Discard</button>
|
|
257
252
|
<button type="button" class="go" data-ch-act="review"${i?" disabled":""}>
|
|
258
|
-
${i?`Maximum ${
|
|
259
|
-
</button>`),this.stagedEl?.querySelectorAll("[data-ch-act]").forEach(r=>{r.addEventListener("click",()=>{r.dataset.chAct==="discard"?this.host.clearSelection():this.openDialog({kind:"review"})})})}setBanner(e,t="",s){let n=this.bannerEl;if(!n)return;if(this.host.root.classList.toggle("ch-preview",e),!e){n.classList.remove("on"),n.innerHTML="";return}let i=this.previewAudience.length===1?this.markerFor(this.previewAudience[0]):{color:"var(--slm-accent)",letter:""},
|
|
260
|
-
Previewing buyer access \xB7 ${h(t)}${
|
|
261
|
-
<button type="button" data-ch-act="exit-preview">Exit preview</button>`,n.classList.add("on"),n.querySelector('[data-ch-act="exit-preview"]')?.addEventListener("click",()=>this.setView("inspect"))}setView(e){if(this.view=e,e==="inspect")this.previewProjection=null,this.previewState="idle",this.setBanner(!1);else{if(!this.previewAudience.length){let t=this.list?.channels.find(s=>s.state==="active");this.previewAudience=[t?t.id:
|
|
253
|
+
${i?`Maximum ${A.toLocaleString()} seats`:"Review changes"}
|
|
254
|
+
</button>`),this.stagedEl?.querySelectorAll("[data-ch-act]").forEach(r=>{r.addEventListener("click",()=>{r.dataset.chAct==="discard"?this.host.clearSelection():this.openDialog({kind:"review"})})})}setBanner(e,t="",s){let n=this.bannerEl;if(!n)return;if(this.host.root.classList.toggle("ch-preview",e),!e){n.classList.remove("on"),n.innerHTML="";return}let i=this.previewAudience.length===1?this.markerFor(this.previewAudience[0]):{color:"var(--slm-accent)",letter:""},a=s==null?"":` \xB7 ${s.toLocaleString()} ${s===1?"seat":"seats"} available now`;n.innerHTML=`<span class="dot" style="background:${h(i.color)}"></span>
|
|
255
|
+
Previewing buyer access \xB7 ${h(t)}${a} \xB7 read-only
|
|
256
|
+
<button type="button" data-ch-act="exit-preview">Exit preview</button>`,n.classList.add("on"),n.querySelector('[data-ch-act="exit-preview"]')?.addEventListener("click",()=>this.setView("inspect"))}setView(e){if(this.view=e,e==="inspect")this.previewProjection=null,this.previewState="idle",this.setBanner(!1);else{if(!this.previewAudience.length){let t=this.list?.channels.find(s=>s.state==="active");this.previewAudience=[t?t.id:C]}this.loadPreview()}this.paintRail(),this.paintOverlay(),this.paintStagedBar(),this.onInteractionChange?.()}async loadPreview(){let e=[...this.previewAudience],t=e.map(s=>this.nameOf(s)??T).join(" + ");this.setBanner(!0,t),this.previewProjection=null,this.previewState="loading",this.active&&this.paintRail();try{this.previewProjection=await this.host.api.channelPreview(this.host.eventKey,e,{includePublic:this.previewIncludePublic||e.some(B)}),this.previewState="ready";let s=this.previewProjection.available===!1?void 0:this.previewProjection.counts?.eligible??this.previewProjection.eligible?.length;this.setBanner(!0,t,s)}catch(s){let n=s instanceof f?s.status:0;this.previewState=n===404||n===405||n===501?"unsupported":"error",this.previewProjection=null,this.previewState==="error"&&this.host.onError(s)}this.active&&(this.paintRail(),this.paintOverlay())}setRailHtml(e){if(e===this.railHtml)return!1;let t=this.host.rail,s=t.scrollTop;return t.innerHTML=e,this.railHtml=e,s&&(t.scrollTop=s),!0}paintRail(){if(!this.active)return;let e=this.host.rail;if(!this.caps.view){this.setRailHtml(`<p class="slm-eyebrow">Sales channels</p>
|
|
262
257
|
<p class="slm-hint">You need channel-management permission on this event to see allocations.</p>`);return}if(this.loading&&!this.list){this.setRailHtml(`<p class="slm-eyebrow">Sales channels</p>
|
|
263
258
|
<div class="slm-ch-busy" role="status" data-ch-state="list-loading">Loading channels and allocations\u2026</div>`);return}if(!this.list&&this.loadError){this.setRailHtml(`<p class="slm-eyebrow">Sales channels</p>
|
|
264
259
|
<div class="slm-ch-alert err" role="alert" data-ch-state="list-error"><span>\u26A0</span>
|
|
@@ -277,28 +272,28 @@
|
|
|
277
272
|
<div class="slm-ch-mapnav-head"><span>${e?`Viewing ${h(e)}`:"Section overview"}</span>
|
|
278
273
|
<button type="button" data-ch-act="sections">All sections</button></div>
|
|
279
274
|
${n}
|
|
280
|
-
</div>`}countsHtml(e,t){let s=(n,i,
|
|
275
|
+
</div>`}countsHtml(e,t){let s=(n,i,a,l="")=>{let r=this.lastCounts.get(`${t}:${n}`),d=r!=null&&r!==i?" bump":"";return this.lastCounts.set(`${t}:${n}`,i),`<span class="${l}"><b class="${d.trim()}">${i.toLocaleString()}</b> ${a}</span>`};return`<span class="slm-ch-counts">
|
|
281
276
|
${s("allocated",e.allocated,"allocated")}
|
|
282
277
|
${s("free",e.free,"free","free")}
|
|
283
278
|
${s("booked",e.booked,"sold")}
|
|
284
279
|
${e.held?s("held",e.held,"held"):""}
|
|
285
|
-
</span>`}channelRowHtml(e,t={}){let s=this.markerFor(e.id),n=t.builtin?"builtin":e.state,i=e.state==="paused"||e.state==="archived"?" dim":"",
|
|
286
|
-
aria-label="More actions for ${h(e.name)}" aria-haspopup="dialog">\u22EF</button>`:"",d=
|
|
280
|
+
</span>`}channelRowHtml(e,t={}){let s=this.markerFor(e.id),n=t.builtin?"builtin":e.state,i=e.state==="paused"||e.state==="archived"?" dim":"",a=!t.builtin&&this.caps.manage&&this.detailChannelId!==e.id,l=`slm-ch-row${t.builtin?" public":""}${e.state==="archived"?" archived":""}${this.detailChannelId===e.id?" on":""}${a?" open":""}`,r=!t.builtin&&this.caps.manage?`<button type="button" class="slm-ch-more" data-ch-menu="${h(e.id)}"
|
|
281
|
+
aria-label="More actions for ${h(e.name)}" aria-haspopup="dialog">\u22EF</button>`:"",d=a?` role="button" tabindex="0" data-ch-open="${h(e.id)}"
|
|
287
282
|
aria-label="Open ${h(e.name)}"`:"";return`<div class="${l}"${d}>
|
|
288
283
|
<span class="slm-ch-head">
|
|
289
284
|
<span class="slm-ch-mk${i}" style="background:${h(s.color)}" aria-hidden="true">${h(s.letter)}</span>
|
|
290
285
|
<span class="slm-ch-name">${h(e.name)}</span>
|
|
291
|
-
<span class="slm-ch-badge ${n}">${h(
|
|
286
|
+
<span class="slm-ch-badge ${n}">${h(Se(t.builtin?"builtin":e.state))}</span>
|
|
292
287
|
${r}
|
|
293
288
|
</span>
|
|
294
289
|
${this.countsHtml(e.counts,e.id||"public")}
|
|
295
290
|
${t.builtin?"":`<span class="slm-ch-access">${h(fe(e.access))}</span>`}
|
|
296
|
-
</div>`}listRailHtml(){let e=this.list,t=e.channels.filter(r=>r.state==="archived").length,s=e.channels.filter(r=>this.showArchived||r.state!=="archived"),n=[this.channelRowHtml(e.publicSale,{builtin:!0}),...s.map((r,d)=>this.channelRowHtml(r,{index:d}))].join(""),i=s.length?"":`<p class="slm-note" data-ch-state="list-empty">${t&&!this.showArchived?`No open channels \u2014 every seat is on public sale. ${t.toLocaleString()} archived channel${t===1?" is":"s are"} hidden below.`:"No private channels yet \u2014 every seat is on public sale."}</p>`,
|
|
291
|
+
</div>`}listRailHtml(){let e=this.list,t=e.channels.filter(r=>r.state==="archived").length,s=e.channels.filter(r=>this.showArchived||r.state!=="archived"),n=[this.channelRowHtml(e.publicSale,{builtin:!0}),...s.map((r,d)=>this.channelRowHtml(r,{index:d}))].join(""),i=s.length?"":`<p class="slm-note" data-ch-state="list-empty">${t&&!this.showArchived?`No open channels \u2014 every seat is on public sale. ${t.toLocaleString()} archived channel${t===1?" is":"s are"} hidden below.`:"No private channels yet \u2014 every seat is on public sale."}</p>`,a=this.caps.manage?'<button type="button" class="slm-btn ghost" style="width:100%" data-ch-act="create">+ Create channel</button>':"",l=this.caps.manage?"":'<p class="slm-note">You can see how inventory is allocated. Changing it needs channel-management permission.</p>';return`
|
|
297
292
|
<p class="slm-eyebrow">Sales channels</p>
|
|
298
293
|
<p class="slm-hint">Channel colours and names are only visible to organizers, never to buyers.</p>
|
|
299
294
|
<div class="slm-ch-list">${n}</div>
|
|
300
295
|
${i}
|
|
301
|
-
${
|
|
296
|
+
${a}
|
|
302
297
|
${l}
|
|
303
298
|
${this.assignEntryHtml()}
|
|
304
299
|
<p class="slm-note" style="margin-top:10px">
|
|
@@ -328,16 +323,16 @@
|
|
|
328
323
|
multiple rows, a dragged area, or individual seats.</p>`}selectionRailHtml(e){let t=ue(e,this.allocation,this.list),s=this.conflict?`<div class="slm-ch-alert err" role="alert"><span>\u26A0</span>
|
|
329
324
|
<span><b>Assignments changed while you were editing.</b> Nothing was applied.
|
|
330
325
|
Your ${e.length.toLocaleString()}-seat selection is kept.
|
|
331
|
-
<button type="button" data-ch-act="refresh-review">Refresh and review \u2192</button></span></div>`:"",n=e.length!==this.lastSelectionCount?" bump":"";this.lastSelectionCount=e.length;let i=e.length>
|
|
326
|
+
<button type="button" data-ch-act="refresh-review">Refresh and review \u2192</button></span></div>`:"",n=e.length!==this.lastSelectionCount?" bump":"";this.lastSelectionCount=e.length;let i=e.length>A,a=t.map(l=>{let r=this.markerFor(l.channelId);return`<div class="slm-ch-selsrc-row">
|
|
332
327
|
<span class="mk" style="background:${h(r.color)}" aria-hidden="true">${h(r.letter)}</span>
|
|
333
328
|
<b>${l.count.toLocaleString()}</b><span>${h(l.name)}</span></div>`}).join("");return`
|
|
334
329
|
${s}
|
|
335
330
|
${this.assignmentToolsHtml()}
|
|
336
331
|
<div class="slm-selbar"><span class="slm-selnum slm-ch-selnum${n}">${e.length.toLocaleString()}</span>
|
|
337
332
|
<span class="slm-sellabel">selected</span></div>
|
|
338
|
-
<div class="slm-ch-selsrc" aria-live="polite" aria-label="Selection sources">${
|
|
333
|
+
<div class="slm-ch-selsrc" aria-live="polite" aria-label="Selection sources">${a}</div>
|
|
339
334
|
${i?`<div class="slm-ch-alert warn" role="alert"><span>\u2139</span><span>
|
|
340
|
-
<b>This selection is too large to apply at once.</b> Choose at most ${
|
|
335
|
+
<b>This selection is too large to apply at once.</b> Choose at most ${A.toLocaleString()}
|
|
341
336
|
seats, for example by splitting the venue into row groups.</span></div>`:""}
|
|
342
337
|
<p class="slm-note">Changes are staged \u2014 nothing moves until you review and apply.
|
|
343
338
|
Seats in checkout or already sold are never moved.</p>
|
|
@@ -347,7 +342,7 @@
|
|
|
347
342
|
</div>
|
|
348
343
|
<p class="slm-note">The seat list offers the same selection with checkboxes for keyboard and screen-reader use.</p>`}detailRailHtml(e){let t=`<p class="slm-eyebrow">
|
|
349
344
|
<button type="button" class="slm-linkbtn" data-ch-act="back" style="text-align:left">\u2039 All channels</button>
|
|
350
|
-
</p>`,s=this.list?.channels.find(
|
|
345
|
+
</p>`,s=this.list?.channels.find(a=>a.id===e);if(!s)return`${t}
|
|
351
346
|
<div class="slm-ch-alert warn" role="status" data-ch-state="detail-gone"><span>\u2139</span>
|
|
352
347
|
<span><b>This channel is no longer on this event.</b> It was archived or removed while you had it open.
|
|
353
348
|
${this.showArchived?"":"Archived channels are hidden \u2014 use \u201CShow archived\u201D on the list to see it."}</span></div>`;let n=this.caps.manage?`
|
|
@@ -362,20 +357,20 @@
|
|
|
362
357
|
<p class="slm-eyebrow">Channel \xB7 ${h(s.name)}</p>
|
|
363
358
|
<div class="slm-ch-list">${this.channelRowHtml(s)}</div>
|
|
364
359
|
${i}
|
|
365
|
-
${n}`}distributeHtml(e){let t=e.access?.intent??"none",s=e.access?.hasActiveGrants===!0,n=this.linksState!=="unsupported",i=this.links.some(te),
|
|
366
|
-
<b>${h(
|
|
360
|
+
${n}`}distributeHtml(e){let t=e.access?.intent??"none",s=e.access?.hasActiveGrants===!0,n=this.linksState!=="unsupported",i=this.links.some(te),a=t==="none"?"Protected reserve \u2014 no route can sell these seats. Every buyer path is refused.":t==="internal"?"Your staff sell these seats. No buyer-facing route is open.":s?t==="server"?"Your website is letting buyers in. Only they can buy these seats.":"A buyer link is live. Only people with that link can buy these seats.":t==="server"?"Set up for your website \u2014 no buyer has come through yet. Seats stay reserved.":"Set up for buyer links \u2014 no buyer has come through yet. Seats stay reserved.",l=(r,d,c,p)=>{let g=r===t;return`<div class="slm-ch-dist${g?" on":""}" data-ch-route="${h(r)}">
|
|
361
|
+
<b>${h(z(r))}${g?'<span class="cur">Current route</span>':""}</b>
|
|
367
362
|
<span class="why">${h(ye(r))}</span>
|
|
368
363
|
${g&&!c?"":`<button type="button" class="slm-btn${p&&!g?"":" ghost"}"
|
|
369
364
|
data-ch-act="${h(d)}">${h(c)}</button>`}
|
|
370
365
|
</div>`};return`
|
|
371
366
|
<p class="slm-eyebrow" style="margin-top:14px">Distribute</p>
|
|
372
|
-
<p class="slm-hint">${h(
|
|
367
|
+
<p class="slm-hint">${h(a)}</p>
|
|
373
368
|
${n?l("hosted_link","link-create",i?"Create another buyer link":"Create buyer link",!0):""}
|
|
374
369
|
${l("server","embed-code",t==="server"?"Get embed code":"Use a website or app",!1)}
|
|
375
370
|
${l("internal","route-internal",t==="internal"?"":"Hand to your staff",!1)}
|
|
376
371
|
${l("none","route-none",t==="none"?"":"Keep as reserve",!1)}
|
|
377
372
|
${this.hostedLinksHtml()}
|
|
378
|
-
${t==="server"?
|
|
373
|
+
${t==="server"?at:""}`}async loadLinks(e){if(!this.caps.view)return;let t=++this.linksSeq,s=()=>t!==this.linksSeq||this.linksChannelId!==e;this.linksChannelId!==e&&(this.links=[],this.linksChannelId=e,this.linksState="loading");try{let n=await this.host.api.accessLinks(this.host.eventKey,e);if(s())return;this.links=n.links??[],this.linksState="ready"}catch(n){if(s())return;let i=n instanceof f?n.status:0;this.links=[],this.linksState=i===404||i===405||i===501?"unsupported":"error",this.linksState==="error"&&this.host.onError(n)}}hostedLinksHtml(){let e='<p class="slm-eyebrow" style="margin-top:18px">Buyer links</p>';return this.linksState==="unsupported"?`${e}<div class="slm-ch-alert warn" data-ch-state="links-unsupported"><span>\u2139</span>
|
|
379
374
|
<span><b>Buyer links need a newer server.</b> Everything else on this channel works normally.</span></div>`:this.linksState==="error"?`${e}<div class="slm-ch-alert err" role="alert" data-ch-state="links-error"><span>\u26A0</span>
|
|
380
375
|
<span><b>Couldn't load this channel's links.</b> This is a failed read, not an empty list \u2014
|
|
381
376
|
any live link is still working.
|
|
@@ -384,7 +379,7 @@
|
|
|
384
379
|
<p class="slm-note">A link is shown once, when you create it. SeatLayer keeps only a fingerprint of it, so it
|
|
385
380
|
can never be shown again \u2014 if a link is lost, rotate it and send the fresh one.</p>`:""}linkCardHtml(e){let t=we(e),s=e.maxRedemptions>0?Math.min(100,Math.round(e.redemptions/e.maxRedemptions*100)):0,n=se(e).map(r=>`<div class="slm-ch-lkrow"><span class="k">${h(r.k)}</span>
|
|
386
381
|
<span class="v">${h(r.v)}</span></div>`).join(""),i=e.activeSessions?`<div class="slm-ch-lkrow"><span class="k">Buyers inside now</span>
|
|
387
|
-
<span class="v">${e.activeSessions.toLocaleString()}</span></div>`:"",
|
|
382
|
+
<span class="v">${e.activeSessions.toLocaleString()}</span></div>`:"",a=e.lastRedeemedAt?`<div class="slm-ch-lkrow"><span class="k">Last opened</span>
|
|
388
383
|
<span class="v">${h(new Date(e.lastRedeemedAt).toLocaleString())}</span></div>`:"",l=this.caps.manage&&te(e)?`<div class="slm-ch-row2">
|
|
389
384
|
<button type="button" class="slm-btn ghost" data-ch-rotate="${h(e.id)}">Rotate</button>
|
|
390
385
|
<button type="button" class="slm-btn ghost" data-ch-revoke="${h(e.id)}">Revoke</button>
|
|
@@ -396,19 +391,19 @@
|
|
|
396
391
|
<div class="slm-ch-meter" role="img"
|
|
397
392
|
aria-label="${e.redemptions.toLocaleString()} of ${e.maxRedemptions.toLocaleString()} redemptions used">
|
|
398
393
|
<i style="width:${s}%"></i></div>
|
|
399
|
-
${n}${i}${
|
|
394
|
+
${n}${i}${a}
|
|
400
395
|
<div class="slm-ch-lkrow"><span class="k">The URL</span>
|
|
401
396
|
<span class="v">Revealed once at creation \u2014 not recoverable</span></div>
|
|
402
397
|
${l}
|
|
403
|
-
</div>`}previewRailHtml(){let e=[{id:
|
|
398
|
+
</div>`}previewRailHtml(){let e=[{id:C,name:this.list?.publicSale.name??T},...(this.list?.channels??[]).filter(p=>p.state!=="archived").map(p=>({id:p.id,name:p.name}))],t=this.previewAudience[0]??C,s=e.map(p=>`<option value="${h(p.id)}"${p.id===t?" selected":""}>${h(p.name)}</option>`).join(""),n=this.previewState==="unsupported"?`<div class="slm-ch-alert warn" data-ch-state="preview-unsupported"><span>\u2139</span>
|
|
404
399
|
<span><b>Preview needs a newer server.</b> Allocation management works normally;
|
|
405
|
-
the buyer-view simulation will appear once this event's API is updated.</span></div>`:"",i=this.previewState==="loading"?'<div class="slm-ch-busy" role="status" data-ch-state="preview-loading">Asking the server what this audience sees\u2026</div>':"",
|
|
400
|
+
the buyer-view simulation will appear once this event's API is updated.</span></div>`:"",i=this.previewState==="loading"?'<div class="slm-ch-busy" role="status" data-ch-state="preview-loading">Asking the server what this audience sees\u2026</div>':"",a=this.previewState==="error"?`<div class="slm-ch-alert err" role="alert" data-ch-state="preview-error"><span>\u26A0</span>
|
|
406
401
|
<span><b>Couldn't load the buyer preview.</b> Nothing is wrong with this audience's seats \u2014
|
|
407
402
|
the simulation itself failed to load.
|
|
408
403
|
<button type="button" data-ch-act="preview-retry">Try again</button></span></div>`:"",l=this.previewProjection?.available===!1?`<div class="slm-ch-alert warn" role="status"><span>\u23F8</span>
|
|
409
404
|
<span><b>This private sale is not available.</b> ${h((this.previewProjection.unavailable??[]).map(p=>`${this.nameOf(p.channelId)??"This channel"} is ${p.state}`).join("; ")||"The audience cannot buy right now")}.
|
|
410
405
|
A buyer arriving with this access sees this message, not these seats.</span></div>`:"",r=this.previewProjection?.counts?.eligible??this.previewProjection?.eligible?.length,d=r!=null&&this.previewProjection?.available!==!1?`<div class="slm-ch-alert info"><span>\u2713</span><span><b>${r.toLocaleString()} ${r===1?"seat is":"seats are"} available now.</b>
|
|
411
|
-
This is the exact buyer-visible allocation.${this.previewProjection?.includePublic===!1?" Public sale seats are <b>not</b> included in this access.":""}</span></div>`:"",c=
|
|
406
|
+
This is the exact buyer-visible allocation.${this.previewProjection?.includePublic===!1?" Public sale seats are <b>not</b> included in this access.":""}</span></div>`:"",c=B(t)?"":`
|
|
412
407
|
<label class="slm-note" style="display:flex;gap:8px;align-items:center;margin:10px 0">
|
|
413
408
|
<input type="checkbox" data-ch-includepublic ${this.previewIncludePublic?"checked":""} />
|
|
414
409
|
Also include Public sale seats in this grant
|
|
@@ -421,13 +416,13 @@
|
|
|
421
416
|
${c}
|
|
422
417
|
<p class="slm-hint">This is the same projection the buyer SDK receives for this audience \u2014 not a local
|
|
423
418
|
approximation. It is read-only: clicks open seat details, and no holds are created.</p>
|
|
424
|
-
${i}${
|
|
419
|
+
${i}${a}${n}${l}
|
|
425
420
|
<div class="slm-ch-legend">
|
|
426
421
|
<div class="r"><span class="sw" style="background:#6e7bff"></span> Eligible & free \u2014 buyable by this audience</div>
|
|
427
422
|
<div class="r"><span class="sw" style="background:#3a4051"></span> Unavailable to this audience (one neutral state)</div>
|
|
428
423
|
<div class="r"><span class="sw" style="background:#22a06b"></span> Sold \u2014 same as any buyer sees</div>
|
|
429
424
|
</div>
|
|
430
|
-
${d}`}paintSelection(){this.caps.manage&&this.host.selectionLabels().length&&(this.assignOpen=!0),this.view==="inspect"&&!this.detailChannelId&&this.paintRail()}wireRail(){let e=this.host.rail;e.querySelectorAll("[data-ch-view]").forEach(
|
|
425
|
+
${d}`}paintSelection(){this.caps.manage&&this.host.selectionLabels().length&&(this.assignOpen=!0),this.view==="inspect"&&!this.detailChannelId&&this.paintRail()}wireRail(){let e=this.host.rail;e.querySelectorAll("[data-ch-view]").forEach(a=>{a.addEventListener("click",()=>this.setView(a.dataset.chView))}),e.querySelectorAll("[data-ch-map]").forEach(a=>{a.addEventListener("click",()=>{this.mapIntent=a.dataset.chMap==="assign"?"assign":"pan",this.mapIntent==="assign"&&(this.assignOpen=!0),this.paintRail(),this.onInteractionChange?.()})}),e.querySelectorAll("[data-ch-open]").forEach(a=>{let l=()=>this.openChannel(a.dataset.chOpen);a.addEventListener("click",l),a.addEventListener("keydown",r=>{r.key!=="Enter"&&r.key!==" "&&r.key!=="Spacebar"||(r.preventDefault(),l())})}),e.querySelectorAll("[data-ch-menu]").forEach(a=>{a.addEventListener("click",l=>{l.stopPropagation(),this.openDialog({kind:"menu",channelId:a.dataset.chMenu})}),a.addEventListener("keydown",l=>{l.stopPropagation()})}),e.querySelectorAll("[data-ch-rotate]").forEach(a=>{a.addEventListener("click",()=>this.openDialog({kind:"linkRotate",channelId:this.detailChannelId,linkId:a.dataset.chRotate}))}),e.querySelectorAll("[data-ch-revoke]").forEach(a=>{a.addEventListener("click",()=>this.openDialog({kind:"linkRevoke",channelId:this.detailChannelId,linkId:a.dataset.chRevoke}))});let t=e.querySelector("[data-ch-target]");t?.addEventListener("change",()=>{this.targetChannelId=t.value,this.conflict=!1,this.paintRail()});let s=e.querySelector("[data-ch-audience]");s?.addEventListener("change",()=>{this.previewAudience=[s.value],this.loadPreview()});let n=e.querySelector("[data-ch-includepublic]");n?.addEventListener("change",()=>{this.previewIncludePublic=n.checked,this.loadPreview()}),e.querySelector(".slm-ch-grab")?.addEventListener("click",()=>this.cycleDetent()),e.querySelectorAll("[data-ch-act]").forEach(a=>{a.addEventListener("click",()=>this.railAction(a.dataset.chAct))})}openChannel(e){this.detailChannelId=e,this.paintRail(),this.loadLinks(e).then(()=>this.paintRail())}railAction(e){switch(e){case"sections":this.focusedSectionId=null,this.host.showSectionOverview(),this.paintRail();break;case"create":this.openDialog({kind:"create"});break;case"review":this.openDialog({kind:"review"});break;case"rename":this.openDialog({kind:"rename",channelId:this.detailChannelId});break;case"archive":this.openDialog({kind:"archive",channelId:this.detailChannelId});break;case"seatlist":this.openDialog({kind:"seatlist"});break;case"pause":this.togglePause();break;case"discard":this.host.clearSelection();break;case"back":this.detailChannelId=null,this.linksChannelId=null,this.links=[],this.linksState="idle",this.paintRail();break;case"link-create":this.openDialog({kind:"linkCreate",channelId:this.detailChannelId});break;case"embed-code":this.chooseWebsiteIntegration();break;case"route-internal":this.setAccessIntent("internal");break;case"route-none":this.setAccessIntent("none");break;case"link-reload":this.detailChannelId&&this.reloadLinks();break;case"retry":this.refresh();break;case"toggle-archived":this.showArchived=!this.showArchived,this.refresh();break;case"refresh-review":this.conflict=!1,this.refresh().then(()=>this.openDialog({kind:"review"}));break;case"pick-sections":this.openDialog({kind:"scope",scope:"sections"});break;case"pick-rows":this.openDialog({kind:"scope",scope:"rows"});break;case"drag-select":this.mapIntent="assign",this.paintRail(),this.onInteractionChange?.();break;case"pick-category":this.pickCategory();break;case"assign-open":this.assignOpen=!0,this.paintRail();break;case"assign-close":this.assignOpen=!1,this.mapIntent="pan",this.paintRail(),this.onInteractionChange?.();break;case"preview-retry":this.loadPreview();break;default:break}}assignmentRows(){return this.assignmentRowsCache||(this.assignmentRowsCache=this.host.rows()),this.assignmentRowsCache}pickCategory(){let e=this.host.categories();e.length&&this.promptChoice("Select a whole category",e.map(t=>({value:t.key,label:t.label})),t=>{this.host.selectByLabels(this.host.labelsInCategory(t))})}promptChoice(e,t,s){this.renderScrim(`
|
|
431
426
|
<h3 id="slm-ch-dlg-title">${h(e)}</h3>
|
|
432
427
|
<div class="slm-field">
|
|
433
428
|
<label for="slm-ch-choice">Choose one</label>
|
|
@@ -438,7 +433,7 @@
|
|
|
438
433
|
<div class="foot">
|
|
439
434
|
<button type="button" class="quiet" data-ch-close>Cancel</button>
|
|
440
435
|
<button type="button" class="slm-btn" data-ch-confirm>Select</button>
|
|
441
|
-
</div>`,n=>{n.querySelector("[data-ch-confirm]")?.addEventListener("click",()=>{let
|
|
436
|
+
</div>`,n=>{n.querySelector("[data-ch-confirm]")?.addEventListener("click",()=>{let a=n.querySelector("#slm-ch-choice")?.value;this.closeDialog(),a&&s(a)})})}renderScopeDialog(e){let t=e.scope==="rows"?"rows":"sections",n=(t==="sections"?this.host.sections().map(l=>({id:l.id,label:l.label,group:"Sections",labels:this.host.labelsInSection(l.id)})):this.assignmentRows().map(l=>({id:l.id,label:l.label,group:l.sectionLabel,labels:l.labels}))).filter(l=>l.labels.length>0),i=new Map;for(let l of n){let r=i.get(l.group)??[];r.push(l),i.set(l.group,r)}if(t==="rows"){let l=[...i.entries()];this.renderScrim(`
|
|
442
437
|
<h3 id="slm-ch-dlg-title">Add multiple rows</h3>
|
|
443
438
|
<p class="sub">Sections stay collapsed for speed. Select a whole section, expand only the rows you need, or search across every row.</p>
|
|
444
439
|
<div class="slm-ch-scopebar">
|
|
@@ -451,20 +446,20 @@
|
|
|
451
446
|
<div class="foot">
|
|
452
447
|
<button type="button" class="quiet" data-ch-close>Cancel</button>
|
|
453
448
|
<button type="button" class="slm-btn" data-ch-add-scope disabled>Add seats</button>
|
|
454
|
-
</div>`,r=>{let d=new Map(n.map(y=>[y.id,y])),c=new Set,p=new Set,g=new Set(this.host.selectionLabels()),w=r.querySelector("[data-ch-add-scope]"),
|
|
455
|
-
<button type="button" class="slm-ch-groupcheck" role="checkbox" aria-checked="${
|
|
456
|
-
aria-label="Select all ${R.length.toLocaleString()} rows in ${h(
|
|
457
|
-
<span class="box" aria-hidden="true">\u2713</span><span>${h(
|
|
458
|
-
<span class="meta">${R.length.toLocaleString()} rows \xB7 ${
|
|
449
|
+
</div>`,r=>{let d=new Map(n.map(y=>[y.id,y])),c=new Set,p=new Set,g=new Set(this.host.selectionLabels()),w=r.querySelector("[data-ch-add-scope]"),b=r.querySelector("[data-ch-error]"),m=r.querySelector("[data-ch-scope-list]"),v=r.querySelector("[data-ch-scope-search]"),P=r.querySelector("[data-ch-scope-summary]"),F=r.querySelector("[data-ch-scope-filter]"),q=200,E="",x=()=>{let y=new Set(g);for(let H of c)for(let D of d.get(H)?.labels??[])y.add(D);return y},S=()=>{let y=x(),H=y.size-g.size,D=y.size>A;w.disabled=H===0||D,w.textContent=D?`Maximum ${A.toLocaleString()} seats`:`Add ${H.toLocaleString()} seat${H===1?"":"s"}`,b.hidden=!D,b.textContent=D?`That would make ${y.size.toLocaleString()} selected seats. Choose fewer rows or sections.`:"",P.textContent=`${c.size.toLocaleString()} row${c.size===1?"":"s"} \xB7 ${H.toLocaleString()} seat${H===1?"":"s"} added`},k=()=>{let y=[],H=0,D=0;l.forEach(([M,R],G)=>{let j=M.toLocaleLowerCase(),V=E?R.filter(I=>`${j} ${I.label.toLocaleLowerCase()}`.includes(E)):R;if(E&&!V.length)return;H+=V.length;let Fe=R.every(I=>c.has(I.id)),mt=!Fe&&R.some(I=>c.has(I.id)),X=!!E||p.has(G),bt=Math.max(0,q-D),Q=X?V.slice(0,bt):[];D+=Q.length;let gt=R.reduce((I,vt)=>I+vt.labels.length,0);y.push(`<div class="slm-ch-scopegroup">
|
|
450
|
+
<button type="button" class="slm-ch-groupcheck" role="checkbox" aria-checked="${Fe?"true":mt?"mixed":"false"}"
|
|
451
|
+
aria-label="Select all ${R.length.toLocaleString()} rows in ${h(M)}" data-ch-scope-group="${G}">
|
|
452
|
+
<span class="box" aria-hidden="true">\u2713</span><span>${h(M)}</span>
|
|
453
|
+
<span class="meta">${R.length.toLocaleString()} rows \xB7 ${gt.toLocaleString()} seats</span>
|
|
459
454
|
</button>
|
|
460
|
-
<button type="button" class="slm-ch-grouptoggle" aria-expanded="${X}" aria-label="${X?"Hide":"Show"} rows in ${h(
|
|
455
|
+
<button type="button" class="slm-ch-grouptoggle" aria-expanded="${X}" aria-label="${X?"Hide":"Show"} rows in ${h(M)}"
|
|
461
456
|
data-ch-scope-toggle="${G}">${X?"Hide":"Show"}</button>
|
|
462
|
-
</div>`),y.push(...Q.map(
|
|
463
|
-
aria-checked="${c.has(
|
|
464
|
-
<span class="box" aria-hidden="true">\u2713</span><span>${h(
|
|
465
|
-
<span class="meta">${
|
|
466
|
-
</button>`)),X&&V.length>Q.length&&y.push(`<p class="slm-ch-scopehint">${(V.length-Q.length).toLocaleString()} more row${V.length-Q.length===1?"":"s"}. Search to narrow the list.</p>`)}),
|
|
467
|
-
Use Drag box or the seat list instead.</div>`),
|
|
457
|
+
</div>`),y.push(...Q.map(I=>`<button type="button" class="slm-ch-seatitem" role="checkbox"
|
|
458
|
+
aria-checked="${c.has(I.id)}" data-ch-scope-id="${h(I.id)}">
|
|
459
|
+
<span class="box" aria-hidden="true">\u2713</span><span>${h(I.label)}</span>
|
|
460
|
+
<span class="meta">${I.labels.length.toLocaleString()} seat${I.labels.length===1?"":"s"}</span>
|
|
461
|
+
</button>`)),X&&V.length>Q.length&&y.push(`<p class="slm-ch-scopehint">${(V.length-Q.length).toLocaleString()} more row${V.length-Q.length===1?"":"s"}. Search to narrow the list.</p>`)}),m.innerHTML=y.join("")||(E?`<div class="slm-ch-scope-empty" data-ch-state="scope-nomatch">No sections or rows match \u201C${h(E)}\u201D.</div>`:`<div class="slm-ch-scope-empty" data-ch-state="scope-empty">This chart has no rows with selectable seats.
|
|
462
|
+
Use Drag box or the seat list instead.</div>`),F.textContent=E?`Showing ${D.toLocaleString()} of ${H.toLocaleString()} matching rows. Section checkboxes still select every row in that section.`:"Showing section groups. Expand one or search to see rows.",m.querySelectorAll("[data-ch-scope-group]").forEach(M=>M.addEventListener("click",()=>{let R=l[Number(M.dataset.chScopeGroup)]?.[1]??[],G=R.every(j=>c.has(j.id));for(let j of R)G?c.delete(j.id):c.add(j.id);k(),S()})),m.querySelectorAll("[data-ch-scope-toggle]").forEach(M=>M.addEventListener("click",()=>{let R=Number(M.dataset.chScopeToggle);p.has(R)?p.delete(R):p.add(R),k()})),m.querySelectorAll("[data-ch-scope-id]").forEach(M=>M.addEventListener("click",()=>{let R=M.dataset.chScopeId;c.has(R)?c.delete(R):c.add(R),k(),S()}))};v.addEventListener("input",()=>{E=v.value.trim().toLocaleLowerCase(),k(),S()}),w.addEventListener("click",()=>{let y=[...x()];!c.size||y.length>A||(this.closeDialog(),this.host.selectByLabels(y))}),k(),S()});return}let a=[...i.entries()].map(([l,r])=>`
|
|
468
463
|
<div class="slm-ch-seatgroup"><span>${h(l)}</span></div>
|
|
469
464
|
${r.map(d=>`<button type="button" class="slm-ch-seatitem" role="checkbox"
|
|
470
465
|
aria-checked="false" data-ch-scope-id="${h(d.id)}">
|
|
@@ -474,17 +469,17 @@
|
|
|
474
469
|
</button>`).join("")}`).join("");this.renderScrim(`
|
|
475
470
|
<h3 id="slm-ch-dlg-title">Add whole sections</h3>
|
|
476
471
|
<p class="sub">Choose one or more. They are added to the seats already selected on the map.</p>
|
|
477
|
-
<div class="slm-ch-seatlist">${
|
|
472
|
+
<div class="slm-ch-seatlist">${a||`<div class="slm-ch-scope-empty" data-ch-state="scope-empty">This chart has no sections with selectable seats.
|
|
478
473
|
Use Drag box or the seat list instead.</div>`}</div>
|
|
479
474
|
<p class="slm-ch-err" data-ch-error hidden></p>
|
|
480
475
|
<div class="foot">
|
|
481
476
|
<button type="button" class="quiet" data-ch-close>Cancel</button>
|
|
482
477
|
<button type="button" class="slm-btn" data-ch-add-scope disabled>Add seats</button>
|
|
483
|
-
</div>`,l=>{let r=new Map(n.map(
|
|
478
|
+
</div>`,l=>{let r=new Map(n.map(m=>[m.id,m])),d=new Set,c=new Set(this.host.selectionLabels()),p=l.querySelector("[data-ch-add-scope]"),g=l.querySelector("[data-ch-error]"),w=()=>{let m=new Set(c);for(let v of d)for(let P of r.get(v)?.labels??[])m.add(P);return m},b=()=>{let m=w(),v=m.size-c.size,P=m.size>A;p.disabled=v===0||P,p.textContent=P?`Maximum ${A.toLocaleString()} seats`:`Add ${v.toLocaleString()} seat${v===1?"":"s"}`,g.hidden=!P,g.textContent=P?`That would make ${m.size.toLocaleString()} selected seats. Choose fewer rows or sections.`:""};l.querySelectorAll("[data-ch-scope-id]").forEach(m=>{m.addEventListener("click",()=>{let v=m.dataset.chScopeId;d.has(v)?d.delete(v):d.add(v),m.setAttribute("aria-checked",String(d.has(v))),b()})}),p.addEventListener("click",()=>{let m=[...w()];!d.size||m.length>A||(this.closeDialog(),this.host.selectByLabels(m))}),b()})}openDialog(e){this.dialog=e,this.renderDialog()}renderDialog(){let e=this.dialog;e&&(e.kind==="create"?this.renderCreateDialog(e):e.kind==="review"?this.renderReviewDialog(e):e.kind==="scope"?this.renderScopeDialog(e):e.kind==="archive"?this.renderArchiveDialog(e):e.kind==="rename"?this.renderRenameDialog(e):e.kind==="seatlist"?this.renderSeatListDialog():e.kind==="menu"?this.renderMenuDialog(e):e.kind==="linkCreate"?this.renderLinkCreateDialog(e):e.kind==="linkRotate"?this.renderLinkRotateDialog(e):e.kind==="linkRevoke"?this.renderLinkRevokeDialog(e):e.kind==="intentSwitch"&&this.renderIntentSwitchDialog(e))}renderIntentSwitchDialog(e){let t=this.list?.channels.find(a=>a.id===e.channelId),s=e.intentTo;if(!t||!s||!this.caps.manage){this.closeDialog();return}let{headline:n,consequences:i}=ke(e.switchBlocked);this.renderScrim(`
|
|
484
479
|
<h3 id="slm-ch-dlg-title">Change how ${h(t.name)} reaches buyers?</h3>
|
|
485
480
|
<p class="sub">${h(n)}</p>
|
|
486
481
|
<div class="slm-ch-alert warn" role="alert"><span>\u26A0</span><span>
|
|
487
|
-
${i.map(
|
|
482
|
+
${i.map(a=>`<span style="display:block;margin-bottom:6px">${h(a)}</span>`).join("")}
|
|
488
483
|
</span></div>
|
|
489
484
|
${e.pendingLink?`<p class="slm-note">Your new buyer link is created as soon as
|
|
490
485
|
the route changes \u2014 you will not have to fill the form in again.</p>`:""}
|
|
@@ -492,9 +487,9 @@
|
|
|
492
487
|
<div class="foot">
|
|
493
488
|
<button type="button" class="quiet" data-ch-close>Leave it as it is</button>
|
|
494
489
|
<button type="button" class="slm-btn" data-ch-intent-ack${e.busy?" disabled":""}>
|
|
495
|
-
${e.busy?"Changing\u2026":`Change to "${h(
|
|
496
|
-
</div>`,
|
|
497
|
-
aria-labelledby="slm-ch-dlg-title" tabindex="-1">${e}</div>`,this.host.root.appendChild(n),this.scrimEl=n;let i=n.firstElementChild;i.querySelectorAll("[data-ch-close]").forEach(l=>{l.addEventListener("click",()=>this.closeDialog())}),n.addEventListener("keydown",l=>{if(l.key==="Escape"){l.stopPropagation(),this.closeDialog();return}if(l.key!=="Tab")return;let r=[...i.querySelectorAll('button:not([disabled]),select,input,textarea,a[href],[tabindex]:not([tabindex="-1"])')];if(!r.length)return;let d=r[0],c=r[r.length-1];l.shiftKey&&document.activeElement===d?(l.preventDefault(),c.focus()):!l.shiftKey&&document.activeElement===c&&(l.preventDefault(),d.focus())}),t(i),(i.querySelector("input,select,button")??i).focus()}closeDialog(e={}){this.dialog=null,this.scrimEl?.remove(),this.scrimEl=null,e.restoreFocus!==!1&&this.lastFocus?.focus?.(),this.lastFocus=null}renderCreateDialog(e){let t=(this.list?.channels??[]).map(n=>
|
|
490
|
+
${e.busy?"Changing\u2026":`Change to "${h(z(s))}"`}</button>
|
|
491
|
+
</div>`,a=>{a.querySelector("[data-ch-intent-ack]")?.addEventListener("click",()=>{this.acknowledgeIntentSwitch(s,e.pendingLink??null)})})}async acknowledgeIntentSwitch(e,t){let s=this.detailChannelId;if(!s){this.closeDialog();return}if(this.dialog&&(this.dialog.busy=!0,this.renderDialog()),!await this.setAccessIntent(e,{acknowledgeLiveAccess:!0})){this.dialog?.kind==="intentSwitch"&&(this.dialog.busy=!1,this.renderDialog());return}if(!t){this.closeDialog();return}this.dialog?.kind==="intentSwitch"&&(this.dialog.busy=!1,this.renderDialog()),await this.mintLink(s,t)}renderScrim(e,t){let s=this.scrimEl;s||(this.lastFocus=document.activeElement??null),s?.remove();let n=document.createElement("div");n.className="slm-ch-scrim",n.innerHTML=`<div class="slm-ch-dialog" role="dialog" aria-modal="true"
|
|
492
|
+
aria-labelledby="slm-ch-dlg-title" tabindex="-1">${e}</div>`,this.host.root.appendChild(n),this.scrimEl=n;let i=n.firstElementChild;i.querySelectorAll("[data-ch-close]").forEach(l=>{l.addEventListener("click",()=>this.closeDialog())}),n.addEventListener("keydown",l=>{if(l.key==="Escape"){l.stopPropagation(),this.closeDialog();return}if(l.key!=="Tab")return;let r=[...i.querySelectorAll('button:not([disabled]),select,input,textarea,a[href],[tabindex]:not([tabindex="-1"])')];if(!r.length)return;let d=r[0],c=r[r.length-1];l.shiftKey&&document.activeElement===d?(l.preventDefault(),c.focus()):!l.shiftKey&&document.activeElement===c&&(l.preventDefault(),d.focus())}),t(i),(i.querySelector("input,select,button")??i).focus()}closeDialog(e={}){this.dialog=null,this.scrimEl?.remove(),this.scrimEl=null,e.restoreFocus!==!1&&this.lastFocus?.focus?.(),this.lastFocus=null}renderCreateDialog(e){let t=(this.list?.channels??[]).map(n=>O(n.marker||n.name,"")),s=J("",t);this.renderScrim(`
|
|
498
493
|
<h3 id="slm-ch-dlg-title">Create channel</h3>
|
|
499
494
|
<p class="sub">A named allocation only the right audience can buy from. You'll pick the seats next.</p>
|
|
500
495
|
<div class="slm-field">
|
|
@@ -519,22 +514,22 @@
|
|
|
519
514
|
<button type="button" class="quiet" data-ch-close>Cancel</button>
|
|
520
515
|
<button type="button" class="slm-btn ghost" data-ch-create="plain">Create without allocating</button>
|
|
521
516
|
<button type="button" class="slm-btn" data-ch-create="allocate">Create and allocate seats</button>
|
|
522
|
-
</div>`,n=>{let i=n.querySelector("#slm-ch-name"),
|
|
517
|
+
</div>`,n=>{let i=n.querySelector("#slm-ch-name"),a=n.querySelector("[data-ch-marker]");i.addEventListener("input",()=>{let l=J(i.value,t);a.textContent=l.letter,a.style.background=l.color}),n.querySelectorAll("[data-ch-create]").forEach(l=>{l.addEventListener("click",()=>{let r=l.dataset.chCreate==="allocate";this.createChannel(i.value,a.textContent??"",a.style.background,n.querySelector("#slm-ch-ref")?.value??"",r)})})})}async createChannel(e,t,s,n,i){let a=e.trim();if(!a){this.showDialogError("Give the channel a name your team will recognise.");return}try{let l=await this.host.api.createChannel(this.host.eventKey,{name:a,marker:O(t,"")||null,color:s||null,externalRef:n.trim()||null});this.closeDialog(),await this.refresh(),this.targetChannelId=l.channel.id,this.detailChannelId=i?null:l.channel.id,this.announce(`Channel ${a} created with 0 seats allocated.`),this.host.toast(i?`${a} created. Select seats on the map to allocate them.`:`${a} created.`,"ok"),this.paintRail()}catch(l){let r=l instanceof f?l.code:void 0;this.showDialogError(r==="channel_name_taken"?"That name is already used on this event. Pick another.":"Couldn't create the channel. Try again."),this.host.onError(l)}}showDialogError(e){let t=this.scrimEl?.querySelector("[data-ch-error]");t&&(t.textContent=e,t.hidden=!1)}renderReviewDialog(e){let{labels:t,buckets:s}=this.currentPlan(),n=e.applied,i=n?n.buckets:s,a=this.nameOf(n?.targetChannelId??this.targetChannelId)??T,l=ge(i,a),r=n?n.applied:K(s),d=!n&&t.length>0&&r===0,c=!n&&t.length>A,p=W(l),g=n?'<div class="foot"><button type="button" class="slm-btn" data-ch-close>Done</button></div>':`<div class="foot">
|
|
523
518
|
<button type="button" class="quiet" data-ch-close>Back</button>
|
|
524
519
|
<button type="button" class="slm-btn" data-ch-apply ${d||c||e.busy?"disabled":""}>
|
|
525
520
|
${e.busy?"Applying\u2026":`Apply ${r.toLocaleString()} change${r===1?"":"s"}`}
|
|
526
521
|
</button>
|
|
527
|
-
</div>`,w=!n&&be(s)?'<p class="slm-note">Applying moves inventory out of another private channel. That is the line marked above.</p>':"",
|
|
528
|
-
every seat is in a buyer's checkout, already sold, or already in ${h(
|
|
529
|
-
One Apply can cover at most ${
|
|
530
|
-
<h3 id="slm-ch-dlg-title">${n?`Moved ${n.applied.toLocaleString()} seat${n.applied===1?"":"s"} to ${h(
|
|
522
|
+
</div>`,w=!n&&be(s)?'<p class="slm-note">Applying moves inventory out of another private channel. That is the line marked above.</p>':"",b=d?`<div class="slm-ch-alert warn"><span>\u2139</span><span>Nothing in this selection can move right now \u2014
|
|
523
|
+
every seat is in a buyer's checkout, already sold, or already in ${h(a)}.</span></div>`:"",m=c?`<div class="slm-ch-alert warn" role="alert"><span>\u2139</span><span><b>Choose fewer seats.</b>
|
|
524
|
+
One Apply can cover at most ${A.toLocaleString()} seats.</span></div>`:"";this.renderScrim(`
|
|
525
|
+
<h3 id="slm-ch-dlg-title">${n?`Moved ${n.applied.toLocaleString()} seat${n.applied===1?"":"s"} to ${h(a)}`:`Move ${t.length.toLocaleString()} selected seat${t.length===1?"":"s"} to ${h(a)}`}</h3>
|
|
531
526
|
<p class="sub">${n?"These are the exact counts the server applied.":"Every selected seat is in exactly one line below."}</p>
|
|
527
|
+
${b}
|
|
532
528
|
${m}
|
|
533
|
-
${u}
|
|
534
529
|
${p||'<div class="slm-empty">Nothing selected.</div>'}
|
|
535
530
|
${w}
|
|
536
531
|
<p class="slm-ch-err" data-ch-error ${e.error?"":"hidden"}>${h(e.error??"")}</p>
|
|
537
|
-
${g}`,v=>{v.querySelector("[data-ch-apply]")?.addEventListener("click",()=>{this.apply()})}),n&&this.announce(`Applied ${n.applied} change${n.applied===1?"":"s"} to ${
|
|
532
|
+
${g}`,v=>{v.querySelector("[data-ch-apply]")?.addEventListener("click",()=>{this.apply()})}),n&&this.announce(`Applied ${n.applied} change${n.applied===1?"":"s"} to ${a}.`)}async apply(){if(!this.dialog||!this.caps.manage)return;let{labels:e}=this.currentPlan();if(e.length){if(e.length>A){this.showDialogError(`Choose at most ${A.toLocaleString()} seats for one Apply.`);return}this.dialog={...this.dialog,busy:!0,error:null},this.renderDialog();try{let t=await this.host.api.applyChannelAssignment(this.host.eventKey,{targetChannelId:B(this.targetChannelId)?null:this.targetChannelId,labels:e,assignmentVersion:this.assignmentVersion});this.assignmentVersion=t.assignmentVersion,this.conflict=!1,await this.refresh({quiet:!0}),this.closeDialog({restoreFocus:!1}),this.host.clearSelection(),this.showApplied(t)}catch(t){if(t instanceof f&&t.status===409&&t.code==="channel_assignment_conflict"){this.conflict=!0,this.closeDialog(),this.shakeStaged(),this.paintRail(),this.announce("Assignments changed while you were editing. Nothing was applied and your selection is kept.");return}if(t instanceof f&&t.status===403){this.caps={view:this.caps.view,manage:!1},this.closeDialog(),this.paintRail(),this.host.toast("Changing channels needs channel-management permission.","err");return}this.dialog={kind:"review",busy:!1,error:"Couldn't apply those changes. Try again."},this.renderDialog(),this.host.onError(t)}}}showApplied(e){let t=this.nameOf(e.targetChannelId)??T,s=e.buckets.skippedHeld.count+e.buckets.skippedBooked.count+e.buckets.notFound.count;this.setStaged(`<span class="slm-ch-tick" aria-hidden="true">\u2713</span>
|
|
538
533
|
<span>Assigned <b>${e.applied.toLocaleString()}</b> seat${e.applied===1?"":"s"} to ${h(t)}
|
|
539
534
|
${s?` \xB7 ${s.toLocaleString()} skipped`:""}</span>
|
|
540
535
|
<span class="grow"></span>
|
|
@@ -551,7 +546,7 @@
|
|
|
551
546
|
<p class="slm-note">Pausing stops new buyer access; checkouts already running can finish.
|
|
552
547
|
Archiving closes the channel for good and returns its free seats to a destination you choose.
|
|
553
548
|
Nothing is ever deleted silently.</p>
|
|
554
|
-
<div class="foot"><button type="button" class="quiet" data-ch-close>Cancel</button></div>`,n=>{n.querySelectorAll("[data-ch-menu-act]").forEach(i=>{i.addEventListener("click",()=>{let
|
|
549
|
+
<div class="foot"><button type="button" class="quiet" data-ch-close>Cancel</button></div>`,n=>{n.querySelectorAll("[data-ch-menu-act]").forEach(i=>{i.addEventListener("click",()=>{let a=i.dataset.chMenuAct;if(a==="open"){this.closeDialog(),this.openChannel(t.id);return}if(a==="rename"){this.openDialog({kind:"rename",channelId:t.id});return}if(a==="archive"){this.openDialog({kind:"archive",channelId:t.id});return}this.closeDialog(),this.togglePause(t.id)})})})}renderRenameDialog(e){let t=this.list?.channels.find(s=>s.id===e.channelId);if(!t){this.closeDialog();return}this.renderScrim(`
|
|
555
550
|
<h3 id="slm-ch-dlg-title">Rename ${h(t.name)}</h3>
|
|
556
551
|
<p class="sub">Only your team and your reports see this name.</p>
|
|
557
552
|
<div class="slm-field">
|
|
@@ -562,7 +557,7 @@
|
|
|
562
557
|
<div class="foot">
|
|
563
558
|
<button type="button" class="quiet" data-ch-close>Cancel</button>
|
|
564
559
|
<button type="button" class="slm-btn" data-ch-rename>Save name</button>
|
|
565
|
-
</div>`,s=>{s.querySelector("[data-ch-rename]")?.addEventListener("click",()=>{let n=s.querySelector("#slm-ch-newname")?.value??"";if(!n.trim()){this.showDialogError("A channel needs a name.");return}this.host.api.renameChannel(this.host.eventKey,t.id,n.trim()).then(()=>(this.closeDialog(),this.refresh())).catch(i=>{this.showDialogError(i instanceof f&&i.code==="channel_name_taken"?"That name is already used on this event.":"Couldn't rename the channel."),this.host.onError(i)})})})}async chooseWebsiteIntegration(){this.detailChannelId&&await this.setAccessIntent("server")}async setAccessIntent(e,t={}){let s=this.detailChannelId;if(!s||!this.caps.manage)return!1;try{let n=await this.host.api.setChannelAccessIntent(this.host.eventKey,s,e,t);return await this.refresh(),this.host.toast(this.intentSavedCopy(e,n?.intentSwitch),"ok"),!0}catch(n){return n instanceof f&&n.code==="channel_intent_switch_blocked"?(this.openDialog({kind:"intentSwitch",channelId:s,intentTo:e,switchBlocked:n.details}),!1):n instanceof f&&n.code==="channel_access_intent_forbids"?(this.host.toast(Y(n.details),"err"),!1):(this.host.toast("Couldn't change how this channel reaches buyers.","err"),this.host.onError(n),!1)}}intentSavedCopy(e,t){let s=e==="server"?"Set to your website or app. The embed code is on the Embed page.":e==="internal"?"Only your own staff can sell this channel now.":e==="hosted_link"?"Set to buyer links. Create one to let buyers in.":"Kept as a protected reserve. No route can sell these seats.";if(!t)return s;let n=t.closedLinks?` ${t.closedLinks.toLocaleString()} buyer link${t.closedLinks===1?"":"s"} closed.`:"",i=t.keptSessions?` ${t.keptSessions.toLocaleString()} buyer${t.keptSessions===1?"":"s"} already in a checkout can still finish.`:"";return`${s}${n}${i}`}async togglePause(e=this.detailChannelId){let t=this.list?.channels.find(n=>n.id===e);if(!t||!this.caps.manage)return;let s=t.state!=="paused";try{await this.host.api.setChannelPaused(this.host.eventKey,t.id,s),await this.refresh(),this.host.toast(s?`${t.name} paused. Existing checkouts can finish; no new buyer access is issued.`:`${t.name} resumed.`,"ok")}catch(n){this.host.toast("Couldn't change that channel.","err"),this.host.onError(n)}}renderArchiveDialog(e){let t=this.list?.channels.find(l=>l.id===e.channelId);if(!t){this.closeDialog();return}let s=e.archiveBlocked??null,n=!s&&t.counts.held>0,i=this.assignableChannels().filter(l=>l.id!==t.id),
|
|
560
|
+
</div>`,s=>{s.querySelector("[data-ch-rename]")?.addEventListener("click",()=>{let n=s.querySelector("#slm-ch-newname")?.value??"";if(!n.trim()){this.showDialogError("A channel needs a name.");return}this.host.api.renameChannel(this.host.eventKey,t.id,n.trim()).then(()=>(this.closeDialog(),this.refresh())).catch(i=>{this.showDialogError(i instanceof f&&i.code==="channel_name_taken"?"That name is already used on this event.":"Couldn't rename the channel."),this.host.onError(i)})})})}async chooseWebsiteIntegration(){this.detailChannelId&&await this.setAccessIntent("server")}async setAccessIntent(e,t={}){let s=this.detailChannelId;if(!s||!this.caps.manage)return!1;try{let n=await this.host.api.setChannelAccessIntent(this.host.eventKey,s,e,t);return await this.refresh(),this.host.toast(this.intentSavedCopy(e,n?.intentSwitch),"ok"),!0}catch(n){return n instanceof f&&n.code==="channel_intent_switch_blocked"?(this.openDialog({kind:"intentSwitch",channelId:s,intentTo:e,switchBlocked:n.details}),!1):n instanceof f&&n.code==="channel_access_intent_forbids"?(this.host.toast(Y(n.details),"err"),!1):(this.host.toast("Couldn't change how this channel reaches buyers.","err"),this.host.onError(n),!1)}}intentSavedCopy(e,t){let s=e==="server"?"Set to your website or app. The embed code is on the Embed page.":e==="internal"?"Only your own staff can sell this channel now.":e==="hosted_link"?"Set to buyer links. Create one to let buyers in.":"Kept as a protected reserve. No route can sell these seats.";if(!t)return s;let n=t.closedLinks?` ${t.closedLinks.toLocaleString()} buyer link${t.closedLinks===1?"":"s"} closed.`:"",i=t.keptSessions?` ${t.keptSessions.toLocaleString()} buyer${t.keptSessions===1?"":"s"} already in a checkout can still finish.`:"";return`${s}${n}${i}`}async togglePause(e=this.detailChannelId){let t=this.list?.channels.find(n=>n.id===e);if(!t||!this.caps.manage)return;let s=t.state!=="paused";try{await this.host.api.setChannelPaused(this.host.eventKey,t.id,s),await this.refresh(),this.host.toast(s?`${t.name} paused. Existing checkouts can finish; no new buyer access is issued.`:`${t.name} resumed.`,"ok")}catch(n){this.host.toast("Couldn't change that channel.","err"),this.host.onError(n)}}renderArchiveDialog(e){let t=this.list?.channels.find(l=>l.id===e.channelId);if(!t){this.closeDialog();return}let s=e.archiveBlocked??null,n=!s&&t.counts.held>0,i=this.assignableChannels().filter(l=>l.id!==t.id),a=s?`<div class="slm-ch-alert warn" role="alert"><span>\u23F3</span>
|
|
566
561
|
<span><b>${(s.heldUnits??s.activeHolds??0).toLocaleString()} seats are in a buyer's checkout right now.</b>
|
|
567
562
|
Archive is unavailable while seats are held \u2014 try again ${h(ve(s))}.</span></div>`:n?`<div class="slm-ch-alert warn"><span>\u23F3</span>
|
|
568
563
|
<span>${t.counts.held.toLocaleString()} seats are in a buyer's checkout right now.
|
|
@@ -570,7 +565,7 @@
|
|
|
570
565
|
<h3 id="slm-ch-dlg-title">Archive ${h(t.name)}</h3>
|
|
571
566
|
<p class="sub">The channel closes for good. Its seats move to a destination you choose;
|
|
572
567
|
sales history keeps its attribution.</p>
|
|
573
|
-
${
|
|
568
|
+
${a}
|
|
574
569
|
<div class="slm-field">
|
|
575
570
|
<label for="slm-ch-dest">Move the ${t.counts.free.toLocaleString()} remaining free seats to</label>
|
|
576
571
|
<select class="slm-select" id="slm-ch-dest">
|
|
@@ -584,12 +579,12 @@
|
|
|
584
579
|
<div class="foot">
|
|
585
580
|
<button type="button" class="quiet" data-ch-close>Cancel</button>
|
|
586
581
|
<button type="button" class="slm-btn danger" data-ch-archive ${s?"disabled":""}>Archive channel</button>
|
|
587
|
-
</div>`,l=>{l.querySelector("[data-ch-archive]")?.addEventListener("click",()=>{let r=l.querySelector("#slm-ch-dest")?.value??"";this.archive(t.id,r||null)})})}async archive(e,t){try{await this.host.api.archiveChannel(this.host.eventKey,e,t),this.closeDialog(),this.detailChannelId=null,await this.refresh(),this.host.toast("Channel archived. Its remaining seats moved to the destination you chose.","ok")}catch(s){if(s instanceof f&&s.status===409&&s.code==="channel_archive_blocked_by_holds"){this.dialog={kind:"archive",channelId:e,archiveBlocked:s.details??{}},this.renderDialog();return}this.showDialogError("Couldn't archive that channel. Try again."),this.host.onError(s)}}renderSeatListDialog(){let e=new Set(this.host.selectionLabels()),t=new Map,s=0;for(let i of this.host.seats()){if(s+=1,s>this.seatListLimit)break;let
|
|
582
|
+
</div>`,l=>{l.querySelector("[data-ch-archive]")?.addEventListener("click",()=>{let r=l.querySelector("#slm-ch-dest")?.value??"";this.archive(t.id,r||null)})})}async archive(e,t){try{await this.host.api.archiveChannel(this.host.eventKey,e,t),this.closeDialog(),this.detailChannelId=null,await this.refresh(),this.host.toast("Channel archived. Its remaining seats moved to the destination you chose.","ok")}catch(s){if(s instanceof f&&s.status===409&&s.code==="channel_archive_blocked_by_holds"){this.dialog={kind:"archive",channelId:e,archiveBlocked:s.details??{}},this.renderDialog();return}this.showDialogError("Couldn't archive that channel. Try again."),this.host.onError(s)}}renderSeatListDialog(){let e=new Set(this.host.selectionLabels()),t=new Map,s=0;for(let i of this.host.seats()){if(s+=1,s>this.seatListLimit)break;let a=this.host.sectionOfLabel(i.label),l=a?.id??"",r=t.get(l)??{label:a?.label??"Other seats",seats:[]};r.seats.push({label:i.label,status:this.host.statusOf(i.label)??"free"}),t.set(l,r)}let n=[...t.entries()].map(([i,a])=>`
|
|
588
583
|
<div class="slm-ch-seatgroup">
|
|
589
|
-
<span>${h(
|
|
584
|
+
<span>${h(a.label)}</span>
|
|
590
585
|
${i?`<button type="button" data-ch-section="${h(i)}">Select section</button>`:""}
|
|
591
586
|
</div>
|
|
592
|
-
${
|
|
587
|
+
${a.seats.map(l=>{let r=this.allocation.get(l.label)??C,d=this.nameOf(r)??T;return`<button type="button" class="slm-ch-seatitem" role="checkbox"
|
|
593
588
|
aria-checked="${e.has(l.label)}" data-ch-seat="${h(l.label)}">
|
|
594
589
|
<span class="box" aria-hidden="true">\u2713</span>
|
|
595
590
|
<span>${h(l.label)}</span>
|
|
@@ -599,7 +594,7 @@
|
|
|
599
594
|
<p class="sub">The same selection as the map, with checkboxes. Space or Enter toggles a seat.</p>
|
|
600
595
|
<div class="slm-ch-seatlist">${n||'<div class="slm-empty">No seats on this chart.</div>'}</div>
|
|
601
596
|
${s>this.seatListLimit?'<div class="foot"><button type="button" class="slm-btn ghost" data-ch-more>Show more seats</button></div>':""}
|
|
602
|
-
<div class="foot"><button type="button" class="slm-btn" data-ch-close>Done</button></div>`,i=>{i.querySelectorAll("[data-ch-seat]").forEach(
|
|
597
|
+
<div class="foot"><button type="button" class="slm-btn" data-ch-close>Done</button></div>`,i=>{i.querySelectorAll("[data-ch-seat]").forEach(a=>{a.addEventListener("click",()=>{let l=a.dataset.chSeat,r=new Set(this.host.selectionLabels());r.has(l)?r.delete(l):r.add(l),this.host.clearSelection(),r.size&&this.host.selectByLabels([...r]),this.renderSeatListDialog()})}),i.querySelectorAll("[data-ch-section]").forEach(a=>{a.addEventListener("click",()=>{this.host.selectSection(a.dataset.chSection),this.renderSeatListDialog()})}),i.querySelector("[data-ch-more]")?.addEventListener("click",()=>{this.seatListLimit+=ht,this.renderSeatListDialog()})})}async reloadLinks(){let e=this.detailChannelId;e&&(this.linksState=this.links.length?this.linksState:"loading",await this.loadLinks(e),this.paintRail())}async reloadAfterLinkChange(e){if(this.detailChannelId===e){await this.refresh({quiet:!0});return}await this.loadLinks(e),this.active&&this.paintRail()}linkById(e){return this.links.find(t=>t.id===e)??null}renderLinkCreateDialog(e){let t=this.list?.channels.find(s=>s.id===e.channelId);if(!t||!this.caps.manage){this.closeDialog();return}this.renderScrim(`
|
|
603
598
|
<h3 id="slm-ch-dlg-title">Create a buyer link for ${h(t.name)}</h3>
|
|
604
599
|
<p class="sub">Anyone who opens the link can buy from this channel's allocation \u2014 and only from it.
|
|
605
600
|
You'll see the link once, right after you create it.</p>
|
|
@@ -638,9 +633,9 @@
|
|
|
638
633
|
<div class="foot">
|
|
639
634
|
<button type="button" class="quiet" data-ch-close>Cancel</button>
|
|
640
635
|
<button type="button" class="slm-btn" data-ch-lk-create>Create link</button>
|
|
641
|
-
</div>`,s=>{let n=s.querySelector("[data-ch-lk-expiry]"),i=s.querySelector("[data-ch-lk-when-field]"),
|
|
642
|
-
<b>The old link has stopped working.</b> ${e.endedSessions?`${e.endedSessions.toLocaleString()} buyer${e.endedSessions===1?"":"s"} lost access immediately.`:"Buyers who already came in can finish; every new visit needs this link."}</span></div>`:"",i=se(e.link).map(
|
|
643
|
-
<span class="v">${h(
|
|
636
|
+
</div>`,s=>{let n=s.querySelector("[data-ch-lk-expiry]"),i=s.querySelector("[data-ch-lk-when-field]"),a=s.querySelector("#slm-ch-lk-when");n.addEventListener("change",()=>{let l=n.value==="custom";i.hidden=!l,l&&!a.value&&(a.value=ot(Date.now()+7*864e5))}),s.querySelector("[data-ch-lk-create]")?.addEventListener("click",()=>{let l=De(s,"#slm-ch-lk-redemptions"),r=De(s,"#slm-ch-lk-quantity");if(l==null||r==null){this.showDialogError("Those two settings need to be whole numbers.");return}let d;if(n.value==="custom"&&(d=Date.parse(a.value),!Number.isFinite(d))){this.showDialogError("Pick the date and time the link should stop working.");return}this.createLink(t.id,{label:s.querySelector("#slm-ch-lk-label")?.value.trim()||null,includePublic:s.querySelector("#slm-ch-lk-public")?.checked??!1,...d===void 0?{}:{expiresAt:d},maxRedemptions:l,maxQuantity:r})})})}async createLink(e,t){await this.ensureHostedLinkRoute(e,t)&&await this.mintLink(e,t)}async mintLink(e,t){try{let s=await this.host.api.createAccessLink(this.host.eventKey,e,t);this.revealLink(s,{channelId:e}),await this.reloadAfterLinkChange(e)}catch(s){this.showDialogError(_(s instanceof f?s:void 0)),s instanceof f||this.host.onError(s)}}async ensureHostedLinkRoute(e,t){if((this.list?.channels.find(n=>n.id===e)?.access?.intent??"none")==="hosted_link")return!0;try{return await this.host.api.setChannelAccessIntent(this.host.eventKey,e,"hosted_link"),await this.refresh(),!0}catch(n){return n instanceof f&&n.code==="channel_intent_switch_blocked"?(this.openDialog({kind:"intentSwitch",channelId:e,intentTo:"hosted_link",switchBlocked:n.details,pendingLink:t}),!1):(this.showDialogError(n instanceof f&&n.code==="channel_access_intent_forbids"?Y(n.details):"Couldn't set this channel up for buyer links. Try again."),n instanceof f||this.host.onError(n),!1)}}revealLink(e,t){let s=e.url;this.dialog=null;let n=t.rotated?`<div class="slm-ch-alert warn" role="status"><span>\u26A0</span><span>
|
|
637
|
+
<b>The old link has stopped working.</b> ${e.endedSessions?`${e.endedSessions.toLocaleString()} buyer${e.endedSessions===1?"":"s"} lost access immediately.`:"Buyers who already came in can finish; every new visit needs this link."}</span></div>`:"",i=se(e.link).map(a=>`<div class="slm-ch-lkrow"><span class="k">${h(a.k)}</span>
|
|
638
|
+
<span class="v">${h(a.v)}</span></div>`).join("");this.renderScrim(`
|
|
644
639
|
<h3 id="slm-ch-dlg-title">Copy this link now</h3>
|
|
645
640
|
<p class="sub">This is the only time SeatLayer can show it. We keep just a fingerprint, so it cannot be
|
|
646
641
|
shown again \u2014 if it is lost, rotate the link for a fresh one.</p>
|
|
@@ -656,7 +651,7 @@
|
|
|
656
651
|
${i}
|
|
657
652
|
<div class="foot">
|
|
658
653
|
<button type="button" class="slm-btn" data-ch-close data-ch-lk-done>I've copied it</button>
|
|
659
|
-
</div>`,
|
|
654
|
+
</div>`,a=>{let l=a.querySelector("[data-ch-lk-copy]");l?.addEventListener("click",()=>{let r=()=>{l.textContent="Copied",this.announce("Buyer link copied.")},d=typeof navigator>"u"?null:navigator.clipboard;if(d?.writeText){d.writeText(s).then(r,()=>Oe(a));return}Oe(a)})}),this.announce("Your buyer link is ready and is shown once.")}renderLinkRotateDialog(e){let t=this.linkById(e.linkId);if(!t||!this.caps.manage){this.closeDialog();return}let s=t.activeSessions??0,n=s?`<div class="slm-ch-alert warn"><span>\u26A0</span>
|
|
660
655
|
<span><b>${s.toLocaleString()} buyer${s===1?"":"s"}</b> got in with the current link
|
|
661
656
|
and still ${s===1?"has":"have"} active access.</span></div>`:"";this.renderScrim(`
|
|
662
657
|
<h3 id="slm-ch-dlg-title">Rotate the ${h(t.label||"buyer")} link?</h3>
|
|
@@ -678,7 +673,7 @@
|
|
|
678
673
|
<div class="foot">
|
|
679
674
|
<button type="button" class="quiet" data-ch-close>Cancel</button>
|
|
680
675
|
<button type="button" class="slm-btn" data-ch-lk-rotate disabled>Rotate and copy new link</button>
|
|
681
|
-
</div>`,i=>{let
|
|
676
|
+
</div>`,i=>{let a=i.querySelector("[data-ch-lk-rotate]");i.querySelectorAll("[data-ch-rot]").forEach(l=>{l.addEventListener("change",()=>{a.disabled=!1})}),a.addEventListener("click",()=>{let l=[...i.querySelectorAll("[data-ch-rot]")].find(r=>r.checked);if(!l){this.showDialogError(_({code:"end_active_sessions_required"}));return}this.rotateLink(e.channelId,t.id,l.value==="end")})})}async rotateLink(e,t,s){try{let n=await this.host.api.rotateAccessLink(this.host.eventKey,e,t,s);this.revealLink(n,{channelId:e,rotated:!0}),await this.reloadAfterLinkChange(e)}catch(n){this.showDialogError(_(n instanceof f?n:void 0)),n instanceof f||this.host.onError(n)}}renderLinkRevokeDialog(e){let t=this.linkById(e.linkId);if(!t||!this.caps.manage){this.closeDialog();return}let s=t.activeSessions??0;this.renderScrim(`
|
|
682
677
|
<h3 id="slm-ch-dlg-title">Revoke the ${h(t.label||"buyer")} link?</h3>
|
|
683
678
|
<p class="sub">It stops opening immediately and cannot be restored \u2014 there is no undo, and no way to
|
|
684
679
|
bring the same URL back. Seats already bought through it keep their sale.</p>
|
|
@@ -692,7 +687,7 @@
|
|
|
692
687
|
<div class="foot">
|
|
693
688
|
<button type="button" class="quiet" data-ch-close>Cancel</button>
|
|
694
689
|
<button type="button" class="slm-btn danger" data-ch-lk-revoke>Revoke link</button>
|
|
695
|
-
</div>`,n=>{n.querySelector("[data-ch-lk-revoke]")?.addEventListener("click",()=>{let i=n.querySelector("[data-ch-lk-endsessions]")?.checked??!1;this.revokeLink(e.channelId,t.id,i)})})}async revokeLink(e,t,s){try{let n=await this.host.api.revokeAccessLink(this.host.eventKey,e,t,s);this.closeDialog(),await this.reloadAfterLinkChange(e),this.host.toast(n.endedSessions?`Link revoked. ${n.endedSessions.toLocaleString()} buyer${n.endedSessions===1?"":"s"} lost access.`:"Link revoked. It no longer opens for anyone.","ok")}catch(n){this.showDialogError(
|
|
690
|
+
</div>`,n=>{n.querySelector("[data-ch-lk-revoke]")?.addEventListener("click",()=>{let i=n.querySelector("[data-ch-lk-endsessions]")?.checked??!1;this.revokeLink(e.channelId,t.id,i)})})}async revokeLink(e,t,s){try{let n=await this.host.api.revokeAccessLink(this.host.eventKey,e,t,s);this.closeDialog(),await this.reloadAfterLinkChange(e),this.host.toast(n.endedSessions?`Link revoked. ${n.endedSessions.toLocaleString()} buyer${n.endedSessions===1?"":"s"} lost access.`:"Link revoked. It no longer opens for anyone.","ok")}catch(n){this.showDialogError(_(n instanceof f?n:void 0)),n instanceof f||this.host.onError(n)}}applySheetClasses(){let e=this.host.root,t=this.host.isCompact();e.classList.toggle("ch-sheet",t&&this.active);for(let s of["collapsed","medium","full"])e.classList.toggle(`detent-${s}`,t&&this.active&&this.detent===s);this.host.setMapInert(t&&this.active&&this.detent==="full")}cycleDetent(){let e=["collapsed","medium","full"];this.detent=e[(e.indexOf(this.detent)+1)%e.length],this.applySheetClasses()}handleBack(){return this.scrimEl?(this.closeDialog(),!0):this.host.isCompact()&&this.detent==="full"?(this.detent="medium",this.applySheetClasses(),!0):!1}}});var Kt={};qe(Kt,{ACCESS_LINK_DEFAULTS:()=>ee,ChannelsMode:()=>ne,ManageApi:()=>U,ManageApiError:()=>f,PUBLIC_CHANNEL_ID:()=>C,PUBLIC_CHANNEL_NAME:()=>T,SeatManager:()=>Re,accessIntentDescription:()=>ye,accessIntentLabel:()=>z,accessLine:()=>fe,accessLinkBadge:()=>we,accessLinkErrorCopy:()=>_,accessLinkIsLive:()=>te,accessLinkPolicyLines:()=>se,bucketRows:()=>ge,bucketRowsHtml:()=>W,dropReviewRows:()=>it,intentForbidsCopy:()=>Y,intentSwitchBlockedCopy:()=>ke,isPublicChannelId:()=>B,markerLetter:()=>O,markerOf:()=>pe,mutationCount:()=>K,needsMoveConfirmation:()=>be,planAssignment:()=>me,retryAfterCopy:()=>ve,selectionSources:()=>ue,stateBadge:()=>Se,suggestMarker:()=>J});module.exports=St(Kt);var L=require("@seatlayer/core");ae();var Rt=/^[a-zA-Z0-9._-]+$/;function $t(o){let e;try{e=new URL(o,"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]),n=decodeURIComponent(t[2]);return!s||!Rt.test(n)?null:{eventKey:s,asset:n}}catch{return null}}function Tt(o){try{return/^\/v1\/events\/[^/]+\/assets(?:\/|$)/.test(new URL(o,"https://seatlayer.invalid").pathname)}catch{return!1}}var oe=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=$t(e);if(!t)return Promise.resolve(Tt(e)?null:e);if(t.eventKey!==this.eventKey||this.disposed)return Promise.resolve(null);let s=`${t.eventKey}/${t.asset}`,n=this.pending.get(s);if(n)return n;let i=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,i),i}async prepareRendererChart(e){let t=async i=>{if(!i?.url)return;let a=await this.resolve(i.url);if(!a)throw new Error("organizer_event_asset_scope_mismatch");i.url=a},s=async i=>{for(let a of i){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}},n=async i=>{await t(i.backgroundImage),await s(i.objects)};await n(e);for(let i of e.floors??[])await n(i);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 Ne="seatlayer-manager-style",Ue="seatlayer-manager-channels-style",At=`
|
|
696
691
|
/* The floor was 480px, which trapped the cockpit in any host shorter than that:
|
|
697
692
|
every ancestor here is overflow:hidden, so the bottom of the rail (Create
|
|
698
693
|
channel, Show archived) was rendered but clipped away by the host and could
|
|
@@ -727,6 +722,7 @@
|
|
|
727
722
|
.slm-bar{display:grid;grid-template-columns:auto auto auto minmax(0,1fr);align-items:center;column-gap:14px;row-gap:10px;
|
|
728
723
|
padding:10px 16px;border-bottom:1px solid var(--slm-line);flex:none}
|
|
729
724
|
.slm-modes{display:inline-flex;background:var(--slm-surface);border:1px solid var(--slm-line);border-radius:999px;padding:3px}
|
|
725
|
+
.slm-tools{display:none}
|
|
730
726
|
.slm-mode{padding:6px 16px;border-radius:999px;font-weight:700;font-size:13px;color:var(--slm-muted)}
|
|
731
727
|
.slm-mode.on{background:var(--slm-accent);color:var(--slm-accent-ink)}
|
|
732
728
|
.slm-live{display:inline-flex;align-items:center;gap:6px;font-size:11px;letter-spacing:.12em;font-weight:800;color:var(--slm-muted)}
|
|
@@ -923,13 +919,26 @@
|
|
|
923
919
|
.slm-inspect-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:16px 20px;margin-top:18px}
|
|
924
920
|
.slm-inspect-grid>div{min-width:0}.slm-inspect-grid span{display:block;color:var(--slm-muted);font-size:10px;
|
|
925
921
|
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}
|
|
922
|
+
.slm-categoryassign{display:flex;flex-direction:column;gap:8px}
|
|
923
|
+
.slm-categoryrow{display:grid;grid-template-columns:auto minmax(0,1fr) auto auto;align-items:center;gap:8px;
|
|
924
|
+
padding:9px;border:1px solid var(--slm-line);border-radius:10px;background:var(--slm-surface)}
|
|
925
|
+
.slm-categoryrow>.dot{width:10px;height:10px;border-radius:50%}
|
|
926
|
+
.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}
|
|
927
|
+
.slm-categorycopy small{font-size:10px;color:var(--slm-muted)}.slm-categoryrow .slm-btn{width:auto;padding:6px 9px}
|
|
928
|
+
.slm-tablelist{display:flex;flex-direction:column;gap:8px;margin-top:12px}
|
|
929
|
+
.slm-tablerow{display:grid;grid-template-columns:minmax(0,1fr) minmax(140px,auto);align-items:center;gap:10px;
|
|
930
|
+
padding:10px;border:1px solid var(--slm-line);border-radius:10px;background:var(--slm-surface)}
|
|
931
|
+
.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}
|
|
932
|
+
.slm-tablecopy small{font-size:10px;color:var(--slm-muted)}.slm-tablerow .slm-select{min-width:140px}
|
|
926
933
|
.slm:fullscreen{border-radius:0;min-height:100vh;background:var(--slm-bg)}
|
|
927
934
|
.slm:fullscreen .slm-bar{padding:14px 22px}.slm:fullscreen .slm-kpi b{font-size:21px}.slm:fullscreen .slm-rail{width:360px}
|
|
928
935
|
|
|
929
936
|
.slm.compact .slm-rail{width:100%;border-left:0;border-top:1px solid var(--slm-line);height:44%}
|
|
930
937
|
.slm.compact .slm-body{flex-direction:column}
|
|
931
938
|
.slm.compact .slm-bar{grid-template-columns:minmax(0,1fr) auto;gap:8px;padding:8px}
|
|
932
|
-
.slm.compact .slm-modes{
|
|
939
|
+
.slm.compact .slm-modes{display:none}
|
|
940
|
+
.slm.compact .slm-tools{display:block;width:100%;padding:9px 13px;min-height:44px;border:1px solid var(--slm-line);
|
|
941
|
+
border-radius:10px;background:var(--slm-surface);color:var(--slm-text);font-size:13px;font-weight:800}
|
|
933
942
|
.slm.compact .slm-live{justify-self:end}.slm.compact .slm-bar-actions{grid-column:1/-1;justify-self:stretch}
|
|
934
943
|
.slm.compact .slm-barbtn{flex:1;padding:6px 9px}.slm.compact .slm-kpis{grid-template-columns:repeat(4,minmax(0,1fr));gap:8px}
|
|
935
944
|
.slm.compact .slm-kpi[data-kpi="viewing-map"],.slm.compact .slm-kpi[data-kpi="active-holds"],
|
|
@@ -951,37 +960,41 @@
|
|
|
951
960
|
transition:none!important;
|
|
952
961
|
scroll-behavior:auto!important}
|
|
953
962
|
}
|
|
954
|
-
`;function Ke(){if(typeof document>"u"||document.getElementById(
|
|
955
|
-
<select class="slm-select slm-availmode${t!=="open"?" on":""}" data-avail-id="${
|
|
963
|
+
`;function Ke(){if(typeof document>"u"||document.getElementById(Ne))return;let o=document.createElement("style");o.id=Ne,o.textContent=At,document.head.appendChild(o)}function Ye(o){if(typeof document>"u"||document.getElementById(Ue))return;let e=document.createElement("style");e.id=Ue,e.textContent=o,document.head.appendChild(e)}var We=require("@seatlayer/core");function Ge(o){return o?o.mode:"open"}function Xe(o,e,t){switch(o){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 Qe(o){return new Date(o-new Date().getTimezoneOffset()*6e4).toISOString().slice(0,16)}function Ze(o){if(typeof o=="string"){let e=document.querySelector(o);if(!e)throw new Error(`seatmanager: container "${o}" not found`);return e}if(!(o instanceof HTMLElement))throw new Error("seatmanager: container must be a CSS selector or an HTMLElement");return o}function Ae(o){if(!o.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 le(o){return o==="blocked"?"not_for_sale":o}var Je=[{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 Ee(o){let e=o??{};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 Me(o,e){let t=Math.max(0,Math.round((e-o)/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 Z(o,e){return(0,We.formatMoney)(o,e,{maximumFractionDigits:0,fallback:(t,s)=>`${s} ${Math.round(t).toLocaleString()}`})}function u(o){return String(o??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}var et=require("@seatlayer/core");function Ie(o){return Object.entries(o).filter(([,e])=>e.mode==="closed").map(([e])=>e)}function re(o){let e=o.sectionsBase;if(!e)return{rows:[],hiddenSections:0,closedSections:0};let t=o.doc?.zones??[],s=new Map,n=[];for(let d of e.sections)if(d.zone&&t.some(c=>c.id===d.zone)){let c=s.get(d.zone)??[];c.push(d),s.set(d.zone,c)}else n.push(d);let i=[],a=0,l=0,r=(d,c,p,g=!1)=>{let w=o.availabilityRules[c.id]??null,b=o.effectiveClosed.has(c.id)||g,m=o.effectiveHidden.has(c.id)||p&&!b;d==="section"&&m&&(a+=1),d==="section"&&b&&(l+=1),i.push({kind:d,id:c.id,label:c.label,seatCount:c.seatCount,seatLabels:c.seatLabels,rule:w,hidden:m,closed:b,followsZone:d==="section"&&p})};for(let d of t){let c=s.get(d.id);if(!c||!c.length)continue;let p={id:d.id,label:d.label||"Zone",seatCount:c.reduce((b,m)=>b+m.seatCount,0),seatLabels:c.flatMap(b=>b.seatLabels)},g=!!o.availabilityRules[d.id],w=o.availabilityRules[d.id]?.mode==="closed";r("zone",p,!1);for(let b of c)r("section",b,g,w)}for(let d of n)r("section",d,!1);if(e.ungrouped){let d=e.ungrouped;r("section",{id:et.UNGROUPED_ID,label:d.label,seatCount:d.seatCount,seatLabels:d.seatLabels},!1)}return{rows:i,hiddenSections:a,closedSections:l}}function tt(o,e){let t=Ge(o.rule),s=`slm-availrow${o.kind==="zone"?" zone":""}${o.hidden?" hidden":""}${o.closed?" closed":""}`,n=e?" disabled":"",i=(c,p)=>`<option value="${c}"${t===c?" selected":""}>${p}</option>`,a=o.followsZone?'<span class="slm-availfollows">Follows zone</span>':`<span class="slm-availselwrap">
|
|
964
|
+
<select class="slm-select slm-availmode${t!=="open"?" on":""}" data-avail-id="${u(o.id)}"${n} aria-label="Availability for ${u(o.label)}">
|
|
956
965
|
${i("open","Open \u2014 on sale")}
|
|
957
966
|
${i("closed","Closed \u2014 visible, not on sale")}
|
|
958
967
|
${i("hidden","Hidden \u2014 off the buyer map")}
|
|
959
968
|
${i("timed","Reveal at a time")}
|
|
960
969
|
${i("threshold","Auto-reveal at % sold")}
|
|
961
970
|
</select>
|
|
962
|
-
</span>`,l="";if(!
|
|
963
|
-
<input type="datetime-local" class="slm-input" data-avail-reveal="${
|
|
964
|
-
</div>`}else if(!
|
|
971
|
+
</span>`,l="";if(!o.followsZone&&t==="timed"){let c=o.rule?.revealAt?u(Qe(o.rule.revealAt)):"";l=`<div class="slm-availdetail">
|
|
972
|
+
<input type="datetime-local" class="slm-input" data-avail-reveal="${u(o.id)}" value="${c}"${n} aria-label="Reveal time for ${u(o.label)}" />
|
|
973
|
+
</div>`}else if(!o.followsZone&&t==="threshold"){let c=o.rule?.thresholdPct??80;l=`<div class="slm-availdetail">
|
|
965
974
|
<span class="slm-availpctlabel">Reveal at</span>
|
|
966
|
-
<input type="number" min="1" max="100" class="slm-input slm-availpct" data-avail-pct="${
|
|
975
|
+
<input type="number" min="1" max="100" class="slm-input slm-availpct" data-avail-pct="${u(o.id)}" value="${u(c)}"${n} aria-label="Percent sold to reveal ${u(o.label)}" />
|
|
967
976
|
<span class="slm-availpctlabel">% sold</span>
|
|
968
|
-
</div>`}let r=
|
|
977
|
+
</div>`}let r=o.closed?'<span class="slm-availbadge closed">Closed</span>':o.hidden?'<span class="slm-availbadge hidden">Hidden</span>':"",d=o.kind==="zone"?`<span class="slm-availcaret" aria-hidden="true">${o.hidden?"\u25B8":"\u25BE"}</span>`:"";return`<div class="${s}">
|
|
969
978
|
<div class="slm-availhead">
|
|
970
|
-
<span class="slm-availlabel">${d}${
|
|
979
|
+
<span class="slm-availlabel">${d}${u(o.label)}</span>
|
|
971
980
|
${r}
|
|
972
|
-
<span class="slm-availcount">${
|
|
973
|
-
${
|
|
981
|
+
<span class="slm-availcount">${o.seatCount.toLocaleString()}</span>
|
|
982
|
+
${a}
|
|
974
983
|
</div>
|
|
975
984
|
${l}
|
|
976
|
-
</div>`}var Vt="https://api.seatlayer.io",Ce=80,jt=16,pt=4,Re=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 n=t?.closest("[data-feed-id]");n?.dataset.feedId&&this.locateActivity(n.dataset.feedId)};this.sectionOptions=[];Te(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 U(e.apiBase??Vt,e.token),this.organizerAssetUrls=new oe(this.key,(t,s)=>this.withAuthRetry(()=>this.api.asset(t,s))),this.host=Qe(e.container)}async render(){Ke(),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 n=new Set(t);s={view:n.has("event:channels:view"),manage:n.has("event:channels:view")&&n.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 Promise.resolve().then(()=>(Oe(),ht));if(this.closed||!this.channelCaps.view||(this.channels||(Ye(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 n=e.get(s.rowId);if(!n||n.type!=="row"&&n.type!=="table")continue;let i=s.logicalRowId??s.rowId,o=this.sectionByObject.get(s.rowId)??C.UNGROUPED_ID,l=t.get(i)??{id:i,label:n.type==="row"?n.segmentedRow?.displayLabel??n.displayLabel??n.label:n.displayLabel??n.label,sectionId:o,sectionLabel:this.sectionLabelById.get(o)??"Other seats",labels:[]};l.labels.push(s.label),t.set(i,l)}return[...t.values()].sort((s,n)=>s.sectionLabel.localeCompare(n.sectionLabel,void 0,{numeric:!0,sensitivity:"base"})||s.label.localeCompare(n.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)??C.UNGROUPED_ID;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(Ie(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){Te(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(),n=Math.min(12e4,Math.max(3e4,s*.2)),i=Math.max(0,s-n);this.tokenRefreshTimer=setTimeout(()=>{this.tokenRefreshTimer=null,this.rotateToken()},i)}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(i=>this.status.get(i)==="free");if(!s.length)return;let n=t.releaseAt??this.releaseAt??void 0;this.setSeatsLocal(s,"blocked");try{await this.api.block(this.key,s,{...t,releaseAt:n}),this.clearSelection(),this.done("block",s,n?`Blocked ${s.length} \u2014 auto-release ${new Date(n).toLocaleString()}.`:`Blocked ${s.length} seat${s.length===1?"":"s"}.`)}catch(i){this.setSeatsLocal(s,"free"),this.toastErr(i instanceof f&&i.status===409?"Some seats were just taken. Try again.":"Couldn't block those seats."),this.opts.onError?.(i)}}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(n=>this.status.get(n)==="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(n){this.setSeatsLocal(s,"booked"),this.toastErr("Couldn't release that booked inventory. Check the booking reference."),this.opts.onError?.(n)}}}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 C.SeatmapRenderer(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(0,C.expandChart)(e))this.labelToId.set(t.label,t.id),this.labelToSeat.set(t.label,t),this.allIds.push(t.id);for(let t of(0,C.gaAreasOf)(e))for(let s of(0,C.gaUnitLabels)(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,n]of this.status.entries()){let i=this.labelToId.get(s);i&&t[le(n)].push(i)}["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 n=[],i=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 c=this.labelToId.get(l.label);c&&(this.renderer?.setStatus([c],le(r)),n.push(c));let p=this.verbFor(d,r),g=`${p}:${r}`,w=i.get(g)??{labels:[],verb:p,status:r};w.labels.push(l.label),i.set(g,w)}for(let l of i.values()){let r=this.pushActivity(l.labels,l.verb,l.status);r&&this.paintSpatialActivity(r)}n.length&&(this.lastSyncedAt=Date.now(),this.afterPaint());let o=typeof s.bookedValue?.gross=="number"?s.bookedValue.gross:s.revenue?.gross;typeof o=="number"&&Number.isFinite(o)&&this.applyLiveGross(o),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=i=>["free","held","booked","blocked"].includes(i)?i:"free",n=new Map;if(t!==void 0){let i=s(t);for(let o of this.knownLabels())n.set(o,i)}for(let[i,o]of Object.entries(e))n.set(i,s(o));this.status=n,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 n of e){this.setStatusLabel(n,t);let i=this.labelToId.get(n);i&&s.push(i)}s.length&&this.renderer?.setStatus(s,le(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 n of e){let i=this.labelToSeat.get(n);if(!i)continue;let o=this.sectionByObject.get(i.rowId);o&&o!==C.UNGROUPED_ID&&t.add(o)}let s=[...t];return{ids:s,labels:s.map(n=>this.sectionLabelById.get(n)??n)}}pulseSeatLabels(e,t){let s=this.activityColor(t);for(let n of e.slice(0,jt)){let i=this.labelToId.get(n);i&&this.renderer?.flashSeat(i,s)}}paintSpatialActivity(e){let t=e.sectionIds??this.sectionsForLabels(e.labels).ids,s=this.renderer?.getFocusedSection()??null,n=this.followLive&&t.length===1&&(e.status==="held"||e.status==="booked");if(n&&s===t[0]){this.pulseSeatLabels(e.labels,e.status);return}if(n){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 i=this.activityColor(e.status);for(let o of t.slice(0,pt))this.renderer?.flashSection(o,i);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(n=>n.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 n=this.activityColor(t.status);for(let i of s.slice(0,pt))this.renderer?.flashSection(i,n)}else this.pulseSeatLabels(t.labels,t.status)},280)}showLiveEvent(e){let t=this.els.liveevent;if(!t)return;let s=e.sectionLabels??[],n=s.length===1?s[0]:s.length>1?`${s.length} sections`:e.label,i=e.count===1?"seat":"seats";t.innerHTML=`<span class="slm-liveeventdot" style="background:${this.activityColor(e.status)}"></span>
|
|
977
|
-
<span class="slm-liveeventcopy">${
|
|
978
|
-
<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 n=Number.isFinite(s.bookedValue)?s.bookedValue:s.bookedRevenue;return t+(Number.isFinite(n)?n: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),n=s.bookedValue??s.revenue??{gross:0,bySection:[]},i=(n.bySection??[]).map(r=>{let d=Number.isFinite(r.bookedValue)?r.bookedValue:r.bookedRevenue;return{...r,bookedValue:d??0,bookedRevenue:d??0}}),
|
|
985
|
+
</div>`}var Nt="https://api.seatlayer.io",Ce=80,Ut=16,ut=4,Re=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 n=t?.closest("[data-feed-id]");n?.dataset.feedId&&this.locateActivity(n.dataset.feedId)};this.sectionOptions=[];Ae(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 U(e.apiBase??Nt,e.token),this.organizerAssetUrls=new oe(this.key,(t,s)=>this.withAuthRetry(()=>this.api.asset(t,s))),this.host=Ze(e.container)}assertSelectionOptions(e,t){let s=n=>n===void 0||Number.isInteger(n)&&n>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(){Ke(),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",n=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(),n&&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 n=new Set(t);s={view:n.has("event:channels:view"),manage:n.has("event:channels:view")&&n.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 Promise.resolve().then(()=>(ze(),pt));if(this.closed||!this.channelCaps.view||(this.channels||(Ye(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 n=e.get(s.rowId);if(!n||n.type!=="row"&&n.type!=="table")continue;let i=s.logicalRowId??s.rowId,a=this.sectionByObject.get(s.rowId)??L.UNGROUPED_ID,l=t.get(i)??{id:i,label:n.type==="row"?n.segmentedRow?.displayLabel??n.displayLabel??n.label:n.displayLabel??n.label,sectionId:a,sectionLabel:this.sectionLabelById.get(a)??"Other seats",labels:[]};l.labels.push(s.label),t.set(i,l)}return[...t.values()].sort((s,n)=>s.sectionLabel.localeCompare(n.sectionLabel,void 0,{numeric:!0,sensitivity:"base"})||s.label.localeCompare(n.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)??L.UNGROUPED_ID;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(Ee(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){Ae(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(),n=Math.min(12e4,Math.max(3e4,s*.2)),i=Math.max(0,s-n);this.tokenRefreshTimer=setTimeout(()=>{this.tokenRefreshTimer=null,this.rotateToken()},i)}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(i=>this.status.get(i)==="free");if(!s.length)return;let n=t.releaseAt??this.releaseAt??void 0;this.setSeatsLocal(s,"blocked");try{await this.api.block(this.key,s,{...t,releaseAt:n}),this.clearSelection(),this.done("block",s,n?`Blocked ${s.length} \u2014 auto-release ${new Date(n).toLocaleString()}.`:`Blocked ${s.length} seat${s.length===1?"":"s"}.`)}catch(i){this.setSeatsLocal(s,"free"),this.toastErr(i instanceof f&&i.status===409?"Some seats were just taken. Try again.":"Couldn't block those seats."),this.opts.onError?.(i)}}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(n=>this.status.get(n)==="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(n){this.setSeatsLocal(s,"booked"),this.toastErr("Couldn't release that booked inventory. Check the booking reference."),this.opts.onError?.(n)}}}async setCategory(e,t){let s=this.doc?.categories.find(i=>i.key===e),n=[...new Set(t??this.selectionLabels())].filter(i=>this.labelToSeat.has(i));if(!(!s||!n.length))try{let i=await this.withAuthRetry(()=>this.api.setCategory(this.key,n,e)),a={};for(let l of i.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",i.labels,`Assigned ${i.labels.length.toLocaleString()} object${i.labels.length===1?"":"s"} to ${s.label??s.key}.`)}catch(i){this.toastErr("Couldn't update those event categories."),this.opts.onError?.(i)}}async setTableBooking(e,t,s={}){let n=[...new Set(e)].filter(Boolean);if(!(!n.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 i=await this.withAuthRetry(()=>this.api.setTableBooking(this.key,n,t,s));await this.reloadEventChart(),this.done("setTableBooking",i.tableIds,`Updated ${i.tableIds.length.toLocaleString()} table${i.tableIds.length===1?"":"s"} to ${t==="individual"?"per-chair":t==="whole"?"whole-table":"flexible-group"} booking.`)}catch(i){let a=i instanceof f&&i.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?.(i)}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 n=t!==this.filteredSectionLabel;this.filteredSectionLabel=t,this.mode==="filterSections"&&(this.applyFilteredSectionCanvas(s),this.renderFilterSectionsRail());let i=s.map(({id:a,label:l,seatCount:r,zone:d})=>({id:a,label:l,seatCount:r,...d?{zone:d}:{}}));return n&&this.opts.onFilteredSectionChange?.(i),i}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:n})=>({id:e,label:t,seatCount:s,...n?{zone:n}:{}})):[]}selectByLabels(e){let t=e;if(this.mode==="select"){let s=new Set(this.selectionLabels()),n=this.selectionCap(),i=[...new Set(e)].filter(l=>!s.has(l)).map(l=>this.labelToSeat.get(l)).filter(l=>!!l&&this.canSelectObject(l)),a=Math.max(0,n-s.size);i.length>a&&this.opts.onSelectionLimit?.(n),t=i.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 L.SeatmapRenderer(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(n=>n.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(0,L.expandChart)(e))this.labelToId.set(t.label,t.id),this.labelToSeat.set(t.label,t),this.allIds.push(t.id);for(let t of(0,L.gaAreasOf)(e))for(let s of(0,L.gaUnitLabels)(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,n]of this.status.entries()){let i=this.labelToId.get(s);i&&t[le(n)].push(i)}["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 n=[],i=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 c=this.labelToId.get(l.label);c&&(this.renderer?.setStatus([c],le(r)),n.push(c));let p=this.verbFor(d,r),g=`${p}:${r}`,w=i.get(g)??{labels:[],verb:p,status:r};w.labels.push(l.label),i.set(g,w)}for(let l of i.values()){let r=this.pushActivity(l.labels,l.verb,l.status);r&&this.paintSpatialActivity(r)}n.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=i=>["free","held","booked","blocked"].includes(i)?i:"free",n=new Map;if(t!==void 0){let i=s(t);for(let a of this.knownLabels())n.set(a,i)}for(let[i,a]of Object.entries(e))n.set(i,s(a));this.status=n,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 n of e){this.setStatusLabel(n,t);let i=this.labelToId.get(n);i&&s.push(i)}s.length&&this.renderer?.setStatus(s,le(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 n of e){let i=this.labelToSeat.get(n);if(!i)continue;let a=this.sectionByObject.get(i.rowId);a&&a!==L.UNGROUPED_ID&&t.add(a)}let s=[...t];return{ids:s,labels:s.map(n=>this.sectionLabelById.get(n)??n)}}pulseSeatLabels(e,t){let s=this.activityColor(t);for(let n of e.slice(0,Ut)){let i=this.labelToId.get(n);i&&this.renderer?.flashSeat(i,s)}}paintSpatialActivity(e){let t=e.sectionIds??this.sectionsForLabels(e.labels).ids,s=this.renderer?.getFocusedSection()??null,n=this.followLive&&t.length===1&&(e.status==="held"||e.status==="booked");if(n&&s===t[0]){this.pulseSeatLabels(e.labels,e.status);return}if(n){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 i=this.activityColor(e.status);for(let a of t.slice(0,ut))this.renderer?.flashSection(a,i);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(n=>n.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 n=this.activityColor(t.status);for(let i of s.slice(0,ut))this.renderer?.flashSection(i,n)}else this.pulseSeatLabels(t.labels,t.status)},280)}showLiveEvent(e){let t=this.els.liveevent;if(!t)return;let s=e.sectionLabels??[],n=s.length===1?s[0]:s.length>1?`${s.length} sections`:e.label,i=e.count===1?"seat":"seats";t.innerHTML=`<span class="slm-liveeventdot" style="background:${this.activityColor(e.status)}"></span>
|
|
986
|
+
<span class="slm-liveeventcopy">${u(n)} \xB7 ${e.count.toLocaleString()} ${i} ${u(e.verb)}</span>
|
|
987
|
+
<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 n=Number.isFinite(s.bookedValue)?s.bookedValue:s.bookedRevenue;return t+(Number.isFinite(n)?n: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),n=s.bookedValue??s.revenue??{gross:0,bySection:[]},i=(n.bySection??[]).map(r=>{let d=Number.isFinite(r.bookedValue)?r.bookedValue:r.bookedRevenue;return{...r,bookedValue:d??0,bookedRevenue:d??0}}),a={...n,bySection:i},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],n=this.controlRoomSnapshot?.event?.seatTotal,i={free:s("free"),held:s("held"),booked:s("booked"),blocked:s("blocked"),total:Number.isFinite(n)?n:this.unitTotal(),capacityPct:0,sellThroughPct:0,bookedValue:this.authoritativeGrossRevenue,grossRevenue:this.authoritativeGrossRevenue,bookedValueStatus:this.revenueStatus,revenueStatus:this.revenueStatus,currency:this.currency};i.capacityPct=i.total?Math.round(i.booked/i.total*100):0;let a=i.total-i.blocked;return i.sellThroughPct=a>0?Math.round(i.booked/a*100):0,i}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,n=Date.now()){let i=e[0];if(!i)return null;let a=this.sectionsForLabels(e),l={id:`${i}:${n}:${Math.random().toString(36).slice(2,6)}`,at:n,label:i,labels:[...e],count:e.length,verb:t,status:s,sectionIds:a.ids,sectionLabels:a.labels};return this.feed.unshift(l),this.feed.length>Ce&&(this.feed.length=Ce),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 n of e){let i=n.labels[0];if(!i)continue;let a=this.sectionsForLabels(n.labels),l={id:`log:${n.id}`,at:n.at,label:i,labels:[...n.labels],count:n.labels.length,verb:t[n.action]??n.action,status:s[n.action]??"free",sectionIds:a.ids,sectionLabels:a.labels};this.feed.push(l),this.opts.onActivity?.(l)}this.feed.sort((n,i)=>i.at-n.at),this.feed.length>Ce&&(this.feed.length=Ce),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,n=t==="free"||s||this.selectableObjectLabels.has(e.label);if(!this.opts.isObjectSelectable)return n;try{return this.opts.isObjectSelectable(e,n)===!0}catch(i){return this.opts.onError?.(i),!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[n,i]of t)this.lastSelectionLabels.has(n)||this.opts.onObjectSelected?.(i);for(let n of this.lastSelectionLabels)if(!t.has(n)){let i=this.labelToSeat.get(n);i&&this.opts.onObjectDeselected?.(i)}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=Ee(this.opts.theme);for(let[n,i]of Object.entries(t))e.style.setProperty(n,i);e.innerHTML=`
|
|
979
988
|
<div class="slm-bar">
|
|
980
989
|
<div class="slm-modes" data-ref="modes" role="tablist" aria-label="Manager tools">
|
|
981
990
|
<button class="slm-mode" role="tab" data-mode="view" title="Monitor (M)" aria-keyshortcuts="M">Monitor</button>
|
|
982
991
|
<button class="slm-mode" role="tab" data-mode="inspect" title="Inspect (I)" aria-keyshortcuts="I">Inspect</button>
|
|
992
|
+
<button class="slm-mode" role="tab" data-mode="select" title="Select (X)" aria-keyshortcuts="X">Select</button>
|
|
993
|
+
<button class="slm-mode" role="tab" data-mode="filterSections" title="Filter sections (L)" aria-keyshortcuts="L">Filter</button>
|
|
983
994
|
<button class="slm-mode" role="tab" data-mode="block" title="Block (B)" aria-keyshortcuts="B">Block</button>
|
|
984
995
|
<button class="slm-mode" role="tab" data-mode="sections" title="Sections (S)" aria-keyshortcuts="S">Sections</button>
|
|
996
|
+
<button class="slm-mode" role="tab" data-mode="categories" title="Categories (G)" aria-keyshortcuts="G" hidden>Categories</button>
|
|
997
|
+
<button class="slm-mode" role="tab" data-mode="tables" title="Tables (T)" aria-keyshortcuts="T" hidden>Tables</button>
|
|
985
998
|
<button class="slm-mode" role="tab" data-mode="channels" title="Channels (C)" aria-keyshortcuts="C" hidden>Channels</button>
|
|
986
999
|
</div>
|
|
987
1000
|
<select class="slm-tools" data-ref="tools" aria-label="Manager tools"></select>
|
|
@@ -1006,10 +1019,10 @@
|
|
|
1006
1019
|
<aside class="slm-rail"><div class="slm-railscroll" data-ref="rail"></div></aside>
|
|
1007
1020
|
</div>
|
|
1008
1021
|
<div class="slm-toast" data-ref="toast"></div>
|
|
1009
|
-
`,this.host.appendChild(e),this.root=e,this.updateContainerLayout(),typeof ResizeObserver<"u"&&(this.layoutObserver=new ResizeObserver(()=>this.updateContainerLayout()),this.layoutObserver.observe(e));let s=n=>e.querySelector(`[data-ref="${n}"]`);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(n=>n.addEventListener("click",()=>this.setMode(n.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=(0,
|
|
1010
|
-
<b>${
|
|
1022
|
+
`,this.host.appendChild(e),this.root=e,this.updateContainerLayout(),typeof ResizeObserver<"u"&&(this.layoutObserver=new ResizeObserver(()=>this.updateContainerLayout()),this.layoutObserver.observe(e));let s=n=>e.querySelector(`[data-ref="${n}"]`);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(n=>n.addEventListener("click",()=>this.setMode(n.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(c=>c.id)),s=this.filterableSections().filter(c=>!t.has(c.id)).map(c=>c.id);this.renderer.clearSectionFocus(),this.renderer.setClosedSections(null),this.renderer.setDimmedSections(s);let n=e.flatMap(c=>c.seatLabels).map(c=>this.labelToSeat.get(c)).filter(c=>!!c&&Number.isFinite(c.x)&&Number.isFinite(c.y));if(!n.length){this.renderer.focusRegion(e[0].id);return}let i=Math.min(...n.map(c=>c.x)),a=Math.min(...n.map(c=>c.y)),l=Math.max(...n.map(c=>c.x)),r=Math.max(...n.map(c=>c.y)),d=Math.max(40,Math.max(l-i,r-a)*.08);this.renderer.focusRegion({x:i-d,y:a-d,width:Math.max(1,l-i)+d*2,height:Math.max(1,r-a)+d*2})}buildSectionOptions(){if(this.doc)try{let e=(0,L.computeSections)(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:L.UNGROUPED_ID,label:e.ungrouped.label}),this.sectionLabelById.set(L.UNGROUPED_ID,e.ungrouped.label))}catch{}}paintModeTabs(){let e=[];this.els.modes?.querySelectorAll("[data-mode]").forEach(s=>{let n=s,i=n.dataset.mode,a=i==="channels"?this.channelCaps.view:i==="categories"?this.categoryCanManage:i==="tables"?this.tableCanManage:!0;if(n.hidden=!a,!a)return;e.push({mode:i,label:n.textContent??i});let l=i===this.mode;n.classList.toggle("on",l),n.setAttribute("aria-selected",String(l)),n.tabIndex=l?0:-1});let t=this.els.tools;t&&(t.innerHTML=e.map(s=>`<option value="${s.mode}"${s.mode===this.mode?" selected":""}>${u(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(n=>n.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 n=t>0?"+":"\u2212",i=Math.abs(t);return e==="booked-value"?`${n}${Z(i,s)}`:e==="booked-pct"?`${n}${i.toLocaleString()}pt`:`${n}${i.toLocaleString()}`}paintKpis(e){if(!this.els.kpis)return;let t=e.bookedValueStatus==="current"?Z(e.bookedValue,e.currency):"\u2014",s=this.presenceCounts(),n=[{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"}],i=!1;this.els.kpis.innerHTML=n.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&&(i=!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 c=this.activeKpiDeltas.get(a.key);return`<div class="slm-kpi${c?" changed":""}" data-kpi="${a.key}" title="${u(a.title)}">
|
|
1023
|
+
<b>${a.dot?`<span class="dot" style="background:${a.dot}"></span>`:""}${a.n}</b><span>${a.l}</span>
|
|
1011
1024
|
${c?`<span class="slm-kpidelta${c.down?" down":""}">${c.text}</span>`:""}
|
|
1012
|
-
</div>`}).join(""),i&&(this.kpiCleanupTimer&&clearTimeout(this.kpiCleanupTimer),this.kpiCleanupTimer=setTimeout(()=>{this.kpiCleanupTimer=null,this.activeKpiDeltas.clear(),this.els.kpis?.querySelectorAll(".slm-kpidelta").forEach(
|
|
1025
|
+
</div>`}).join(""),i&&(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=`
|
|
1013
1026
|
<p class="slm-eyebrow">Monitor</p>
|
|
1014
1027
|
<p class="slm-hint">Read-only. Inventory, map activity and booking movement update on the same live board.</p>
|
|
1015
1028
|
<div class="slm-health" data-ref="presence"></div>
|
|
@@ -1027,56 +1040,114 @@
|
|
|
1027
1040
|
<div class="slm-sectionlist" data-ref="sections"></div>
|
|
1028
1041
|
<p class="slm-eyebrow">Activity</p>
|
|
1029
1042
|
<div class="slm-feed" data-ref="feed"></div>
|
|
1030
|
-
`,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 n=this.root?.classList.contains("live"),i=this.lastSyncedAt?
|
|
1031
|
-
<div class="slm-healthitem" title="Active map sessions right now"><b>${
|
|
1032
|
-
<div class="slm-healthitem" title="Sessions currently holding inventory"><b>${
|
|
1043
|
+
`,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 n=this.root?.classList.contains("live"),i=this.lastSyncedAt?Me(this.lastSyncedAt,Date.now()):"waiting",a=this.presenceCounts();this.els.presence.innerHTML=`
|
|
1044
|
+
<div class="slm-healthitem" title="Active map sessions right now"><b>${a?a.shoppingSessions.toLocaleString():"\u2014"}</b><span>Viewing map</span></div>
|
|
1045
|
+
<div class="slm-healthitem" title="Sessions currently holding inventory"><b>${a?a.activeHolds.toLocaleString():"\u2014"}</b><span>Active holds</span></div>
|
|
1033
1046
|
<div class="slm-healthitem"><b>${n?"Healthy":"Reconnecting"}</b><span>Live connection</span></div>
|
|
1034
|
-
<div class="slm-healthitem"><b>${i}</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(n=>[n.sectionId,n])),s=[...e.bookedValue.bySection].sort((n,i)=>{let
|
|
1035
|
-
<span class="slm-sectiontop"><span>${
|
|
1047
|
+
<div class="slm-healthitem"><b>${i}</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(n=>[n.sectionId,n])),s=[...e.bookedValue.bySection].sort((n,i)=>{let a=t.get(n.sectionId)?.netBooked??0;return(t.get(i.sectionId)?.netBooked??0)-a||i.bookedValue-n.bookedValue});this.els.sections.innerHTML=s.length?s.map(n=>{let i=t.get(n.sectionId),a=i?.netBooked??0,l=`${a>0?"+":""}${a}`,r=i?.trend==="rising"||i?.trend==="cooling"?i.trend:"steady";return`<button type="button" class="slm-sectionrow" data-section-focus="${u(n.sectionId)}" title="Focus ${u(n.sectionLabel)} on the map">
|
|
1048
|
+
<span class="slm-sectiontop"><span>${u(n.sectionLabel)}</span><span>${Z(n.bookedValue,e.currency)}</span></span>
|
|
1036
1049
|
<span class="slm-sectionmeta"><span>${n.booked.toLocaleString()}/${n.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>
|
|
1037
|
-
</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(
|
|
1050
|
+
</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})),n=Math.max(0,...s.map(a=>a.rate)),i={};for(let a of s)i[a.sectionId]=n>0?Math.sqrt(a.rate/n):0;this.renderer?.setSectionHeat(i)}renderSelectRail(){let e=this.doc?.categories??[],t=this.opts.numberOfPlacesToSelect,s=this.selectionCap();this.els.rail.innerHTML=`
|
|
1051
|
+
<p class="slm-eyebrow">Custom selection</p>
|
|
1052
|
+
<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>
|
|
1053
|
+
<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>
|
|
1054
|
+
<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>
|
|
1055
|
+
<div class="slm-row">
|
|
1056
|
+
<button class="slm-btn ghost" data-ref="selectall">Select all</button>
|
|
1057
|
+
<button class="slm-btn ghost" data-ref="selectclear" disabled>Clear</button>
|
|
1058
|
+
</div>
|
|
1059
|
+
${e.length?`<p class="slm-eyebrow" style="margin-top:8px">Select by category</p>
|
|
1060
|
+
<div class="slm-chipgrid">${e.map(n=>`
|
|
1061
|
+
<button class="slm-chip" type="button" data-select-cat="${u(n.key)}" aria-pressed="false">
|
|
1062
|
+
<span class="dot" style="background:${u(n.color??"#6e7bff")}"></span>
|
|
1063
|
+
<span>${u(n.label??n.key)}</span>
|
|
1064
|
+
<span class="slm-chipcount" data-cat-count>0</span>
|
|
1065
|
+
<span class="slm-chipcheck" aria-hidden="true">\u2713</span>
|
|
1066
|
+
</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(n=>{n.addEventListener("click",()=>{let i=n.dataset.selectCat,a=[...this.labelToSeat.values()].filter(r=>r.categoryKey===i&&this.canSelectObject(r)),l=new Set(this.selectionLabels());a.length>0&&a.every(r=>l.has(r.label))?this.deselectCategories([i]):this.selectCategories([i])})}),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 n=new Set(e.map(i=>i.label));this.els.rail?.querySelectorAll("[data-select-cat]").forEach(i=>{let a=i.dataset.selectCat,l=[...this.labelToSeat.values()].filter(p=>p.categoryKey===a&&this.canSelectObject(p)),r=l.filter(p=>n.has(p.label)).length,d=l.length>0&&r===l.length;i.classList.toggle("on",d),i.setAttribute("aria-pressed",String(d));let c=i.querySelector("[data-cat-count]");c&&(c.textContent=r?`${r}/${l.length}`:l.length.toLocaleString()),i.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=`
|
|
1067
|
+
<p class="slm-eyebrow">Filter sections</p>
|
|
1068
|
+
<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>
|
|
1069
|
+
${e.size?`<div class="slm-sectionlist">${[...e].map(([t,s])=>{let n=t===this.filteredSectionLabel,i=s.reduce((a,l)=>a+l.seatCount,0);return`<button type="button" class="slm-sectionrow${n?" on":""}" data-filter-section="${u(t)}" aria-pressed="${n}">
|
|
1070
|
+
<span class="slm-sectiontop"><span>${u(t)}</span><span>${i.toLocaleString()}</span></span>
|
|
1071
|
+
<span class="slm-sectionmeta"><span>${s.length===1?"One section":`${s.length.toLocaleString()} matching sections`}</span><span class="slm-sectionlocate">${n?"Filtered":"Filter"}</span></span>
|
|
1072
|
+
</button>`}).join("")}</div>
|
|
1073
|
+
<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=`
|
|
1074
|
+
<p class="slm-eyebrow">Event categories</p>
|
|
1075
|
+
<p class="slm-hint">Override categories for this event only. The reusable chart and existing hold/booking value snapshots do not change.</p>
|
|
1076
|
+
<div class="slm-selbar" aria-live="polite"><span class="slm-selnum" data-ref="categorynum">0</span><span class="slm-sellabel">objects selected</span></div>
|
|
1077
|
+
<div class="slm-row">
|
|
1078
|
+
<button class="slm-btn ghost" data-ref="categoryall">Select all</button>
|
|
1079
|
+
<button class="slm-btn ghost" data-ref="categoryclear" disabled>Clear</button>
|
|
1080
|
+
</div>
|
|
1081
|
+
<p class="slm-eyebrow" style="margin-top:8px">Assign selected objects</p>
|
|
1082
|
+
<div class="slm-categoryassign">${e.map(t=>{let s=[...this.labelToSeat.values()].filter(n=>n.categoryKey===t.key).length;return`<div class="slm-categoryrow">
|
|
1083
|
+
<span class="dot" style="background:${u(t.color??"#6e7bff")}"></span>
|
|
1084
|
+
<span class="slm-categorycopy"><b>${u(t.label??t.key)}</b><small>${s.toLocaleString()} currently</small></span>
|
|
1085
|
+
<button type="button" class="slm-linkbtn" data-pick-category="${u(t.key)}">Select current</button>
|
|
1086
|
+
<button type="button" class="slm-btn" data-assign-category="${u(t.key)}" disabled>Apply</button>
|
|
1087
|
+
</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?(0,L.floorsOf)(this.doc):[]).flatMap(s=>s.objects).filter(s=>s.type==="table");if(this.inventoryModelVersion!==2){this.els.rail.innerHTML=`
|
|
1088
|
+
<p class="slm-eyebrow">Table booking</p>
|
|
1089
|
+
<p class="slm-hint">Per-event table modes require inventory model 2 so grouped tables have one authoritative inventory identity.</p>
|
|
1090
|
+
<div class="slm-empty">Upgrade this event's inventory model before changing table booking.</div>`;return}if(!e.length){this.els.rail.innerHTML=`
|
|
1091
|
+
<p class="slm-eyebrow">Table booking</p>
|
|
1092
|
+
<p class="slm-hint">Choose whether each event table is sold by chair or as one grouped booking.</p>
|
|
1093
|
+
<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=`
|
|
1094
|
+
<p class="slm-eyebrow">Table booking</p>
|
|
1095
|
+
<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>
|
|
1096
|
+
<div class="slm-row">
|
|
1097
|
+
<button class="slm-btn ghost" data-table-all="individual"${this.tableBookingSaving?" disabled":""}>All per chair</button>
|
|
1098
|
+
<button class="slm-btn ghost" data-table-all="whole"${this.tableBookingSaving?" disabled":""}>All whole table</button>
|
|
1099
|
+
</div>
|
|
1100
|
+
<div class="slm-tablelist">${e.map(s=>{let n=t(s);return`<div class="slm-tablerow">
|
|
1101
|
+
<span class="slm-tablecopy"><b>${u(s.displayLabel??s.label)}</b><small>${s.seatCount.toLocaleString()} chairs</small></span>
|
|
1102
|
+
<select class="slm-select" data-table-mode="${u(s.id)}" ${this.tableBookingSaving?"disabled":""}>
|
|
1103
|
+
<option value="individual"${n==="individual"?" selected":""}>Book per chair</option>
|
|
1104
|
+
<option value="whole"${n==="whole"?" selected":""}>Book whole table</option>
|
|
1105
|
+
${n==="variable"?'<option value="variable" selected>Flexible group</option>':""}
|
|
1106
|
+
</select>
|
|
1107
|
+
</div>`}).join("")}</div>
|
|
1108
|
+
<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(n=>n.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=`
|
|
1038
1109
|
<p class="slm-eyebrow">Inspect seats</p>
|
|
1039
1110
|
<p class="slm-hint">Select a seat to see its availability and booking context. Nothing changes in this view.</p>
|
|
1040
|
-
<div class="slm-empty">Select a seat on the map.</div>`;return}let s=this.status.get(t.label)??"free",n={free:"Free",held:"Held",booked:"Booked",blocked:"Blocked"},i=this.sectionByObject.get(t.rowId)??
|
|
1111
|
+
<div class="slm-empty">Select a seat on the map.</div>`;return}let s=this.status.get(t.label)??"free",n={free:"Free",held:"Held",booked:"Booked",blocked:"Blocked"},i=this.sectionByObject.get(t.rowId)??L.UNGROUPED_ID,a=this.sectionLabelById.get(i)??"Other seats",l=this.doc?.categories.find(g=>g.key===t.categoryKey),r=this.controlRoomSnapshot?.bookedValue.bySection.find(g=>g.sectionId===i),d=this.doc?.objects.find(g=>g.id===t.rowId),c=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=`
|
|
1041
1112
|
<p class="slm-eyebrow">${p} details</p>
|
|
1042
1113
|
<p class="slm-hint">Live availability and section performance.</p>
|
|
1043
1114
|
<div class="slm-inspect-card">
|
|
1044
|
-
<div class="slm-inspect-label">${
|
|
1115
|
+
<div class="slm-inspect-label">${u(t.label)}</div>
|
|
1045
1116
|
<div class="slm-inspect-grid">
|
|
1046
1117
|
<div><span>Status</span><b>${n[s]}</b></div>
|
|
1047
|
-
<div><span>Section</span><b>${
|
|
1048
|
-
${c?`<div><span>${c.label}</span><b>${
|
|
1049
|
-
<div><span>Category</span><b>${
|
|
1118
|
+
<div><span>Section</span><b>${u(a)}</b></div>
|
|
1119
|
+
${c?`<div><span>${c.label}</span><b>${u(c.value)}</b></div>`:""}
|
|
1120
|
+
<div><span>Category</span><b>${u(l?.label??t.categoryKey)}</b></div>
|
|
1050
1121
|
<div><span>Booked in section</span><b>${r?`${r.booked} of ${r.total}`:"\u2014"}</b></div>
|
|
1051
1122
|
<div><span>Section booked value</span><b>${r&&this.controlRoomSnapshot?Z(r.bookedValue,this.controlRoomSnapshot.currency):"\u2014"}</b></div>
|
|
1052
1123
|
</div>
|
|
1053
|
-
</div>`}async refreshAvailability(){try{let e=await this.withAuthRetry(()=>this.api.availability(this.key));this.availabilityRules=e.rules??{},this.effectiveClosed=new Set(
|
|
1124
|
+
</div>`}async refreshAvailability(){try{let e=await this.withAuthRetry(()=>this.api.availability(this.key));this.availabilityRules=e.rules??{},this.effectiveClosed=new Set(Ie(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 f&&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(n=>typeof n=="string")),s=!0),Array.isArray(t)&&(this.effectiveClosed=new Set(t.filter(n=>typeof n=="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}=re(this.sectionRowsInput());if(!e.length){this.els.rail.innerHTML=`
|
|
1054
1125
|
<p class="slm-eyebrow">Availability windows</p>
|
|
1055
1126
|
<p class="slm-hint">Draw sections or zones in the designer to schedule availability per area. This chart has none yet.</p>
|
|
1056
|
-
<div class="slm-empty">No sections on this chart.</div>`;return}let n=[];t&&n.push(`${t} hidden`),s&&n.push(`${s} closed`);let i=n.length?n.join(" \xB7 "):"All sections open and on sale",
|
|
1127
|
+
<div class="slm-empty">No sections on this chart.</div>`;return}let n=[];t&&n.push(`${t} hidden`),s&&n.push(`${s} closed`);let i=n.length?n.join(" \xB7 "):"All sections open and on sale",a=t>0||s>0;this.els.rail.innerHTML=`
|
|
1057
1128
|
<p class="slm-eyebrow">Availability windows</p>
|
|
1058
1129
|
<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>
|
|
1059
|
-
<div class="slm-availlist" data-ref="availlist">${e.map(l=>
|
|
1130
|
+
<div class="slm-availlist" data-ref="availlist">${e.map(l=>tt(l,this.availabilitySaving)).join("")}</div>
|
|
1060
1131
|
<div class="slm-availsummary">
|
|
1061
|
-
<span class="slm-availdot${
|
|
1062
|
-
<span>${
|
|
1132
|
+
<span class="slm-availdot${a?" warn":""}"></span>
|
|
1133
|
+
<span>${u(i)}</span>
|
|
1063
1134
|
</div>
|
|
1064
1135
|
<div class="slm-availcallout">
|
|
1065
1136
|
<span class="slm-availstar" aria-hidden="true">\u2726</span>
|
|
1066
1137
|
<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>
|
|
1067
|
-
</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=re(this.sectionRowsInput()).rows.find(l=>l.id===e),n=s?.seatLabels??this.availabilityRules[e]?.labels??[],i={...this.availabilityRules},
|
|
1068
|
-
<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 n=s.count>1?` +${s.count-1}`:"",i=s.sectionLabels??[],
|
|
1138
|
+
</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=re(this.sectionRowsInput()).rows.find(l=>l.id===e),n=s?.seatLabels??this.availabilityRules[e]?.labels??[],i={...this.availabilityRules},a=Xe(t,n,this.availabilityRules[e]);if(a?i[e]=a:delete i[e],s?.kind==="zone"&&this.sectionsBase)for(let l of this.sectionsBase.sections)l.zone===e&&delete i[l.id];this.persistAvailability(i)}setSectionRulePatch(e,t){let s=this.availabilityRules[e];if(!s)return;let i=re(this.sectionRowsInput()).rows.find(a=>a.id===e)?.seatLabels??s.labels??[];this.persistAvailability({...this.availabilityRules,[e]:{...s,...t,labels:i}})}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(Ie(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=Je.map(t=>`<div class="slm-legrow"><span class="slm-legdot" style="background:${t.color}"></span>
|
|
1139
|
+
<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 n=s.count>1?` +${s.count-1}`:"",i=s.sectionLabels??[],a=i.length===1?i[0]:i.length>1?`${i.length} sections`:"";return`<button type="button" class="slm-feedrow" data-feed-id="${u(s.id)}" title="Locate this activity on the map">
|
|
1069
1140
|
<span class="slm-feeddot" style="background:${t[s.status]}"></span>
|
|
1070
|
-
<span class="slm-feedtext">${
|
|
1071
|
-
<span class="slm-feedmeta"><span class="slm-feedtime">${
|
|
1072
|
-
</button>`}).join("")}renderBlockRail(){let t=(this.doc?.categories??[]).map(c=>`<button class="slm-chip" type="button" data-cat="${
|
|
1073
|
-
<span class="dot" style="background:${
|
|
1074
|
-
<span>${
|
|
1141
|
+
<span class="slm-feedtext">${a?`<span class="slm-feedsection">${u(a)}</span>`:""}${s.count===1?"Seat":"Seats"} <b>${u(s.label)}${n}</b> ${u(s.verb)}</span>
|
|
1142
|
+
<span class="slm-feedmeta"><span class="slm-feedtime">${Me(s.at,e)}</span><span class="slm-feedlocate">Locate</span></span>
|
|
1143
|
+
</button>`}).join("")}renderBlockRail(){let t=(this.doc?.categories??[]).map(c=>`<button class="slm-chip" type="button" data-cat="${u(c.key)}" aria-pressed="false">
|
|
1144
|
+
<span class="dot" style="background:${u(c.color??"#6e7bff")}"></span>
|
|
1145
|
+
<span>${u(c.label??c.key)}</span>
|
|
1075
1146
|
<span class="slm-chipcount" data-cat-count>0</span>
|
|
1076
1147
|
<span class="slm-chipcheck" aria-hidden="true">\u2713</span>
|
|
1077
1148
|
</button>`).join(""),s=this.sectionOptions.length?`<div class="slm-field"><label>Select a whole section</label>
|
|
1078
1149
|
<select class="slm-select" data-ref="section"><option value="">Choose a section\u2026</option>
|
|
1079
|
-
${this.sectionOptions.map(c=>`<option value="${
|
|
1150
|
+
${this.sectionOptions.map(c=>`<option value="${u(c.id)}">${u(c.label)}</option>`).join("")}</select></div>`:"",n=this.sectionOptions.map(c=>`<option value="${u(c.id)}">${u(c.label)}</option>`).join("");this.els.rail.innerHTML=`
|
|
1080
1151
|
<p class="slm-eyebrow">Block & unblock</p>
|
|
1081
1152
|
<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>
|
|
1082
1153
|
<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>
|
|
@@ -1119,8 +1190,8 @@
|
|
|
1119
1190
|
<button class="slm-btn ghost" data-ref="markall" style="width:100%" disabled>Put all blocked seats on sale</button>
|
|
1120
1191
|
<p class="slm-note slm-allnote" data-ref="markallnote">For a full reset only. You will be asked to confirm.</p>
|
|
1121
1192
|
</div>
|
|
1122
|
-
`;let i=c=>this.els.rail.querySelector(`[data-ref="${c}"]`);this.els.selnum=i("selnum"),this.els.doblock=i("doblock"),this.els.dounblock=i("dounblock"),this.els.selmeta=i("selmeta"),this.els.blockedcount=i("blockedcount"),this.els.blockedshowing=i("blockedshowing"),this.els.blockedlist=i("blockedlist"),this.els.selblocked=i("selblocked"),this.els.markall=i("markall"),this.els.markallnote=i("markallnote"),i("doblock").addEventListener("click",()=>{this.block()}),i("dounblock").addEventListener("click",()=>{this.unblock()}),i("selall").addEventListener("click",()=>this.selectAll()),i("clearsel").addEventListener("click",()=>this.clearSelection()),i("markall").addEventListener("click",()=>this.confirmUnblockAll()),this.els.rail.querySelectorAll("[data-cat]").forEach(c=>c.addEventListener("click",()=>this.toggleCategory(c.dataset.cat)));let
|
|
1193
|
+
`;let i=c=>this.els.rail.querySelector(`[data-ref="${c}"]`);this.els.selnum=i("selnum"),this.els.doblock=i("doblock"),this.els.dounblock=i("dounblock"),this.els.selmeta=i("selmeta"),this.els.blockedcount=i("blockedcount"),this.els.blockedshowing=i("blockedshowing"),this.els.blockedlist=i("blockedlist"),this.els.selblocked=i("selblocked"),this.els.markall=i("markall"),this.els.markallnote=i("markallnote"),i("doblock").addEventListener("click",()=>{this.block()}),i("dounblock").addEventListener("click",()=>{this.unblock()}),i("selall").addEventListener("click",()=>this.selectAll()),i("clearsel").addEventListener("click",()=>this.clearSelection()),i("markall").addEventListener("click",()=>this.confirmUnblockAll()),this.els.rail.querySelectorAll("[data-cat]").forEach(c=>c.addEventListener("click",()=>this.toggleCategory(c.dataset.cat)));let a=this.els.rail.querySelector('[data-ref="section"]');a?.addEventListener("change",()=>{a.value&&(this.selectSection(a.value),a.value="")});let l=i("blockedsearch"),r=i("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()}),i("selblocked").addEventListener("click",()=>{this.toggleLabels(this.filteredBlockedSeats().map(c=>c.label))}),i("blockedlist").addEventListener("click",c=>{let p=c.target,g=p.closest("[data-blocked-label]");g?.dataset.blockedLabel?this.toggleLabels([g.dataset.blockedLabel]):p.closest("[data-blocked-more]")&&(this.blockedResultLimit+=100,this.paintBlockedInventory())});let d=i("release");d.addEventListener("change",()=>{let c=d.value?new Date(d.value).getTime():NaN;this.releaseAt=Number.isFinite(c)&&c>Date.now()?c:null;let p=i("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,n]of this.labelToSeat.entries())n.categoryKey===e&&this.isBlockSelectable(s)&&t.push(s);this.toggleLabels(t)}toggleLabels(e){if(!this.renderer)return;let t=e.filter(i=>this.labelToSeat.has(i)&&this.isBlockSelectable(i));if(!t.length)return;let s=new Set(this.selectionLabels());if(t.every(i=>s.has(i))){let i=t.map(a=>this.labelToId.get(a)).filter(a=>!!a);this.renderer.deselect(i)}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 n=this.els.doblock,i=this.els.dounblock;n.disabled=t===0,i.disabled=s===0,n.textContent=t?`Block ${t.toLocaleString()}`:"Block selected",i.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 n=s.dataset.cat,i=[];for(let[c,p]of this.labelToSeat.entries())p.categoryKey===n&&this.isBlockSelectable(c)&&i.push(c);let a=i.filter(c=>t.has(c)).length,l=i.length>0&&a===i.length,r=a>0&&!l;s.disabled=i.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 ${i.length.toLocaleString()} seats in this category from the selection`:r?`Select the remaining ${(i.length-a).toLocaleString()} seats in this category`:`Select all ${i.length.toLocaleString()} seats in this category`);let d=s.querySelector("[data-cat-count]");d&&(d.textContent=a?`${a.toLocaleString()}/${i.length.toLocaleString()}`:i.length.toLocaleString())})}filteredBlockedSeats(){let e=this.blockedQuery.trim().toLocaleLowerCase(),t=[];for(let[s,n]of this.labelToSeat.entries()){if(this.status.get(s)!=="blocked")continue;let i=this.sectionByObject.get(n.rowId)??L.UNGROUPED_ID;if(!(this.blockedSection&&i!==this.blockedSection)){if(e){let a=this.doc?.categories.find(p=>p.key===n.categoryKey)?.label??n.categoryKey,l=this.sectionLabelById.get(i)??"Other seats",r=this.doc?.objects.find(p=>p.id===n.rowId),d=r?.type==="row"||r?.type==="table"?r.label:"";if(!`${s} ${a} ${l} ${d}`.toLocaleLowerCase().includes(e))continue}t.push(n)}}return t.sort((s,n)=>s.label.localeCompare(n.label,void 0,{numeric:!0,sensitivity:"base"}))}paintBlockedInventory(){if(!this.els.blockedlist)return;let e=[...this.status.entries()].filter(([,c])=>c==="blocked").length,t=this.filteredBlockedSeats(),s=t.slice(0,this.blockedResultLimit),n=new Set(this.selectionLabels()),i=t.filter(c=>n.has(c.label)).length,a=t.length>0&&i===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(c=>{let p=this.sectionByObject.get(c.rowId)??L.UNGROUPED_ID,g=this.sectionLabelById.get(p)??"Other seats",w=this.doc?.categories.find(m=>m.key===c.categoryKey)?.label??c.categoryKey,b=n.has(c.label);return`<button type="button" class="slm-blockeditem${b?" on":""}" data-blocked-label="${u(c.label)}" aria-pressed="${b}">
|
|
1123
1194
|
<span class="slm-blockedcheck" aria-hidden="true">\u2713</span>
|
|
1124
|
-
<span class="slm-blockedcopy"><span class="slm-blockedlabel">${
|
|
1125
|
-
<span class="slm-blockedmeta">${
|
|
1126
|
-
</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 n=e==="block"?this.pushActivity(t,"blocked","blocked"):e==="unblock"||e==="unblockAll"?this.pushActivity(t,"unblocked","free"):e==="cancelBooking"?this.pushActivity(t,"cancelled","free"):null;n&&this.paintSpatialActivity(n)}e!=="setHoldTtl"&&this.refreshControlRoom().catch(n=>this.opts.onError?.(n)),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>`)}};
|
|
1195
|
+
<span class="slm-blockedcopy"><span class="slm-blockedlabel">${u(c.label)}</span>
|
|
1196
|
+
<span class="slm-blockedmeta">${u(g)} \xB7 ${u(w)}</span></span>
|
|
1197
|
+
</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 n=e==="block"?this.pushActivity(t,"blocked","blocked"):e==="unblock"||e==="unblockAll"?this.pushActivity(t,"unblocked","free"):e==="cancelBooking"?this.pushActivity(t,"cancelled","free"):null;n&&this.paintSpatialActivity(n)}e!=="setHoldTtl"&&this.refreshControlRoom().catch(n=>this.opts.onError?.(n)),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>`)}};ze();xe();ae();0&&(module.exports={ACCESS_LINK_DEFAULTS,ChannelsMode,ManageApi,ManageApiError,PUBLIC_CHANNEL_ID,PUBLIC_CHANNEL_NAME,SeatManager,accessIntentDescription,accessIntentLabel,accessLine,accessLinkBadge,accessLinkErrorCopy,accessLinkIsLive,accessLinkPolicyLines,bucketRows,bucketRowsHtml,dropReviewRows,intentForbidsCopy,intentSwitchBlockedCopy,isPublicChannelId,markerLetter,markerOf,mutationCount,needsMoveConfirmation,planAssignment,retryAfterCopy,selectionSources,stateBadge,suggestMarker});
|