@seatlayer/js 0.52.0 → 0.54.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +16 -17
- package/README.md +2 -2
- package/dist/channelsMode-BX7s9bUl.d.cts +2241 -0
- package/dist/channelsMode-BX7s9bUl.d.ts +2241 -0
- package/dist/channelsMode-RUNPEHKD.js +1 -0
- package/dist/chunk-7F6RSJRL.js +695 -0
- package/dist/chunk-OSZ7DOEH.js +1 -0
- package/dist/hostedCheckout-3Y24DB65.js +35 -0
- package/dist/index.cjs +29 -7751
- package/dist/index.d.cts +170 -106
- package/dist/index.d.ts +170 -106
- package/dist/index.js +28 -7376
- package/dist/manager.cjs +298 -5656
- package/dist/manager.d.cts +8 -2261
- package/dist/manager.d.ts +8 -2261
- package/dist/manager.js +71 -2418
- package/package.json +3 -8
- package/dist/channelsMode-S4GEWCWS.js +0 -12
- package/dist/channelsMode-S4GEWCWS.js.map +0 -1
- package/dist/chunk-5URCWBHM.js +0 -3683
- package/dist/chunk-5URCWBHM.js.map +0 -1
- package/dist/chunk-HMLY7DHA.js +0 -16
- package/dist/chunk-HMLY7DHA.js.map +0 -1
- package/dist/hostedCheckout-GVS6UKYA.js +0 -370
- package/dist/hostedCheckout-GVS6UKYA.js.map +0 -1
- package/dist/index.cjs.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/manager.cjs.map +0 -1
- package/dist/manager.js.map +0 -1
|
@@ -0,0 +1,695 @@
|
|
|
1
|
+
var C="public",E="Public sale";function M(i){return i==null||i===""||i===C}var X=["#a78bfa","#2dd4bf","#fb923c","#60a5fa","#f472b6","#a3e635","#f87171","#38bdf8","#c084fc","#facc15"],He="#f4b740",ce="ABCDEFGHJKLMNPQRSTUVWXYZ";function _(i,e){let t=(i??"").trim();return((/\p{L}/u.exec(t)?.[0]??t[0]??"")||e).toUpperCase().slice(0,1)}function ee(i,e){let t=new Set([...e].map(r=>_(r,"")).filter(Boolean)),n=_(i,"");return{letter:ce.includes(n)&&!t.has(n)?n:[...ce].find(r=>!t.has(r))??(n||"X"),color:X[t.size%X.length]}}function de(i,e=0){return M(i.id)?{letter:_(i.marker,"P"),color:i.color||He}:{letter:_(i.marker||i.name,"?"),color:i.color||X[e%X.length]}}function ue(i,e,t){let n=new Map;for(let o of i){let a=J(e.get(o));n.set(a,(n.get(a)??0)+1)}let s=[{id:C,name:t?.publicSale?.name??E},...(t?.channels??[]).map(o=>({id:o.id,name:o.name}))],r=[];for(let o of s){let a=n.get(o.id);a&&r.push({channelId:o.id,name:o.name,count:a}),n.delete(o.id)}for(let[o,a]of n)r.push({channelId:o,name:M(o)?E:"Another channel",count:a});return r}function J(i){return M(i)?C:i}var he=12;function Q(i){return{count:i.length,labels:i.slice(0,he),truncated:i.length>he}}function pe(i){let{labels:e,allocation:t,statusOf:n,nameOf:s}=i,r=J(i.targetChannelId),o=new Set,a=0,l=0,h=new Map,d=[],p=[],w=[];for(let b of e){if(o.has(b))continue;o.add(b);let u=n(b);if(!u){w.push(b);continue}let m=J(t.get(b));if(m===r){l+=1;continue}if(u==="held"){d.push(b);continue}if(u==="booked"){p.push(b);continue}m===C?a+=1:h.set(m,(h.get(m)??0)+1)}let L=[...h.entries()].map(([b,u])=>({channelId:b,name:s(b),count:u}));return{changedFromPublic:{count:a},movedFromOtherChannel:{count:L.reduce((b,u)=>b+u.count,0),channels:L},alreadyInTarget:{count:l},skippedHeld:Q(d),skippedBooked:Q(p),notFound:Q(w)}}function K(i){return i.changedFromPublic.count+i.movedFromOtherChannel.count}function me(i){return i.movedFromOtherChannel.count>0}function ge(i,e){let t=[];i.changedFromPublic.count&&t.push({kind:"add",icon:"+",count:i.changedFromPublic.count,text:`${i.changedFromPublic.count.toLocaleString()} from ${E}`});for(let n of i.movedFromOtherChannel.channels)t.push({kind:"move",icon:"\u21C4",count:n.count,text:`${n.count.toLocaleString()} moved out of ${n.name??"another channel"}`,why:"needs this confirmation"});return i.alreadyInTarget.count&&t.push({kind:"same",icon:"=",count:i.alreadyInTarget.count,text:`${i.alreadyInTarget.count.toLocaleString()} already in ${e}`,why:"unchanged"}),i.skippedHeld.count&&t.push({kind:"skip",icon:"\u23F8",count:i.skippedHeld.count,text:`${i.skippedHeld.count.toLocaleString()} in a buyer's checkout`,why:"can't move while held",peek:Y(i.skippedHeld)}),i.skippedBooked.count&&t.push({kind:"skip",icon:"\u{1F512}",count:i.skippedBooked.count,text:`${i.skippedBooked.count.toLocaleString()} already sold`,why:"sales are never rewritten",peek:Y(i.skippedBooked)}),i.notFound.count&&t.push({kind:"skip",icon:"?",count:i.notFound.count,text:`${i.notFound.count.toLocaleString()} not on this map`,why:"these seats are no longer part of the event",peek:Y(i.notFound)}),t}function Y(i){if(!i.labels.length)return;let e=i.labels.slice(0,4).join(", ");return i.truncated||i.labels.length>4?`${e}\u2026`:e}function be(i){let e=i?.retryAfterMs??(i?.latestHoldExpiresAt?Math.max(0,i.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 ve(i){if(!i||!i.intent)return"\u2014";let e=i.intent==="server"?"Website integration":i.intent==="hosted_link"?"Buyer link":i.intent==="internal"?"Your staff sell these":"Protected reserve",t=i.hasActiveGrants?"in use now":i.lastMintAt?`last used ${new Date(i.lastMintAt).toLocaleDateString()}`:null,n=i.detail??t;return n?`${e} \xB7 ${n}`:e}function q(i){return i==="internal"?"Sell through your own staff":i==="server"?"Integrate a website or app":i==="hosted_link"?"Sell with a buyer link":"Keep as protected reserve"}function fe(i){switch(i){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 Be(i){return i==="hosted_link"?"hosted_link":i==="server"?"server":i==="staff"?"internal":null}function G(i){let e=Z(i?.accessIntent),t=Be(i?.route),n=`This channel is set to "${q(e)}"`;return t?`${n}, so it cannot do that. Switch it to "${q(t)}" first.`:`${n}, so it cannot do that. Choose a different route for this channel first.`}function Z(i){return i==="internal"||i==="server"||i==="hosted_link"||i==="none"?i:"none"}function F(i,e,t){return`${i.toLocaleString()} ${i===1?e:t}`}function ye(i){let e=Math.max(0,i?.liveAccessLinks??0),t=Math.max(0,i?.activeSessions??0),n=Z(i?.from),s=Z(i?.to),o=`${[e?F(e,"buyer link is live","buyer links are live"):null,t?F(t,"buyer is in a checkout","buyers are in a checkout"):null].filter(Boolean).join(", and ")||"Buyers are inside this channel"} on "${q(n)}". Moving it to "${q(s)}" changes what happens to them.`,a=[];return e&&a.push(`${F(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&&a.push(`${F(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:a}}var te={maxRedemptions:100,maxQuantity:4};function ke(i){switch(i.status??i.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 ne(i){return i.state==="active"&&i.status==="active"}function _e(i){return Number.isFinite(i)?new Date(i).toLocaleString(void 0,{day:"numeric",month:"short",year:"numeric",hour:"numeric",minute:"2-digit"}):"\u2014"}function se(i){return[{k:"Expires",v:_e(i.expiresAt)},{k:"Redemptions",v:`${i.redemptions.toLocaleString()} of ${i.maxRedemptions.toLocaleString()} used`},{k:"Seats per buyer",v:`${i.maxQuantity.toLocaleString()} seat${i.maxQuantity===1?"":"s"} maximum`},{k:"Covers",v:i.includePublic?"This channel's allocation and Public sale seats":"This channel's allocation only"}]}function j(i){let e=i?.serverMessage?.trim();switch(i?.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 G(i?.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 i?.status===403?"Hosted access links need channel-management permission.":e||"That did not go through. Try again."}}function Ze(i){return(i?.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?Y({count:e.count,labels:e.labels,truncated:e.truncated??!1}):void 0}))}function we(i){return i==="builtin"?"Built-in":i==="active"?"Active":i==="paused"?"Paused":"Archived"}var f=class extends Error{constructor(e,t,n,s,r,o){super(t),this.name="ManageApiError",this.status=e,this.code=n,this.conflicts=s,this.details=r,this.serverMessage=o}};async function xe(i){let t=(i.headers.get("content-type")??"").includes("application/json")?await i.json().catch(()=>null):null;if(!i.ok){let n=t;throw new f(i.status,n?.error??`request_failed_${i.status}`,n?.code,n?.conflicts,n?.details,typeof n?.message=="string"?n.message:void 0)}return t}function S(i){return i&&typeof i=="object"?i:{}}function W(i,e,t=0){return typeof i=="number"&&Number.isFinite(i)?i:typeof e=="number"&&Number.isFinite(e)?e:t}function Se(i,e){return i===null?null:typeof i=="number"&&Number.isFinite(i)?i:i!==void 0?null:typeof e=="number"&&Number.isFinite(e)?e:null}function Le(i){let e=S(i),t=W(e.bookedValue,e.bookedRevenue);return{...e,bookedValue:t,bookedRevenue:t}}function qe(i){let e=S(i),t=S(e.report),n=Array.isArray(t.byCategory)?t.byCategory.map(r=>{let o=S(r),a=W(o.bookedValue,o.bookedRevenue);return{...o,bookedValue:a,bookedRevenue:a}}):[],s=Array.isArray(t.bySection)?t.bySection.map(Le):void 0;return{...e,report:{...t,byCategory:n,...s?{bySection:s}:{}}}}function Oe(i){let e=S(i),t=S(e.bookedValue),n=S(e.revenue),s=Object.keys(t).length?t:n,r=Array.isArray(t.bySection)?t.bySection:Array.isArray(n.bySection)?n.bySection:[],o={...s,gross:W(t.gross,n.gross),bySection:r.map(Le)},a=S(e.velocity),l=Array.isArray(a.bySection)?a.bySection.map(h=>{let d=S(h),p=W(d.bookedValue,d.grossRevenue);return{...d,bookedValue:p,grossRevenue:p}}):[];return{...e,bookedValue:o,revenue:o,velocity:{...a,bySection:l}}}function ze(i){let e=S(i),t=S(e.report),n=typeof t.includesBookedValue=="boolean"?t.includesBookedValue:t.includesRevenue===!0,s=Array.isArray(t.rows)?t.rows.map(a=>{let l=S(a),h=S(l.attribution),d=Se(h.bookedValue,h.revenue);return{...l,attribution:{...h,bookedValue:d,revenue:d}}}):[],r=S(t.totals),o=Se(r.bookedValue,r.revenue);return{...e,report:{...t,includesBookedValue:n,includesRevenue:n,rows:s,totals:{...r,bookedValue:o,revenue:o}}}}function ie(i){let e=S(i),t=typeof e.includesBookedValue=="boolean"?e.includesBookedValue:e.includesRevenue===!0;return{...e,includesBookedValue:t,includesRevenue:t}}var Ce=class{constructor(e,t){this.base=e.replace(/\/+$/,""),this.token=t}setToken(e){this.token=e}auth(e,t={}){let n=t.method??"GET",s={Authorization:`Bearer ${this.token}`},r;return t.body!==void 0&&(s["Content-Type"]="application/json",r=JSON.stringify(t.body)),fetch(`${this.base}${e}`,{method:n,headers:s,body:r,credentials:"omit"}).then(o=>xe(o))}async authBlob(e){let t=await fetch(`${this.base}${e}`,{method:"GET",headers:{Authorization:`Bearer ${this.token}`},credentials:"omit"});return t.ok||await xe(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,n={}){let s={labels:t};return typeof n.releaseAt=="number"&&(s.releaseAt=n.releaseAt),n.reason&&(s.reason=n.reason),this.auth(`/v1/events/${encodeURIComponent(e)}/block`,{method:"POST",body:s})}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,n){return this.auth(`/v1/events/${encodeURIComponent(e)}/unbook`,{method:"POST",body:{labels:t,bookingRef:n}})}setHoldTtl(e,t){return this.auth(`/v1/events/${encodeURIComponent(e)}/hold-ttl`,{method:"POST",body:{holdTtlMs:t}})}bookings(e,t={}){let n=new URLSearchParams;t.q&&n.set("q",t.q),t.state&&n.set("state",t.state),t.cursor&&n.set("cursor",t.cursor),t.limit!=null&&n.set("limit",String(t.limit));let s=n.toString();return this.auth(`/v1/events/${encodeURIComponent(e)}/bookings${s?`?${s}`:""}`)}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 n=t.includeArchived?"?includeArchived=1":"";return this.auth(`/v1/events/${encodeURIComponent(e)}/channels${n}`)}channelAllocation(e,t={}){let n=new URLSearchParams;t.afterLabel&&n.set("afterLabel",t.afterLabel),t.limit!=null&&n.set("limit",String(t.limit));let s=n.toString();return this.auth(`/v1/events/${encodeURIComponent(e)}/channels/allocation${s?`?${s}`:""}`)}channelAudit(e,t={}){let n=new URLSearchParams;t.limit!=null&&n.set("limit",String(t.limit)),t.before!=null&&n.set("before",String(t.before));let s=n.toString();return this.auth(`/v1/events/${encodeURIComponent(e)}/channels/audit${s?`?${s}`:""}`)}createChannel(e,t){return this.auth(`/v1/events/${encodeURIComponent(e)}/channels`,{method:"POST",body:t})}renameChannel(e,t,n){return this.auth(`/v1/events/${encodeURIComponent(e)}/channels/${encodeURIComponent(t)}`,{method:"PATCH",body:{name:n}})}setChannelPaused(e,t,n){let s=n?"pause":"unpause";return this.auth(`/v1/events/${encodeURIComponent(e)}/channels/${encodeURIComponent(t)}/${s}`,{method:"POST",body:{}})}archiveChannel(e,t,n){return this.auth(`/v1/events/${encodeURIComponent(e)}/channels/${encodeURIComponent(t)}/archive`,{method:"POST",body:{destination:n}})}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,n={}){let s=new URLSearchParams;t.length&&s.set("channelIds",t.join(",")),n.includePublic!=null&&s.set("includePublic",n.includePublic?"1":"0");let r=s.toString();return this.auth(`/v1/events/${encodeURIComponent(e)}/channels/preview${r?`?${r}`:""}`)}setChannelAccessIntent(e,t,n,s={}){return this.auth(`/v1/events/${encodeURIComponent(e)}/channels/${encodeURIComponent(t)}`,{method:"PATCH",body:{accessIntent:n,...s.acknowledgeLiveAccess?{acknowledgeLiveAccess:!0}:{},...s.reason?{reason:s.reason}:{}}})}createAccessLink(e,t,n={}){return this.auth(`/v1/events/${encodeURIComponent(e)}/channels/${encodeURIComponent(t)}/access-links`,{method:"POST",body:n})}accessLinks(e,t){return this.auth(`/v1/events/${encodeURIComponent(e)}/channels/${encodeURIComponent(t)}/access-links`)}rotateAccessLink(e,t,n,s){return this.auth(`/v1/events/${encodeURIComponent(e)}/channels/${encodeURIComponent(t)}/access-links/${encodeURIComponent(n)}/rotate`,{method:"POST",body:{endActiveSessions:s}})}revokeAccessLink(e,t,n,s=!1){let r=s?"?endActiveSessions=1":"";return this.auth(`/v1/events/${encodeURIComponent(e)}/channels/${encodeURIComponent(t)}/access-links/${encodeURIComponent(n)}${r}`,{method:"DELETE"})}report(e){return this.auth(`/v1/events/${encodeURIComponent(e)}/report`).then(qe)}controlRoom(e,t=15){return this.auth(`/v1/events/${encodeURIComponent(e)}/control-room?window=${t}`).then(Oe)}channelReport(e){return this.auth(`/v1/events/${encodeURIComponent(e)}/channels/report`).then(ze)}createChannelReportLink(e,t,n={}){return this.auth(`/v1/events/${encodeURIComponent(e)}/channels/${encodeURIComponent(t)}/report-links`,{method:"POST",body:n}).then(s=>{let r=S(s);return{...r,link:ie(r.link)}})}channelReportLinks(e,t){return this.auth(`/v1/events/${encodeURIComponent(e)}/channels/${encodeURIComponent(t)}/report-links`).then(n=>{let s=S(n);return{links:Array.isArray(s.links)?s.links.map(ie):[]}})}revokeChannelReportLink(e,t,n){return this.auth(`/v1/events/${encodeURIComponent(e)}/channels/${encodeURIComponent(t)}/report-links/${encodeURIComponent(n)}`,{method:"DELETE"}).then(s=>{let r=S(s);return{...r,link:ie(r.link)}})}log(e,t={}){let n=new URLSearchParams;t.limit!=null&&n.set("limit",String(t.limit)),t.before!=null&&n.set("before",String(t.before));let s=n.toString();return this.auth(`/v1/events/${encodeURIComponent(e)}/log${s?`?${s}`:""}`)}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 ae(i){return i.map((e,t)=>`
|
|
2
|
+
<div class="slm-ch-bucket" style="animation-delay:${Math.min(t,4)*30}ms">
|
|
3
|
+
<span class="ico ${e.kind}" aria-hidden="true">${c(e.icon)}</span>
|
|
4
|
+
<span><b>${e.count.toLocaleString()}</b> ${c(e.text.replace(/^[\d,.\s]+/,""))}
|
|
5
|
+
${e.why?`<span class="why">\u2014 ${c(e.why)}</span>`:""}</span>
|
|
6
|
+
${e.peek?`<span class="peek">${c(e.peek)}</span>`:"<span></span>"}
|
|
7
|
+
</div>`).join("")}var $e=`
|
|
8
|
+
<p class="slm-eyebrow" style="margin-top:18px">On your website</p>
|
|
9
|
+
<p class="slm-hint">There is nothing more to set up on this screen. Your own server mints a short-lived buyer
|
|
10
|
+
access session for this channel with the SeatLayer server SDK and hands it to the widget. A channel name
|
|
11
|
+
on its own never grants access.</p>
|
|
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 c(i){return String(i??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}function Re(i){return new Date(i-new Date(i).getTimezoneOffset()*6e4).toISOString().slice(0,16)}function oe(i,e){let t=i.querySelector(e)?.value.trim()??"",n=Number(t);return t!==""&&Number.isInteger(n)?n:null}function re(i){let e=i.querySelector("[data-ch-lk-url]");if(e)try{let t=document.createRange();t.selectNodeContents(e);let n=window.getSelection();n?.removeAllRanges(),n?.addRange(t)}catch{}}var Ve=8,Ue="#6e7bff",Ne="#b9c0ff",je="#303846",Fe="#4b5669",Ye="#101723";function Ae(i){let e=i.canvas,t=i.layer;if(!e||!t)return;let n=i.host.mapLayer.getBoundingClientRect(),s=Math.max(1,Math.round(n.width)),r=Math.max(1,Math.round(n.height)),o=typeof devicePixelRatio=="number"?Math.min(3,Math.max(1,devicePixelRatio)):1;(e.width!==s*o||e.height!==r*o)&&(e.width=s*o,e.height=r*o);let a=i.resolveCtx();if(!a)return;a.setTransform(o,0,0,o,0,0),a.clearRect(0,0,s,r);let l=i.host.isSeatDetail(),h=Math.max(3,i.host.seatPixelSize()),d=h/2,p=i.view==="preview"?i.previewProjection:null,w=p?new Set(p.available===!1?[]:p.eligible??[]):null,L=new Map,b=!l&&i.host.sections().length>1?new Map:null,u=i.view==="preview"&&l&&h<=15?new Map:null;for(let m of i.host.seats()){let T=i.host.statusOf(m.label)??"free",H=i.allocation.get(m.label)??C;if(H!==C){let y=L.get(H)??{x:0,y:0,n:0};y.x+=m.x,y.y+=m.y,y.n+=1,L.set(H,y)}if(!l){let y=i.host.sectionOfLabel(m.label),v=y?i.host.worldToScreen({x:m.x,y:m.y}):null;if(b&&y&&v){let g=b.get(y.id)??{label:y.label,minX:v.x,minY:v.y,maxX:v.x,maxY:v.y};g.minX=Math.min(g.minX,v.x),g.minY=Math.min(g.minY,v.y),g.maxX=Math.max(g.maxX,v.x),g.maxY=Math.max(g.maxY,v.y),b.set(y.id,g)}continue}if(T!=="free")continue;let B=null,R=null;if(i.view==="preview"?w?.has(m.label)?(B=Ue,R=Ne):(B=je,R=Fe):H!==C&&(B=i.markerFor(H).color,R=Ye),!B)continue;let k=i.host.worldToScreen({x:m.x,y:m.y});if(k&&!(k.x<-h||k.y<-h||k.x>s+h||k.y>r+h)){if(u){let y=i.host.sectionOfLabel(m.label);if(y){let v=u.get(y.id)??{label:y.label,minX:k.x,minY:k.y,maxX:k.x,maxY:k.y};v.minX=Math.min(v.minX,k.x),v.minY=Math.min(v.minY,k.y),v.maxX=Math.max(v.maxX,k.x),v.maxY=Math.max(v.maxY,k.y),u.set(y.id,v)}}if(a.fillStyle=B,i.view==="preview"||H!==C){let y=Math.max(2,d+Math.min(1.5,d*.06));a.globalAlpha=1,a.beginPath(),a.arc(k.x,k.y,y,0,Math.PI*2),a.fill(),a.strokeStyle=R??B,a.lineWidth=i.view==="preview"?Math.max(1,Math.min(1.75,h*.13)):Math.max(1,Math.min(1.5,h*.1)),a.stroke(),i.view==="preview"&&w?.has(m.label)&&h>=22&&Xe(a,m.label,k.x,k.y,y)}else a.globalAlpha=.85,a.fillRect(k.x-d,k.y-d,h,h)}}a.globalAlpha=1,u&&Ge(a,u),Ke(b,i),We(L,i,a)}function Xe(i,e,t,n,s){let r=s*1.55,o=Math.min(13,Math.max(7,s*.55)),a=6;for(;o>=a&&(i.font=`800 ${o}px var(--slm-font, system-ui, sans-serif)`,!(i.measureText(e).width<=r));)o-=.5;o<a||(i.fillStyle="#ffffff",i.textAlign="center",i.textBaseline="middle",i.fillText(e,t,n))}function Ke(i,e){let t=e.layer;if(t&&(t.querySelectorAll(".slm-ch-section-target").forEach(n=>n.remove()),!!i))for(let[n,s]of i){let r=s.maxX-s.minX,o=s.maxY-s.minY;if(r<20||o<20)continue;let a=document.createElement("button");a.type="button",a.className="slm-ch-section-target",a.style.left=`${s.minX-8}px`,a.style.top=`${s.minY-8}px`,a.style.width=`${r+16}px`,a.style.height=`${o+16}px`,a.setAttribute("aria-label",`Open ${s.label} seats`),a.addEventListener("click",()=>e.onSectionTargetClick(n)),t.appendChild(a)}}function Ge(i,e){for(let t of e.values()){let n=t.maxX-t.minX,s=t.maxY-t.minY;if(n<52||s<26)continue;let r=(t.minX+t.maxX)/2,o=(t.minY+t.maxY)/2,a=Math.max(11,Math.min(15,s*.16));i.font=`800 ${a}px var(--slm-font, system-ui, sans-serif)`;let l=Math.min(n-8,i.measureText(t.label).width+18),h=a+10;i.fillStyle="rgba(11, 16, 28, .88)",i.fillRect(r-l/2,o-h/2,l,h),i.fillStyle="#f8fafc",i.textAlign="center",i.textBaseline="middle",i.fillText(t.label,r,o)}}function We(i,e,t){let n=e.layer;if(!n||(n.querySelectorAll(".slm-ch-flag").forEach(r=>r.remove()),e.view==="preview"))return;let s=[...i.entries()].sort((r,o)=>o[1].n-r[1].n).slice(0,Ve);for(let[r,o]of s){let a=e.list?.channels.find(p=>p.id===r);if(!a)continue;let l=e.host.worldToScreen({x:o.x/o.n,y:o.y/o.n});if(!l)continue;let h=e.markerFor(r),d=document.createElement("span");d.className="slm-ch-flag",d.style.left=`${l.x}px`,d.style.top=`${l.y}px`,d.innerHTML=`<span class="mk" style="background:${c(h.color)}">${c(h.letter)}</span>${c(a.name)}${a.state==="paused"?" \xB7 Paused":""}`,n.appendChild(d)}}var Qe=`
|
|
14
|
+
.slm{--slm-mo-instant:80ms;--slm-mo-quick:140ms;--slm-mo-base:200ms;--slm-mo-slow:320ms;--slm-mo-ambient:2000ms;
|
|
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
|
+
--slm-mo-spring:cubic-bezier(.34,1.3,.64,1)}
|
|
17
|
+
|
|
18
|
+
/* map overlay: ONE layer, faded in as a whole (never per seat) */
|
|
19
|
+
.slm-ch-layer{position:absolute;inset:0;pointer-events:none;opacity:0;transition:opacity var(--slm-mo-base) var(--slm-mo-out)}
|
|
20
|
+
.slm-ch-layer.on{opacity:1}
|
|
21
|
+
.slm-ch-canvas{position:absolute;inset:0;width:100%;height:100%}
|
|
22
|
+
.slm-ch-flag{position:absolute;display:flex;align-items:center;gap:5px;padding:3px 8px;border-radius:999px;
|
|
23
|
+
background:rgba(14,16,23,.88);border:1px solid var(--slm-line);font-size:10px;font-weight:800;letter-spacing:.04em;
|
|
24
|
+
transform:translate(-50%,-50%);white-space:nowrap}
|
|
25
|
+
.slm-ch-flag .mk{width:14px;height:14px;border-radius:4px;display:grid;place-items:center;font-size:8.5px;font-weight:800;color:#0e1017}
|
|
26
|
+
.slm-ch-section-target{position:absolute;pointer-events:auto;padding:0;border:0;border-radius:8px;background:transparent;cursor:zoom-in}
|
|
27
|
+
.slm-ch-section-target:focus-visible{outline:2px solid var(--slm-accent);outline-offset:-3px;background:color-mix(in srgb,var(--slm-accent) 12%,transparent)}
|
|
28
|
+
|
|
29
|
+
/* preview banner \u2014 raised with the organizer chrome dim, as one transition */
|
|
30
|
+
.slm-ch-banner{position:absolute;left:50%;top:14px;z-index:6;display:flex;align-items:center;gap:9px;padding:8px 14px;
|
|
31
|
+
border-radius:999px;background:rgba(14,16,23,.92);border:1px solid var(--slm-line);font-size:12px;font-weight:700;
|
|
32
|
+
transform:translate(-50%,-8px);opacity:0;pointer-events:none;
|
|
33
|
+
transition:opacity var(--slm-mo-base) var(--slm-mo-out),transform var(--slm-mo-base) var(--slm-mo-out)}
|
|
34
|
+
.slm-ch-banner.on{opacity:1;transform:translate(-50%,0);pointer-events:auto}
|
|
35
|
+
.slm-ch-banner .dot{width:8px;height:8px;border-radius:50%}
|
|
36
|
+
.slm-ch-banner button{color:var(--slm-accent);font-weight:800;font-size:11.5px;min-height:32px}
|
|
37
|
+
.slm.ch-preview .slm-ch-flag{opacity:0;transition:opacity var(--slm-mo-base) var(--slm-mo-out)}
|
|
38
|
+
|
|
39
|
+
/* sticky staged bar */
|
|
40
|
+
.slm-ch-staged{position:absolute;left:12px;right:12px;bottom:12px;z-index:6;display:flex;align-items:center;gap:12px;
|
|
41
|
+
padding:10px 14px;min-height:44px;border-radius:12px;background:rgba(24,27,36,.96);border:1px solid var(--slm-line);
|
|
42
|
+
box-shadow:0 12px 34px rgba(0,0,0,.45);font-size:12.5px;pointer-events:auto;
|
|
43
|
+
transform:translateY(calc(100% + 18px));opacity:0;
|
|
44
|
+
transition:transform var(--slm-mo-slow) var(--slm-mo-out),opacity var(--slm-mo-base) var(--slm-mo-out)}
|
|
45
|
+
.slm-ch-staged.on{transform:none;opacity:1}
|
|
46
|
+
.slm-ch-staged.done{background:rgba(31,122,77,.96);border-color:#1f7a4d}
|
|
47
|
+
.slm-ch-staged.shake{animation:slm-ch-shake var(--slm-mo-slow) var(--slm-mo-in-out) 2}
|
|
48
|
+
.slm-ch-staged b{font-variant-numeric:tabular-nums}
|
|
49
|
+
.slm-ch-staged .grow{flex:1}
|
|
50
|
+
.slm-ch-staged .go{padding:9px 16px;min-height:44px;display:inline-flex;align-items:center;border-radius:9px;
|
|
51
|
+
background:#f4b740;color:#1a1200;font-weight:800;font-size:12.5px}
|
|
52
|
+
.slm-ch-staged .go:disabled{opacity:.48;cursor:not-allowed}
|
|
53
|
+
.slm-ch-staged .drop{color:var(--slm-muted);font-weight:700;font-size:11.5px;min-height:44px;padding-inline:8px}
|
|
54
|
+
.slm-ch-tick{display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;border-radius:50%;
|
|
55
|
+
background:#fff;color:#1f7a4d;font-weight:900;font-size:11px;animation:slm-ch-tick var(--slm-mo-base) var(--slm-mo-spring)}
|
|
56
|
+
@keyframes slm-ch-shake{0%,100%{transform:none}25%{transform:translateX(-4px)}75%{transform:translateX(4px)}}
|
|
57
|
+
@keyframes slm-ch-tick{from{transform:scale(.4);opacity:0}to{transform:scale(1);opacity:1}}
|
|
58
|
+
|
|
59
|
+
/* rail */
|
|
60
|
+
.slm-ch-viewseg{display:flex;gap:3px;padding:3px;border:1px solid var(--slm-line);border-radius:9px;
|
|
61
|
+
background:var(--slm-surface);margin-bottom:12px}
|
|
62
|
+
.slm-ch-viewseg button{flex:1;padding:6px 8px;min-height:34px;border-radius:7px;font-size:11px;font-weight:800;color:var(--slm-muted)}
|
|
63
|
+
.slm-ch-viewseg button.on{background:var(--slm-accent);color:var(--slm-accent-ink)}
|
|
64
|
+
.slm-ch-viewseg button:disabled{opacity:.5;cursor:not-allowed}
|
|
65
|
+
.slm-ch-mapnav{margin:-2px 0 12px;padding:10px;border:1px solid var(--slm-line);border-radius:10px;background:var(--slm-surface)}
|
|
66
|
+
.slm-ch-mapnav-head{display:flex;align-items:center;justify-content:space-between;gap:8px;font-size:11px;font-weight:800;letter-spacing:.08em;text-transform:uppercase;color:var(--slm-muted)}
|
|
67
|
+
.slm-ch-mapnav-head button{color:var(--slm-accent);font-size:11px;font-weight:800;letter-spacing:0;text-transform:none;min-height:30px}
|
|
68
|
+
.slm-ch-mapnav .slm-ch-viewseg{margin:8px 0 5px}
|
|
69
|
+
.slm-ch-mapnav p{margin:0;font-size:11px;line-height:1.45;color:var(--slm-muted)}
|
|
70
|
+
.slm-ch-list{display:flex;flex-direction:column;gap:8px;margin-bottom:12px}
|
|
71
|
+
.slm-ch-row{padding:10px 11px;border:1px solid var(--slm-line);border-radius:10px;background:var(--slm-surface);
|
|
72
|
+
text-align:left;width:100%;display:block;transition:border-color var(--slm-mo-quick) var(--slm-mo-out)}
|
|
73
|
+
.slm-ch-row:hover{border-color:var(--slm-muted)}
|
|
74
|
+
.slm-ch-row.on{border-color:var(--slm-accent);box-shadow:0 0 0 1px color-mix(in srgb,var(--slm-accent) 40%,transparent)}
|
|
75
|
+
.slm-ch-row.public{background:linear-gradient(100deg,rgba(244,183,64,.09),var(--slm-surface) 60%)}
|
|
76
|
+
.slm-ch-row.archived{opacity:.68}
|
|
77
|
+
.slm-ch-head{display:flex;align-items:center;gap:8px}
|
|
78
|
+
.slm-ch-mk{width:20px;height:20px;border-radius:6px;display:grid;place-items:center;font-size:10px;font-weight:800;
|
|
79
|
+
color:#0e1017;flex:none}
|
|
80
|
+
.slm-ch-mk.dim{opacity:.55}
|
|
81
|
+
.slm-ch-name{flex:1;min-width:0;font-size:13px;font-weight:800;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
82
|
+
.slm-ch-badge{flex:none;font-size:9px;font-weight:800;letter-spacing:.05em;text-transform:uppercase;padding:2px 7px;border-radius:999px}
|
|
83
|
+
.slm-ch-badge.active{background:rgba(34,160,107,.16);color:#5bd39b}
|
|
84
|
+
.slm-ch-badge.paused,.slm-ch-badge.builtin{background:rgba(244,183,64,.15);color:#f7ca6b}
|
|
85
|
+
.slm-ch-badge.archived{background:rgba(139,148,172,.18);color:#c2c9d8}
|
|
86
|
+
.slm-ch-counts{display:flex;gap:10px;flex-wrap:wrap;margin-top:7px;font-size:11px;color:var(--slm-muted);
|
|
87
|
+
font-variant-numeric:tabular-nums}
|
|
88
|
+
.slm-ch-counts b{color:var(--slm-text);font-weight:800}
|
|
89
|
+
.slm-ch-counts .free b{color:#5bd39b}
|
|
90
|
+
.slm-ch-counts b.bump{animation:slm-ch-bump var(--slm-mo-base) var(--slm-mo-spring)}
|
|
91
|
+
@keyframes slm-ch-bump{0%,100%{transform:none}35%{transform:translateY(-2px) scale(1.08)}}
|
|
92
|
+
.slm-ch-access{margin-top:6px;font-size:10.5px;color:var(--slm-muted)}
|
|
93
|
+
/* A row that opens its channel must READ as pressable, and be one for a keyboard
|
|
94
|
+
too. It is a role="button" div rather than a <button> because the \u22EF control
|
|
95
|
+
lives inside it, and a button inside a button is not valid HTML. */
|
|
96
|
+
.slm-ch-row.open{cursor:pointer}
|
|
97
|
+
.slm-ch-row.open:hover{border-color:var(--slm-accent);background:color-mix(in srgb,var(--slm-accent) 6%,var(--slm-surface))}
|
|
98
|
+
.slm-ch-row.open:focus-visible{outline:2px solid var(--slm-accent);outline-offset:2px}
|
|
99
|
+
.slm-ch-row.open:active{border-color:var(--slm-accent)}
|
|
100
|
+
.slm-ch-more{flex:none;color:var(--slm-muted);font-weight:800;padding:0 4px;min-height:28px;border-radius:6px}
|
|
101
|
+
.slm-ch-more:hover{color:var(--slm-text)}
|
|
102
|
+
.slm-ch-more:focus-visible{outline:2px solid var(--slm-accent);outline-offset:1px;color:var(--slm-text)}
|
|
103
|
+
.slm-ch-menu{display:flex;flex-direction:column;gap:8px}
|
|
104
|
+
.slm-ch-menu .slm-btn{width:100%}
|
|
105
|
+
/* loading is a state, never a flash of empty: same slot, visibly working */
|
|
106
|
+
.slm-ch-busy{display:flex;align-items:center;gap:9px;font-size:12.5px;color:var(--slm-muted);padding:12px 0}
|
|
107
|
+
.slm-ch-busy::before{content:"";width:9px;height:9px;border-radius:50%;background:var(--slm-accent);flex:none;
|
|
108
|
+
animation:slm-ch-pulse var(--slm-mo-ambient) var(--slm-mo-in-out) infinite}
|
|
109
|
+
@keyframes slm-ch-pulse{0%,100%{opacity:.25;transform:scale(.7)}50%{opacity:1;transform:scale(1)}}
|
|
110
|
+
.slm-ch-selsrc{display:flex;flex-direction:column;gap:5px;margin:8px 0 12px}
|
|
111
|
+
.slm-ch-selsrc-row{display:flex;align-items:center;gap:8px;font-size:12px;font-variant-numeric:tabular-nums}
|
|
112
|
+
.slm-ch-selsrc-row .mk{width:15px;height:15px;border-radius:4px;display:grid;place-items:center;font-size:8px;
|
|
113
|
+
font-weight:800;color:#0e1017}
|
|
114
|
+
.slm-ch-selsrc-row b{min-width:30px;text-align:right;font-weight:800}
|
|
115
|
+
.slm-ch-selsrc-row span{color:var(--slm-muted)}
|
|
116
|
+
.slm-ch-selnum.bump{animation:slm-ch-bump var(--slm-mo-base) var(--slm-mo-spring)}
|
|
117
|
+
.slm-ch-row2{display:flex;gap:8px;margin-top:8px}
|
|
118
|
+
.slm-ch-row2 .slm-btn{flex:1;min-width:0}
|
|
119
|
+
/* distribute routes \u2014 four choices, each with the one sentence that explains it.
|
|
120
|
+
The current one is NAMED in its own card, never signalled by colour alone. */
|
|
121
|
+
.slm-ch-dist{display:flex;flex-direction:column;gap:6px;padding:12px;margin-bottom:8px;border:1px solid var(--slm-line);
|
|
122
|
+
border-radius:10px;background:var(--slm-surface)}
|
|
123
|
+
.slm-ch-dist.on{border-color:var(--slm-accent,#8b7cf6)}
|
|
124
|
+
.slm-ch-dist b{font-size:13px;font-weight:800;display:flex;align-items:center;gap:8px;justify-content:space-between}
|
|
125
|
+
.slm-ch-dist b .cur{font-size:10.5px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;
|
|
126
|
+
color:var(--slm-accent,#8b7cf6);white-space:nowrap}
|
|
127
|
+
.slm-ch-dist .why{color:var(--slm-muted);font-size:11.5px;line-height:1.45}
|
|
128
|
+
.slm-ch-dist .slm-btn{width:100%;margin-top:2px}
|
|
129
|
+
.slm-ch-alert{display:flex;align-items:flex-start;gap:9px;padding:11px 13px;border-radius:10px;font-size:12.5px;
|
|
130
|
+
line-height:1.5;margin-bottom:12px}
|
|
131
|
+
.slm-ch-alert.warn{background:rgba(244,183,64,.1);border:1px solid rgba(244,183,64,.4);color:#f4d58a}
|
|
132
|
+
.slm-ch-alert.info{background:rgba(110,123,255,.12);border:1px solid rgba(110,123,255,.44);color:#c5cbff}
|
|
133
|
+
.slm-ch-alert.err{background:rgba(229,72,77,.1);border:1px solid rgba(229,72,77,.45);color:#f1a4a6}
|
|
134
|
+
.slm-ch-alert b{color:#fff}
|
|
135
|
+
.slm-ch-alert button{display:block;margin-top:6px;color:#fff;font-weight:800;min-height:36px}
|
|
136
|
+
.slm-ch-legend{display:flex;flex-direction:column;gap:6px;margin-top:10px}
|
|
137
|
+
.slm-ch-legend .r{display:flex;align-items:center;gap:9px;font-size:12px;color:var(--slm-muted)}
|
|
138
|
+
.slm-ch-legend .sw{width:13px;height:13px;border-radius:3.5px;flex:none}
|
|
139
|
+
.slm-ch-live{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
|
|
140
|
+
|
|
141
|
+
/* dialogs */
|
|
142
|
+
.slm-ch-scrim{position:absolute;inset:0;z-index:12;background:rgba(4,6,12,.62);display:grid;place-items:center;
|
|
143
|
+
padding:18px;animation:slm-ch-fade var(--slm-mo-quick) var(--slm-mo-out)}
|
|
144
|
+
.slm-ch-dialog{width:min(460px,100%);max-height:100%;overflow:auto;overscroll-behavior:contain;background:#12151f;border:1px solid var(--slm-line);
|
|
145
|
+
border-radius:14px;padding:20px;box-shadow:0 24px 70px rgba(0,0,0,.6);
|
|
146
|
+
animation:slm-ch-rise var(--slm-mo-base) var(--slm-mo-out)}
|
|
147
|
+
.slm-ch-dialog h3{margin:0 0 4px;font-size:16px;font-weight:800;letter-spacing:-.01em}
|
|
148
|
+
.slm-ch-dialog .sub{font-size:12.5px;color:var(--slm-muted);line-height:1.5;margin-bottom:14px}
|
|
149
|
+
.slm-ch-dialog .foot{display:flex;gap:8px;margin-top:16px}
|
|
150
|
+
.slm-ch-dialog .foot .slm-btn{flex:1;min-width:0}
|
|
151
|
+
.slm-ch-dialog .foot .quiet{flex:none;padding:10px 14px;min-height:44px;color:var(--slm-muted);font-weight:700;font-size:13px}
|
|
152
|
+
@keyframes slm-ch-fade{from{opacity:0}to{opacity:1}}
|
|
153
|
+
@keyframes slm-ch-rise{from{opacity:0;transform:translateY(10px) scale(.985)}to{opacity:1;transform:none}}
|
|
154
|
+
.slm-ch-bucket{display:grid;grid-template-columns:24px 1fr auto;align-items:center;gap:10px;padding:9px 4px;
|
|
155
|
+
border-top:1px solid var(--slm-line);font-size:12.5px;animation:slm-ch-bucket var(--slm-mo-base) var(--slm-mo-out) both}
|
|
156
|
+
.slm-ch-bucket:first-of-type{border-top:0}
|
|
157
|
+
.slm-ch-bucket .ico{width:20px;height:20px;border-radius:6px;display:grid;place-items:center;font-size:10px;font-weight:800}
|
|
158
|
+
.slm-ch-bucket .ico.add{background:rgba(34,160,107,.18);color:#5bd39b}
|
|
159
|
+
.slm-ch-bucket .ico.move{background:rgba(167,139,250,.18);color:#c4b5fd}
|
|
160
|
+
.slm-ch-bucket .ico.same{background:rgba(139,148,172,.14);color:#aab2c4}
|
|
161
|
+
.slm-ch-bucket .ico.skip{background:rgba(244,183,64,.16);color:#f7ca6b}
|
|
162
|
+
.slm-ch-bucket b{font-variant-numeric:tabular-nums;font-weight:800}
|
|
163
|
+
.slm-ch-bucket .why{color:var(--slm-muted);font-size:11px}
|
|
164
|
+
.slm-ch-bucket .peek{color:var(--slm-muted);font-size:11px;font-variant-numeric:tabular-nums}
|
|
165
|
+
@keyframes slm-ch-bucket{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:none}}
|
|
166
|
+
.slm-ch-secret{display:flex;align-items:center;gap:8px;padding:10px 12px;border:1px dashed rgba(244,183,64,.55);
|
|
167
|
+
border-radius:10px;background:rgba(244,183,64,.06);font-family:ui-monospace,Menlo,monospace;font-size:11px;
|
|
168
|
+
overflow:hidden;white-space:nowrap;text-overflow:ellipsis}
|
|
169
|
+
.slm-ch-err{color:#f1a4a6;font-size:11.5px;margin-top:6px}
|
|
170
|
+
|
|
171
|
+
/* buyer links \u2014 STATUS only; there is no Copy control on this card */
|
|
172
|
+
.slm-ch-link{padding:10px 11px;border:1px solid var(--slm-line);border-radius:10px;background:var(--slm-surface);
|
|
173
|
+
margin-bottom:8px}
|
|
174
|
+
.slm-ch-link .lk-head{display:flex;align-items:center;gap:8px}
|
|
175
|
+
.slm-ch-link .lk-name{flex:1;min-width:0;font-size:12.5px;font-weight:800;overflow:hidden;text-overflow:ellipsis;
|
|
176
|
+
white-space:nowrap}
|
|
177
|
+
.slm-ch-lkrow{display:flex;gap:8px;margin-top:5px;font-size:11px;color:var(--slm-muted)}
|
|
178
|
+
.slm-ch-lkrow .k{flex:none;min-width:104px}
|
|
179
|
+
.slm-ch-lkrow .v{color:var(--slm-text);font-variant-numeric:tabular-nums}
|
|
180
|
+
.slm-ch-meter{height:5px;border-radius:3px;background:rgba(255,255,255,.09);overflow:hidden;margin-top:8px}
|
|
181
|
+
.slm-ch-meter i{display:block;height:100%;background:var(--slm-accent);
|
|
182
|
+
transition:width var(--slm-mo-base) var(--slm-mo-out)}
|
|
183
|
+
.slm-ch-radio{display:flex;gap:9px;align-items:flex-start;padding:11px 12px;border:1px solid var(--slm-line);
|
|
184
|
+
border-radius:10px;margin-top:8px;font-size:12.5px;cursor:pointer;
|
|
185
|
+
transition:border-color var(--slm-mo-quick) var(--slm-mo-out)}
|
|
186
|
+
.slm-ch-radio:hover{border-color:var(--slm-muted)}
|
|
187
|
+
.slm-ch-radio input{flex:none;margin-top:2px}
|
|
188
|
+
.slm-ch-radio b{display:block;font-weight:800;margin-bottom:2px}
|
|
189
|
+
.slm-ch-radio .why{display:block;color:var(--slm-muted);font-size:11.5px;line-height:1.45}
|
|
190
|
+
.slm-ch-seatlist{max-height:44vh;overflow:auto;overscroll-behavior:contain;border:1px solid var(--slm-line);
|
|
191
|
+
border-radius:10px;background:var(--slm-surface);margin-top:10px}
|
|
192
|
+
.slm-ch-seatgroup{padding:8px 10px;border-bottom:1px solid var(--slm-line);display:flex;align-items:center;
|
|
193
|
+
justify-content:space-between;gap:8px;font-size:11px;font-weight:800;color:var(--slm-muted);position:sticky;top:0;
|
|
194
|
+
background:var(--slm-surface)}
|
|
195
|
+
.slm-ch-seatgroup button{color:var(--slm-accent);font-weight:800;font-size:11px;min-height:32px}
|
|
196
|
+
.slm-ch-seatitem{display:flex;width:100%;align-items:center;gap:9px;padding:8px 10px;border-bottom:1px solid var(--slm-line);
|
|
197
|
+
text-align:left;font-size:12px}
|
|
198
|
+
.slm-ch-seatitem .box{width:16px;height:16px;border-radius:4px;border:1px solid var(--slm-muted);display:grid;
|
|
199
|
+
place-items:center;font-size:10px;font-weight:900;color:transparent;flex:none}
|
|
200
|
+
.slm-ch-seatitem[aria-checked="true"] .box{border-color:var(--slm-accent);background:var(--slm-accent);color:var(--slm-accent-ink)}
|
|
201
|
+
.slm-ch-seatitem .meta{margin-left:auto;color:var(--slm-muted);font-size:10.5px}
|
|
202
|
+
|
|
203
|
+
/* scope chooser: whole sections or many rows in one additive pass */
|
|
204
|
+
.slm-ch-scopebar{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:end;gap:10px;margin-top:12px}
|
|
205
|
+
.slm-ch-scopebar label{display:grid;gap:5px;color:var(--slm-muted);font-size:10.5px;font-weight:800}
|
|
206
|
+
.slm-ch-scopebar input{width:100%;min-height:40px;padding:8px 10px;border:1px solid var(--slm-line);border-radius:8px;
|
|
207
|
+
background:var(--slm-surface);color:var(--slm-text);font:inherit}
|
|
208
|
+
.slm-ch-scopesummary{padding-bottom:10px;color:var(--slm-muted);font-size:11px;font-variant-numeric:tabular-nums;white-space:nowrap}
|
|
209
|
+
.slm-ch-scopegroup{position:sticky;top:0;z-index:1;display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;
|
|
210
|
+
gap:6px;padding:6px 8px;border-bottom:1px solid var(--slm-line);background:var(--slm-surface)}
|
|
211
|
+
.slm-ch-groupcheck{display:flex;min-width:0;align-items:center;gap:8px;padding:5px 2px;text-align:left;font-size:11px;font-weight:800}
|
|
212
|
+
.slm-ch-groupcheck .box{width:16px;height:16px;flex:none;display:grid;place-items:center;border:1px solid var(--slm-muted);border-radius:4px;
|
|
213
|
+
color:transparent;font-size:10px}
|
|
214
|
+
.slm-ch-groupcheck[aria-checked="true"] .box,.slm-ch-groupcheck[aria-checked="mixed"] .box{border-color:var(--slm-accent);
|
|
215
|
+
background:var(--slm-accent);color:var(--slm-accent-ink)}
|
|
216
|
+
.slm-ch-groupcheck .meta{min-width:0;margin-left:auto;color:var(--slm-muted);font-size:10.5px;font-weight:600;white-space:nowrap}
|
|
217
|
+
.slm-ch-grouptoggle{min-height:32px;padding:5px 8px;color:var(--slm-accent);font-size:11px;font-weight:800}
|
|
218
|
+
.slm-ch-scopehint{padding:8px 10px;color:var(--slm-muted);font-size:10.5px;border-bottom:1px solid var(--slm-line)}
|
|
219
|
+
.slm-ch-scope-empty{padding:18px 12px;color:var(--slm-muted);font-size:11.5px;text-align:center}
|
|
220
|
+
@media(max-width:560px){.slm-ch-scopebar{grid-template-columns:1fr}.slm-ch-scopesummary{padding-bottom:0}}
|
|
221
|
+
|
|
222
|
+
/* compact: bottom sheet with three detents (\xA713) */
|
|
223
|
+
.slm.compact.ch-sheet .slm-rail{position:absolute;left:0;right:0;bottom:0;z-index:8;border-top:1px solid var(--slm-line);
|
|
224
|
+
border-radius:18px 18px 0 0;background:#12151f;
|
|
225
|
+
transition:height var(--slm-mo-slow) var(--slm-mo-in-out)}
|
|
226
|
+
.slm.compact.ch-sheet.detent-collapsed .slm-rail{height:132px}
|
|
227
|
+
.slm.compact.ch-sheet.detent-medium .slm-rail{height:46%}
|
|
228
|
+
.slm.compact.ch-sheet.detent-full .slm-rail{height:92%}
|
|
229
|
+
.slm.compact.ch-sheet .slm-railscroll{padding:8px 14px calc(12px + env(safe-area-inset-bottom,0px))}
|
|
230
|
+
.slm-ch-grab{display:none}
|
|
231
|
+
.slm.compact.ch-sheet .slm-ch-grab{display:flex;align-items:center;gap:10px;width:100%;padding:6px 0 10px}
|
|
232
|
+
.slm-ch-grabbar{width:42px;height:4px;border-radius:2px;background:rgba(255,255,255,.22);margin:0 auto}
|
|
233
|
+
.slm.compact .slm-ch-staged{bottom:auto;top:8px}
|
|
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
|
+
@media (prefers-reduced-motion:reduce){
|
|
241
|
+
.slm-ch-layer,.slm-ch-banner,.slm-ch-staged,.slm-ch-row,.slm.compact.ch-sheet .slm-rail,
|
|
242
|
+
.slm-ch-meter i,.slm-ch-radio{transition:none!important}
|
|
243
|
+
.slm-ch-staged.shake,.slm-ch-tick,.slm-ch-bucket,.slm-ch-scrim,.slm-ch-dialog,
|
|
244
|
+
.slm-ch-counts b.bump,.slm-ch-selnum.bump{animation:none!important}
|
|
245
|
+
.slm-ch-busy::before{animation:none!important;opacity:1}
|
|
246
|
+
.slm-ch-staged.shake{outline:2px solid #e5484d;outline-offset:2px}
|
|
247
|
+
.slm-ch-staged.done{outline:2px solid #5bd39b;outline-offset:2px}
|
|
248
|
+
}
|
|
249
|
+
`;var Je=3e4,Ie=300,$=5e3,Ee=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=Ie;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})},Je),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,n=()=>t!==this.listSeq;try{let s=await this.host.api.channels(this.host.eventKey,{includeArchived:this.showArchived});if(n())return;if(this.list=s,this.assignmentVersion=s.assignmentVersion,this.loadError=null,this.targetChannelId||(this.targetChannelId=s.channels.find(r=>r.state==="active")?.id??C),this.allocationVersion!==s.assignmentVersion){if(await this.loadAllocation(t),n())return;this.allocationVersion=s.assignmentVersion}if(this.detailChannelId&&await this.loadLinks(this.detailChannelId),n())return;this.loading=!1,this.active&&(this.paintRail(),this.paintOverlay())}catch(s){if(n())return;this.loading=!1,s instanceof f&&s.status===403&&(this.caps={view:!1,manage:!1}),this.loadError=s,e.quiet||this.host.onError(s),this.active&&this.paintRail()}}async loadAllocation(e){let t=new Map,n;for(let s=0;s<200;s+=1){if(e!==void 0&&e!==this.listSeq)return;let r=await this.host.api.channelAllocation(this.host.eventKey,{afterLabel:n,limit:1e3});for(let o of r.allocations)M(o.channelId)||t.set(o.label,o.channelId);if(this.assignmentVersion=r.assignmentVersion,!r.nextAfterLabel)break;n=r.nextAfterLabel}e!==void 0&&e!==this.listSeq||(this.allocation=t)}channelById(e){if(M(e))return{id:C,name:this.list?.publicSale?.name??E,marker:"P",color:null};let t=this.list?.channels.find(n=>n.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(s=>s.id===e)??0),n=this.channelById(e);return de(n??{id:e,name:"?",marker:null,color:null},t)}assignableChannels(){return[{id:C,name:this.list?.publicSale.name??E},...(this.list?.channels??[]).filter(e=>e.state!=="archived").map(e=>({id:e.id,name:e.name}))]}currentPlan(){let e=this.host.selectionLabels(),t=pe({labels:e,targetChannelId:this.targetChannelId,allocation:this.allocation,statusOf:n=>this.host.statusOf(n),nameOf:n=>this.nameOf(n)});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
|
+
<canvas class="slm-ch-canvas" data-ch="canvas" aria-hidden="true"></canvas>
|
|
251
|
+
<div class="slm-ch-banner" data-ch="banner" role="status"></div>
|
|
252
|
+
<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||Ae({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:n=>this.markerFor(n),onSectionTargetClick:n=>{this.focusedSectionId=n,this.host.focusSection(n),this.paintRail()}})}setStaged(e,t=""){let n=this.stagedEl;if(n){if(!e){n.classList.remove("on","done","shake"),n.innerHTML="";return}n.innerHTML=e,n.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 n=this.nameOf(this.targetChannelId)??E,s=K(t),r=e.length>$,o=t.skippedHeld.count+t.skippedBooked.count,a=[`<b>${e.length.toLocaleString()}</b> selected`,`<b>+${s.toLocaleString()}</b> to ${c(n)}`];t.alreadyInTarget.count&&a.push(`<b>${t.alreadyInTarget.count.toLocaleString()}</b> already in`),o&&a.push(`<b>${o.toLocaleString()}</b> can't move now`),this.setStaged(`
|
|
254
|
+
<span>${a.join(" \xB7 ")}</span>
|
|
255
|
+
<span class="grow"></span>
|
|
256
|
+
<button type="button" class="drop" data-ch-act="discard">Discard</button>
|
|
257
|
+
<button type="button" class="go" data-ch-act="review"${r?" disabled":""}>
|
|
258
|
+
${r?`Maximum ${$.toLocaleString()} seats`:"Review changes"}
|
|
259
|
+
</button>`),this.stagedEl?.querySelectorAll("[data-ch-act]").forEach(l=>{l.addEventListener("click",()=>{l.dataset.chAct==="discard"?this.host.clearSelection():this.openDialog({kind:"review"})})})}setBanner(e,t="",n){let s=this.bannerEl;if(!s)return;if(this.host.root.classList.toggle("ch-preview",e),!e){s.classList.remove("on"),s.innerHTML="";return}let r=this.previewAudience.length===1?this.markerFor(this.previewAudience[0]):{color:"var(--slm-accent)",letter:""},o=n==null?"":` \xB7 ${n.toLocaleString()} ${n===1?"seat":"seats"} available now`;s.innerHTML=`<span class="dot" style="background:${c(r.color)}"></span>
|
|
260
|
+
Previewing buyer access \xB7 ${c(t)}${o} \xB7 read-only
|
|
261
|
+
<button type="button" data-ch-act="exit-preview">Exit preview</button>`,s.classList.add("on"),s.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(n=>n.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(n=>this.nameOf(n)??E).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(M)}),this.previewState="ready";let n=this.previewProjection.available===!1?void 0:this.previewProjection.counts?.eligible??this.previewProjection.eligible?.length;this.setBanner(!0,t,n)}catch(n){let s=n instanceof f?n.status:0;this.previewState=s===404||s===405||s===501?"unsupported":"error",this.previewProjection=null,this.previewState==="error"&&this.host.onError(n)}this.active&&(this.paintRail(),this.paintOverlay())}setRailHtml(e){if(e===this.railHtml)return!1;let t=this.host.rail,n=t.scrollTop;return t.innerHTML=e,this.railHtml=e,n&&(t.scrollTop=n),!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
|
+
<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
|
+
<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
|
+
<div class="slm-ch-alert err" role="alert" data-ch-state="list-error"><span>\u26A0</span>
|
|
265
|
+
<span><b>Couldn't load sales channels.</b> This event may well have channels \u2014 we could not read them.
|
|
266
|
+
Everything else on this event still works.
|
|
267
|
+
<button type="button" data-ch-act="retry">Try again</button></span></div>`)&&e.querySelector('[data-ch-act="retry"]')?.addEventListener("click",()=>{this.refresh()});return}let t=this.host.selectionLabels(),n=this.host.isCompact()?'<div class="slm-ch-grab"><span class="slm-ch-grabbar"></span></div>':"",s=this.viewSegmentHtml(),r=this.view==="preview"?this.previewRailHtml():this.detailChannelId?this.detailRailHtml(this.detailChannelId):t.length&&this.caps.manage?this.selectionRailHtml(t):this.listRailHtml();this.setRailHtml(`${n}${s}${r}`)&&this.wireRail(),this.paintStagedBar()}viewSegmentHtml(){let e=this.view==="inspect"?" on":"",t=this.view==="preview"?" on":"";return`<div class="slm-ch-viewseg" role="group" aria-label="Channels view">
|
|
268
|
+
<button type="button" class="${e.trim()}" data-ch-view="inspect"
|
|
269
|
+
aria-pressed="${this.view==="inspect"}">Inspect allocation</button>
|
|
270
|
+
<button type="button" class="${t.trim()}" data-ch-view="preview"
|
|
271
|
+
aria-pressed="${this.view==="preview"}">Preview buyer access</button>
|
|
272
|
+
</div>${this.mapNavigationHtml()}`}mapNavigationHtml(){if(this.host.sections().length<2)return"";let e=this.focusedSectionId?this.host.sections().find(r=>r.id===this.focusedSectionId)?.label??"section":null,t=this.mapIntent==="pan"?" on":"",n=this.mapIntent==="assign"?" on":"",s=this.view==="inspect"&&this.caps.manage?`<div class="slm-ch-viewseg" role="group" aria-label="Map interaction">
|
|
273
|
+
<button type="button" class="${t.trim()}" data-ch-map="pan" aria-pressed="${this.mapIntent==="pan"}">Pan map</button>
|
|
274
|
+
<button type="button" class="${n.trim()}" data-ch-map="assign" aria-pressed="${this.mapIntent==="assign"}">Assign seats</button>
|
|
275
|
+
</div>
|
|
276
|
+
<p>${this.mapIntent==="pan"?"Drag to explore. Click a section to open its seats.":"Drag across seats to select them for allocation."}</p>`:"<p>Drag to explore. Click a section to open its seats.</p>";return`<div class="slm-ch-mapnav">
|
|
277
|
+
<div class="slm-ch-mapnav-head"><span>${e?`Viewing ${c(e)}`:"Section overview"}</span>
|
|
278
|
+
<button type="button" data-ch-act="sections">All sections</button></div>
|
|
279
|
+
${s}
|
|
280
|
+
</div>`}countsHtml(e,t){let n=(s,r,o,a="")=>{let l=this.lastCounts.get(`${t}:${s}`),h=l!=null&&l!==r?" bump":"";return this.lastCounts.set(`${t}:${s}`,r),`<span class="${a}"><b class="${h.trim()}">${r.toLocaleString()}</b> ${o}</span>`};return`<span class="slm-ch-counts">
|
|
281
|
+
${n("allocated",e.allocated,"allocated")}
|
|
282
|
+
${n("free",e.free,"free","free")}
|
|
283
|
+
${n("booked",e.booked,"sold")}
|
|
284
|
+
${e.held?n("held",e.held,"held"):""}
|
|
285
|
+
</span>`}channelRowHtml(e,t={}){let n=this.markerFor(e.id),s=t.builtin?"builtin":e.state,r=e.state==="paused"||e.state==="archived"?" dim":"",o=!t.builtin&&this.caps.manage&&this.detailChannelId!==e.id,a=`slm-ch-row${t.builtin?" public":""}${e.state==="archived"?" archived":""}${this.detailChannelId===e.id?" on":""}${o?" open":""}`,l=!t.builtin&&this.caps.manage?`<button type="button" class="slm-ch-more" data-ch-menu="${c(e.id)}"
|
|
286
|
+
aria-label="More actions for ${c(e.name)}" aria-haspopup="dialog">\u22EF</button>`:"",h=o?` role="button" tabindex="0" data-ch-open="${c(e.id)}"
|
|
287
|
+
aria-label="Open ${c(e.name)}"`:"";return`<div class="${a}"${h}>
|
|
288
|
+
<span class="slm-ch-head">
|
|
289
|
+
<span class="slm-ch-mk${r}" style="background:${c(n.color)}" aria-hidden="true">${c(n.letter)}</span>
|
|
290
|
+
<span class="slm-ch-name">${c(e.name)}</span>
|
|
291
|
+
<span class="slm-ch-badge ${s}">${c(we(t.builtin?"builtin":e.state))}</span>
|
|
292
|
+
${l}
|
|
293
|
+
</span>
|
|
294
|
+
${this.countsHtml(e.counts,e.id||"public")}
|
|
295
|
+
${t.builtin?"":`<span class="slm-ch-access">${c(ve(e.access))}</span>`}
|
|
296
|
+
</div>`}listRailHtml(){let e=this.list,t=e.channels.filter(l=>l.state==="archived").length,n=e.channels.filter(l=>this.showArchived||l.state!=="archived"),s=[this.channelRowHtml(e.publicSale,{builtin:!0}),...n.map((l,h)=>this.channelRowHtml(l,{index:h}))].join(""),r=n.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>`,o=this.caps.manage?'<button type="button" class="slm-btn ghost" style="width:100%" data-ch-act="create">+ Create channel</button>':"",a=this.caps.manage?"":'<p class="slm-note">You can see how inventory is allocated. Changing it needs channel-management permission.</p>';return`
|
|
297
|
+
<p class="slm-eyebrow">Sales channels</p>
|
|
298
|
+
<p class="slm-hint">Channel colours and names are only visible to organizers, never to buyers.</p>
|
|
299
|
+
<div class="slm-ch-list">${s}</div>
|
|
300
|
+
${r}
|
|
301
|
+
${o}
|
|
302
|
+
${a}
|
|
303
|
+
${this.assignEntryHtml()}
|
|
304
|
+
<p class="slm-note" style="margin-top:10px">
|
|
305
|
+
<button type="button" class="slm-linkbtn" data-ch-act="toggle-archived" aria-pressed="${this.showArchived}"
|
|
306
|
+
style="text-align:left">${this.showArchived?"Hide":"Show"} archived${t?` (${t})`:""}</button>
|
|
307
|
+
</p>`}assignEntryHtml(){return this.caps.manage?this.assignOpen?`<div style="margin-top:8px">${this.assignmentToolsHtml({collapsible:!0})}</div>`:`
|
|
308
|
+
<button type="button" class="slm-btn ghost" style="width:100%;margin-top:8px"
|
|
309
|
+
data-ch-act="assign-open" aria-expanded="false">Assign seats to a channel</button>
|
|
310
|
+
<p class="slm-note">Move whole sections, rows, a dragged area or single seats out of public sale
|
|
311
|
+
and into a channel. Nothing moves until you review and apply.</p>`:""}assignmentToolsHtml(e={}){let t=this.assignableChannels().map(a=>`<option value="${c(a.id)}"${a.id===this.targetChannelId?" selected":""}>${c(a.name)}</option>`).join(""),n=this.host.sections().length?'<button type="button" class="slm-btn ghost" data-ch-act="pick-sections">Sections</button>':"",s=this.assignmentRows().length?'<button type="button" class="slm-btn ghost" data-ch-act="pick-rows">Rows</button>':"",r=this.mapIntent==="assign"?"slm-btn":"slm-btn ghost";return`
|
|
312
|
+
<p class="slm-eyebrow">${e.collapsible?`<button type="button" class="slm-linkbtn" data-ch-act="assign-close"
|
|
313
|
+
aria-expanded="true" style="float:right;font-weight:800">Done</button>`:""}Assign inventory</p>
|
|
314
|
+
<div class="slm-field">
|
|
315
|
+
<label for="slm-ch-target">Assign to</label>
|
|
316
|
+
<select class="slm-select" id="slm-ch-target" data-ch-target>${t}</select>
|
|
317
|
+
</div>
|
|
318
|
+
<p class="slm-eyebrow" style="margin-top:12px">Select by</p>
|
|
319
|
+
<div class="slm-ch-row2" style="margin-top:2px">
|
|
320
|
+
${n}${s}
|
|
321
|
+
<button type="button" class="${r}" data-ch-act="drag-select">Drag box</button>
|
|
322
|
+
</div>
|
|
323
|
+
<div class="slm-ch-row2">
|
|
324
|
+
<button type="button" class="slm-btn ghost" data-ch-act="pick-category">Category</button>
|
|
325
|
+
<button type="button" class="slm-btn ghost" data-ch-act="seatlist">Seat list \u2328</button>
|
|
326
|
+
</div>
|
|
327
|
+
<p class="slm-note" style="margin-bottom:18px">Choose a destination, then add whole sections,
|
|
328
|
+
multiple rows, a dragged area, or individual seats.</p>`}selectionRailHtml(e){let t=ue(e,this.allocation,this.list),n=this.conflict?`<div class="slm-ch-alert err" role="alert"><span>\u26A0</span>
|
|
329
|
+
<span><b>Assignments changed while you were editing.</b> Nothing was applied.
|
|
330
|
+
Your ${e.length.toLocaleString()}-seat selection is kept.
|
|
331
|
+
<button type="button" data-ch-act="refresh-review">Refresh and review \u2192</button></span></div>`:"",s=e.length!==this.lastSelectionCount?" bump":"";this.lastSelectionCount=e.length;let r=e.length>$,o=t.map(a=>{let l=this.markerFor(a.channelId);return`<div class="slm-ch-selsrc-row">
|
|
332
|
+
<span class="mk" style="background:${c(l.color)}" aria-hidden="true">${c(l.letter)}</span>
|
|
333
|
+
<b>${a.count.toLocaleString()}</b><span>${c(a.name)}</span></div>`}).join("");return`
|
|
334
|
+
${n}
|
|
335
|
+
${this.assignmentToolsHtml()}
|
|
336
|
+
<div class="slm-selbar"><span class="slm-selnum slm-ch-selnum${s}">${e.length.toLocaleString()}</span>
|
|
337
|
+
<span class="slm-sellabel">selected</span></div>
|
|
338
|
+
<div class="slm-ch-selsrc" aria-live="polite" aria-label="Selection sources">${o}</div>
|
|
339
|
+
${r?`<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 ${$.toLocaleString()}
|
|
341
|
+
seats, for example by splitting the venue into row groups.</span></div>`:""}
|
|
342
|
+
<p class="slm-note">Changes are staged \u2014 nothing moves until you review and apply.
|
|
343
|
+
Seats in checkout or already sold are never moved.</p>
|
|
344
|
+
<div class="slm-ch-row2">
|
|
345
|
+
<button type="button" class="slm-btn ghost" data-ch-act="discard">Clear selection</button>
|
|
346
|
+
<button type="button" class="slm-btn" data-ch-act="review"${r?" disabled":""}>Review changes</button>
|
|
347
|
+
</div>
|
|
348
|
+
<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
|
+
<button type="button" class="slm-linkbtn" data-ch-act="back" style="text-align:left">\u2039 All channels</button>
|
|
350
|
+
</p>`,n=this.list?.channels.find(o=>o.id===e);if(!n)return`${t}
|
|
351
|
+
<div class="slm-ch-alert warn" role="status" data-ch-state="detail-gone"><span>\u2139</span>
|
|
352
|
+
<span><b>This channel is no longer on this event.</b> It was archived or removed while you had it open.
|
|
353
|
+
${this.showArchived?"":"Archived channels are hidden \u2014 use \u201CShow archived\u201D on the list to see it."}</span></div>`;let s=this.caps.manage?`
|
|
354
|
+
<p class="slm-eyebrow" style="margin-top:18px">Lifecycle</p>
|
|
355
|
+
<div class="slm-ch-row2" style="margin-top:2px">
|
|
356
|
+
<button type="button" class="slm-btn ghost" data-ch-act="rename">Rename</button>
|
|
357
|
+
<button type="button" class="slm-btn ghost" data-ch-act="pause">${n.state==="paused"?"Resume":"Pause"}</button>
|
|
358
|
+
<button type="button" class="slm-btn ghost" data-ch-act="archive">Archive\u2026</button>
|
|
359
|
+
</div>
|
|
360
|
+
<p class="slm-note">Archive returns the allocation to a destination you choose. Nothing is ever deleted silently.</p>`:"",r=this.caps.manage?this.distributeHtml(n):"";return`
|
|
361
|
+
${t}
|
|
362
|
+
<p class="slm-eyebrow">Channel \xB7 ${c(n.name)}</p>
|
|
363
|
+
<div class="slm-ch-list">${this.channelRowHtml(n)}</div>
|
|
364
|
+
${r}
|
|
365
|
+
${s}`}distributeHtml(e){let t=e.access?.intent??"none",n=e.access?.hasActiveGrants===!0,s=this.linksState!=="unsupported",r=this.links.some(ne),o=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.":n?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.",a=(l,h,d,p)=>{let w=l===t;return`<div class="slm-ch-dist${w?" on":""}" data-ch-route="${c(l)}">
|
|
366
|
+
<b>${c(q(l))}${w?'<span class="cur">Current route</span>':""}</b>
|
|
367
|
+
<span class="why">${c(fe(l))}</span>
|
|
368
|
+
${w&&!d?"":`<button type="button" class="slm-btn${p&&!w?"":" ghost"}"
|
|
369
|
+
data-ch-act="${c(h)}">${c(d)}</button>`}
|
|
370
|
+
</div>`};return`
|
|
371
|
+
<p class="slm-eyebrow" style="margin-top:14px">Distribute</p>
|
|
372
|
+
<p class="slm-hint">${c(o)}</p>
|
|
373
|
+
${s?a("hosted_link","link-create",r?"Create another buyer link":"Create buyer link",!0):""}
|
|
374
|
+
${a("server","embed-code",t==="server"?"Get embed code":"Use a website or app",!1)}
|
|
375
|
+
${a("internal","route-internal",t==="internal"?"":"Hand to your staff",!1)}
|
|
376
|
+
${a("none","route-none",t==="none"?"":"Keep as reserve",!1)}
|
|
377
|
+
${this.hostedLinksHtml()}
|
|
378
|
+
${t==="server"?$e:""}`}async loadLinks(e){if(!this.caps.view)return;let t=++this.linksSeq,n=()=>t!==this.linksSeq||this.linksChannelId!==e;this.linksChannelId!==e&&(this.links=[],this.linksChannelId=e,this.linksState="loading");try{let s=await this.host.api.accessLinks(this.host.eventKey,e);if(n())return;this.links=s.links??[],this.linksState="ready"}catch(s){if(n())return;let r=s instanceof f?s.status:0;this.links=[],this.linksState=r===404||r===405||r===501?"unsupported":"error",this.linksState==="error"&&this.host.onError(s)}}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
|
+
<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
|
+
<span><b>Couldn't load this channel's links.</b> This is a failed read, not an empty list \u2014
|
|
381
|
+
any live link is still working.
|
|
382
|
+
<button type="button" data-ch-act="link-reload">Try again</button></span></div>`:this.linksState==="idle"||this.linksState==="loading"&&!this.links.length?`${e}<div class="slm-ch-busy" role="status" data-ch-state="links-loading">Loading buyer links\u2026</div>`:this.links.length?`${e}
|
|
383
|
+
${this.links.map(t=>this.linkCardHtml(t)).join("")}
|
|
384
|
+
<p class="slm-note">A link is shown once, when you create it. SeatLayer keeps only a fingerprint of it, so it
|
|
385
|
+
can never be shown again \u2014 if a link is lost, rotate it and send the fresh one.</p>`:""}linkCardHtml(e){let t=ke(e),n=e.maxRedemptions>0?Math.min(100,Math.round(e.redemptions/e.maxRedemptions*100)):0,s=se(e).map(l=>`<div class="slm-ch-lkrow"><span class="k">${c(l.k)}</span>
|
|
386
|
+
<span class="v">${c(l.v)}</span></div>`).join(""),r=e.activeSessions?`<div class="slm-ch-lkrow"><span class="k">Buyers inside now</span>
|
|
387
|
+
<span class="v">${e.activeSessions.toLocaleString()}</span></div>`:"",o=e.lastRedeemedAt?`<div class="slm-ch-lkrow"><span class="k">Last opened</span>
|
|
388
|
+
<span class="v">${c(new Date(e.lastRedeemedAt).toLocaleString())}</span></div>`:"",a=this.caps.manage&&ne(e)?`<div class="slm-ch-row2">
|
|
389
|
+
<button type="button" class="slm-btn ghost" data-ch-rotate="${c(e.id)}">Rotate</button>
|
|
390
|
+
<button type="button" class="slm-btn ghost" data-ch-revoke="${c(e.id)}">Revoke</button>
|
|
391
|
+
</div>`:"";return`<div class="slm-ch-link">
|
|
392
|
+
<span class="lk-head">
|
|
393
|
+
<span class="lk-name">${c(e.label||"Buyer link")}</span>
|
|
394
|
+
<span class="slm-ch-badge ${t.kind}">${c(t.text)}</span>
|
|
395
|
+
</span>
|
|
396
|
+
<div class="slm-ch-meter" role="img"
|
|
397
|
+
aria-label="${e.redemptions.toLocaleString()} of ${e.maxRedemptions.toLocaleString()} redemptions used">
|
|
398
|
+
<i style="width:${n}%"></i></div>
|
|
399
|
+
${s}${r}${o}
|
|
400
|
+
<div class="slm-ch-lkrow"><span class="k">The URL</span>
|
|
401
|
+
<span class="v">Revealed once at creation \u2014 not recoverable</span></div>
|
|
402
|
+
${a}
|
|
403
|
+
</div>`}previewRailHtml(){let e=[{id:C,name:this.list?.publicSale.name??E},...(this.list?.channels??[]).filter(p=>p.state!=="archived").map(p=>({id:p.id,name:p.name}))],t=this.previewAudience[0]??C,n=e.map(p=>`<option value="${c(p.id)}"${p.id===t?" selected":""}>${c(p.name)}</option>`).join(""),s=this.previewState==="unsupported"?`<div class="slm-ch-alert warn" data-ch-state="preview-unsupported"><span>\u2139</span>
|
|
404
|
+
<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>`:"",r=this.previewState==="loading"?'<div class="slm-ch-busy" role="status" data-ch-state="preview-loading">Asking the server what this audience sees\u2026</div>':"",o=this.previewState==="error"?`<div class="slm-ch-alert err" role="alert" data-ch-state="preview-error"><span>\u26A0</span>
|
|
406
|
+
<span><b>Couldn't load the buyer preview.</b> Nothing is wrong with this audience's seats \u2014
|
|
407
|
+
the simulation itself failed to load.
|
|
408
|
+
<button type="button" data-ch-act="preview-retry">Try again</button></span></div>`:"",a=this.previewProjection?.available===!1?`<div class="slm-ch-alert warn" role="status"><span>\u23F8</span>
|
|
409
|
+
<span><b>This private sale is not available.</b> ${c((this.previewProjection.unavailable??[]).map(p=>`${this.nameOf(p.channelId)??"This channel"} is ${p.state}`).join("; ")||"The audience cannot buy right now")}.
|
|
410
|
+
A buyer arriving with this access sees this message, not these seats.</span></div>`:"",l=this.previewProjection?.counts?.eligible??this.previewProjection?.eligible?.length,h=l!=null&&this.previewProjection?.available!==!1?`<div class="slm-ch-alert info"><span>\u2713</span><span><b>${l.toLocaleString()} ${l===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>`:"",d=M(t)?"":`
|
|
412
|
+
<label class="slm-note" style="display:flex;gap:8px;align-items:center;margin:10px 0">
|
|
413
|
+
<input type="checkbox" data-ch-includepublic ${this.previewIncludePublic?"checked":""} />
|
|
414
|
+
Also include Public sale seats in this grant
|
|
415
|
+
</label>`;return`
|
|
416
|
+
<p class="slm-eyebrow">Preview buyer access</p>
|
|
417
|
+
<div class="slm-field">
|
|
418
|
+
<label for="slm-ch-audience">Audience</label>
|
|
419
|
+
<select class="slm-select" id="slm-ch-audience" data-ch-audience>${n}</select>
|
|
420
|
+
</div>
|
|
421
|
+
${d}
|
|
422
|
+
<p class="slm-hint">This is the same projection the buyer SDK receives for this audience \u2014 not a local
|
|
423
|
+
approximation. It is read-only: clicks open seat details, and no holds are created.</p>
|
|
424
|
+
${r}${o}${s}${a}
|
|
425
|
+
<div class="slm-ch-legend">
|
|
426
|
+
<div class="r"><span class="sw" style="background:#6e7bff"></span> Eligible & free \u2014 buyable by this audience</div>
|
|
427
|
+
<div class="r"><span class="sw" style="background:#3a4051"></span> Unavailable to this audience (one neutral state)</div>
|
|
428
|
+
<div class="r"><span class="sw" style="background:#22a06b"></span> Sold \u2014 same as any buyer sees</div>
|
|
429
|
+
</div>
|
|
430
|
+
${h}`}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(o=>{o.addEventListener("click",()=>this.setView(o.dataset.chView))}),e.querySelectorAll("[data-ch-map]").forEach(o=>{o.addEventListener("click",()=>{this.mapIntent=o.dataset.chMap==="assign"?"assign":"pan",this.mapIntent==="assign"&&(this.assignOpen=!0),this.paintRail(),this.onInteractionChange?.()})}),e.querySelectorAll("[data-ch-open]").forEach(o=>{let a=()=>this.openChannel(o.dataset.chOpen);o.addEventListener("click",a),o.addEventListener("keydown",l=>{l.key!=="Enter"&&l.key!==" "&&l.key!=="Spacebar"||(l.preventDefault(),a())})}),e.querySelectorAll("[data-ch-menu]").forEach(o=>{o.addEventListener("click",a=>{a.stopPropagation(),this.openDialog({kind:"menu",channelId:o.dataset.chMenu})}),o.addEventListener("keydown",a=>{a.stopPropagation()})}),e.querySelectorAll("[data-ch-rotate]").forEach(o=>{o.addEventListener("click",()=>this.openDialog({kind:"linkRotate",channelId:this.detailChannelId,linkId:o.dataset.chRotate}))}),e.querySelectorAll("[data-ch-revoke]").forEach(o=>{o.addEventListener("click",()=>this.openDialog({kind:"linkRevoke",channelId:this.detailChannelId,linkId:o.dataset.chRevoke}))});let t=e.querySelector("[data-ch-target]");t?.addEventListener("change",()=>{this.targetChannelId=t.value,this.conflict=!1,this.paintRail()});let n=e.querySelector("[data-ch-audience]");n?.addEventListener("change",()=>{this.previewAudience=[n.value],this.loadPreview()});let s=e.querySelector("[data-ch-includepublic]");s?.addEventListener("change",()=>{this.previewIncludePublic=s.checked,this.loadPreview()}),e.querySelector(".slm-ch-grab")?.addEventListener("click",()=>this.cycleDetent()),e.querySelectorAll("[data-ch-act]").forEach(o=>{o.addEventListener("click",()=>this.railAction(o.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,n){this.renderScrim(`
|
|
431
|
+
<h3 id="slm-ch-dlg-title">${c(e)}</h3>
|
|
432
|
+
<div class="slm-field">
|
|
433
|
+
<label for="slm-ch-choice">Choose one</label>
|
|
434
|
+
<select class="slm-select" id="slm-ch-choice">
|
|
435
|
+
${t.map(s=>`<option value="${c(s.value)}">${c(s.label)}</option>`).join("")}
|
|
436
|
+
</select>
|
|
437
|
+
</div>
|
|
438
|
+
<div class="foot">
|
|
439
|
+
<button type="button" class="quiet" data-ch-close>Cancel</button>
|
|
440
|
+
<button type="button" class="slm-btn" data-ch-confirm>Select</button>
|
|
441
|
+
</div>`,s=>{s.querySelector("[data-ch-confirm]")?.addEventListener("click",()=>{let o=s.querySelector("#slm-ch-choice")?.value;this.closeDialog(),o&&n(o)})})}renderScopeDialog(e){let t=e.scope==="rows"?"rows":"sections",s=(t==="sections"?this.host.sections().map(a=>({id:a.id,label:a.label,group:"Sections",labels:this.host.labelsInSection(a.id)})):this.assignmentRows().map(a=>({id:a.id,label:a.label,group:a.sectionLabel,labels:a.labels}))).filter(a=>a.labels.length>0),r=new Map;for(let a of s){let l=r.get(a.group)??[];l.push(a),r.set(a.group,l)}if(t==="rows"){let a=[...r.entries()];this.renderScrim(`
|
|
442
|
+
<h3 id="slm-ch-dlg-title">Add multiple rows</h3>
|
|
443
|
+
<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
|
+
<div class="slm-ch-scopebar">
|
|
445
|
+
<label>Find section or row<input type="search" data-ch-scope-search placeholder="e.g. Orchestra or AA"></label>
|
|
446
|
+
<span class="slm-ch-scopesummary" data-ch-scope-summary aria-live="polite">0 rows selected</span>
|
|
447
|
+
</div>
|
|
448
|
+
<p class="slm-ch-scopehint" data-ch-scope-filter>Showing section groups. Expand one or search to see rows.</p>
|
|
449
|
+
<div class="slm-ch-seatlist" data-ch-scope-list></div>
|
|
450
|
+
<p class="slm-ch-err" data-ch-error hidden></p>
|
|
451
|
+
<div class="foot">
|
|
452
|
+
<button type="button" class="quiet" data-ch-close>Cancel</button>
|
|
453
|
+
<button type="button" class="slm-btn" data-ch-add-scope disabled>Add seats</button>
|
|
454
|
+
</div>`,l=>{let h=new Map(s.map(g=>[g.id,g])),d=new Set,p=new Set,w=new Set(this.host.selectionLabels()),L=l.querySelector("[data-ch-add-scope]"),b=l.querySelector("[data-ch-error]"),u=l.querySelector("[data-ch-scope-list]"),m=l.querySelector("[data-ch-scope-search]"),T=l.querySelector("[data-ch-scope-summary]"),H=l.querySelector("[data-ch-scope-filter]"),B=200,R="",k=()=>{let g=new Set(w);for(let P of d)for(let D of h.get(P)?.labels??[])g.add(D);return g},y=()=>{let g=k(),P=g.size-w.size,D=g.size>$;L.disabled=P===0||D,L.textContent=D?`Maximum ${$.toLocaleString()} seats`:`Add ${P.toLocaleString()} seat${P===1?"":"s"}`,b.hidden=!D,b.textContent=D?`That would make ${g.size.toLocaleString()} selected seats. Choose fewer rows or sections.`:"",T.textContent=`${d.size.toLocaleString()} row${d.size===1?"":"s"} \xB7 ${P.toLocaleString()} seat${P===1?"":"s"} added`},v=()=>{let g=[],P=0,D=0;a.forEach(([A,x],V)=>{let O=A.toLocaleLowerCase(),z=R?x.filter(I=>`${O} ${I.label.toLocaleLowerCase()}`.includes(R)):x;if(R&&!z.length)return;P+=z.length;let le=x.every(I=>d.has(I.id)),Te=!le&&x.some(I=>d.has(I.id)),U=!!R||p.has(V),Pe=Math.max(0,B-D),N=U?z.slice(0,Pe):[];D+=N.length;let De=x.reduce((I,Me)=>I+Me.labels.length,0);g.push(`<div class="slm-ch-scopegroup">
|
|
455
|
+
<button type="button" class="slm-ch-groupcheck" role="checkbox" aria-checked="${le?"true":Te?"mixed":"false"}"
|
|
456
|
+
aria-label="Select all ${x.length.toLocaleString()} rows in ${c(A)}" data-ch-scope-group="${V}">
|
|
457
|
+
<span class="box" aria-hidden="true">\u2713</span><span>${c(A)}</span>
|
|
458
|
+
<span class="meta">${x.length.toLocaleString()} rows \xB7 ${De.toLocaleString()} seats</span>
|
|
459
|
+
</button>
|
|
460
|
+
<button type="button" class="slm-ch-grouptoggle" aria-expanded="${U}" aria-label="${U?"Hide":"Show"} rows in ${c(A)}"
|
|
461
|
+
data-ch-scope-toggle="${V}">${U?"Hide":"Show"}</button>
|
|
462
|
+
</div>`),g.push(...N.map(I=>`<button type="button" class="slm-ch-seatitem" role="checkbox"
|
|
463
|
+
aria-checked="${d.has(I.id)}" data-ch-scope-id="${c(I.id)}">
|
|
464
|
+
<span class="box" aria-hidden="true">\u2713</span><span>${c(I.label)}</span>
|
|
465
|
+
<span class="meta">${I.labels.length.toLocaleString()} seat${I.labels.length===1?"":"s"}</span>
|
|
466
|
+
</button>`)),U&&z.length>N.length&&g.push(`<p class="slm-ch-scopehint">${(z.length-N.length).toLocaleString()} more row${z.length-N.length===1?"":"s"}. Search to narrow the list.</p>`)}),u.innerHTML=g.join("")||(R?`<div class="slm-ch-scope-empty" data-ch-state="scope-nomatch">No sections or rows match \u201C${c(R)}\u201D.</div>`:`<div class="slm-ch-scope-empty" data-ch-state="scope-empty">This chart has no rows with selectable seats.
|
|
467
|
+
Use Drag box or the seat list instead.</div>`),H.textContent=R?`Showing ${D.toLocaleString()} of ${P.toLocaleString()} matching rows. Section checkboxes still select every row in that section.`:"Showing section groups. Expand one or search to see rows.",u.querySelectorAll("[data-ch-scope-group]").forEach(A=>A.addEventListener("click",()=>{let x=a[Number(A.dataset.chScopeGroup)]?.[1]??[],V=x.every(O=>d.has(O.id));for(let O of x)V?d.delete(O.id):d.add(O.id);v(),y()})),u.querySelectorAll("[data-ch-scope-toggle]").forEach(A=>A.addEventListener("click",()=>{let x=Number(A.dataset.chScopeToggle);p.has(x)?p.delete(x):p.add(x),v()})),u.querySelectorAll("[data-ch-scope-id]").forEach(A=>A.addEventListener("click",()=>{let x=A.dataset.chScopeId;d.has(x)?d.delete(x):d.add(x),v(),y()}))};m.addEventListener("input",()=>{R=m.value.trim().toLocaleLowerCase(),v(),y()}),L.addEventListener("click",()=>{let g=[...k()];!d.size||g.length>$||(this.closeDialog(),this.host.selectByLabels(g))}),v(),y()});return}let o=[...r.entries()].map(([a,l])=>`
|
|
468
|
+
<div class="slm-ch-seatgroup"><span>${c(a)}</span></div>
|
|
469
|
+
${l.map(h=>`<button type="button" class="slm-ch-seatitem" role="checkbox"
|
|
470
|
+
aria-checked="false" data-ch-scope-id="${c(h.id)}">
|
|
471
|
+
<span class="box" aria-hidden="true">\u2713</span>
|
|
472
|
+
<span>${c(h.label)}</span>
|
|
473
|
+
<span class="meta">${h.labels.length.toLocaleString()} seat${h.labels.length===1?"":"s"}</span>
|
|
474
|
+
</button>`).join("")}`).join("");this.renderScrim(`
|
|
475
|
+
<h3 id="slm-ch-dlg-title">Add whole sections</h3>
|
|
476
|
+
<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">${o||`<div class="slm-ch-scope-empty" data-ch-state="scope-empty">This chart has no sections with selectable seats.
|
|
478
|
+
Use Drag box or the seat list instead.</div>`}</div>
|
|
479
|
+
<p class="slm-ch-err" data-ch-error hidden></p>
|
|
480
|
+
<div class="foot">
|
|
481
|
+
<button type="button" class="quiet" data-ch-close>Cancel</button>
|
|
482
|
+
<button type="button" class="slm-btn" data-ch-add-scope disabled>Add seats</button>
|
|
483
|
+
</div>`,a=>{let l=new Map(s.map(u=>[u.id,u])),h=new Set,d=new Set(this.host.selectionLabels()),p=a.querySelector("[data-ch-add-scope]"),w=a.querySelector("[data-ch-error]"),L=()=>{let u=new Set(d);for(let m of h)for(let T of l.get(m)?.labels??[])u.add(T);return u},b=()=>{let u=L(),m=u.size-d.size,T=u.size>$;p.disabled=m===0||T,p.textContent=T?`Maximum ${$.toLocaleString()} seats`:`Add ${m.toLocaleString()} seat${m===1?"":"s"}`,w.hidden=!T,w.textContent=T?`That would make ${u.size.toLocaleString()} selected seats. Choose fewer rows or sections.`:""};a.querySelectorAll("[data-ch-scope-id]").forEach(u=>{u.addEventListener("click",()=>{let m=u.dataset.chScopeId;h.has(m)?h.delete(m):h.add(m),u.setAttribute("aria-checked",String(h.has(m))),b()})}),p.addEventListener("click",()=>{let u=[...L()];!h.size||u.length>$||(this.closeDialog(),this.host.selectByLabels(u))}),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(o=>o.id===e.channelId),n=e.intentTo;if(!t||!n||!this.caps.manage){this.closeDialog();return}let{headline:s,consequences:r}=ye(e.switchBlocked);this.renderScrim(`
|
|
484
|
+
<h3 id="slm-ch-dlg-title">Change how ${c(t.name)} reaches buyers?</h3>
|
|
485
|
+
<p class="sub">${c(s)}</p>
|
|
486
|
+
<div class="slm-ch-alert warn" role="alert"><span>\u26A0</span><span>
|
|
487
|
+
${r.map(o=>`<span style="display:block;margin-bottom:6px">${c(o)}</span>`).join("")}
|
|
488
|
+
</span></div>
|
|
489
|
+
${e.pendingLink?`<p class="slm-note">Your new buyer link is created as soon as
|
|
490
|
+
the route changes \u2014 you will not have to fill the form in again.</p>`:""}
|
|
491
|
+
<p class="slm-ch-err" data-ch-error ${e.error?"":"hidden"}>${c(e.error??"")}</p>
|
|
492
|
+
<div class="foot">
|
|
493
|
+
<button type="button" class="quiet" data-ch-close>Leave it as it is</button>
|
|
494
|
+
<button type="button" class="slm-btn" data-ch-intent-ack${e.busy?" disabled":""}>
|
|
495
|
+
${e.busy?"Changing\u2026":`Change to "${c(q(n))}"`}</button>
|
|
496
|
+
</div>`,o=>{o.querySelector("[data-ch-intent-ack]")?.addEventListener("click",()=>{this.acknowledgeIntentSwitch(n,e.pendingLink??null)})})}async acknowledgeIntentSwitch(e,t){let n=this.detailChannelId;if(!n){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(n,t)}renderScrim(e,t){let n=this.scrimEl;n||(this.lastFocus=document.activeElement??null),n?.remove();let s=document.createElement("div");s.className="slm-ch-scrim",s.innerHTML=`<div class="slm-ch-dialog" role="dialog" aria-modal="true"
|
|
497
|
+
aria-labelledby="slm-ch-dlg-title" tabindex="-1">${e}</div>`,this.host.root.appendChild(s),this.scrimEl=s;let r=s.firstElementChild;r.querySelectorAll("[data-ch-close]").forEach(a=>{a.addEventListener("click",()=>this.closeDialog())}),s.addEventListener("keydown",a=>{if(a.key==="Escape"){a.stopPropagation(),this.closeDialog();return}if(a.key!=="Tab")return;let l=[...r.querySelectorAll('button:not([disabled]),select,input,textarea,a[href],[tabindex]:not([tabindex="-1"])')];if(!l.length)return;let h=l[0],d=l[l.length-1];a.shiftKey&&document.activeElement===h?(a.preventDefault(),d.focus()):!a.shiftKey&&document.activeElement===d&&(a.preventDefault(),h.focus())}),t(r),(r.querySelector("input,select,button")??r).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(s=>_(s.marker||s.name,"")),n=ee("",t);this.renderScrim(`
|
|
498
|
+
<h3 id="slm-ch-dlg-title">Create channel</h3>
|
|
499
|
+
<p class="sub">A named allocation only the right audience can buy from. You'll pick the seats next.</p>
|
|
500
|
+
<div class="slm-field">
|
|
501
|
+
<label for="slm-ch-name">Name</label>
|
|
502
|
+
<input class="slm-input" id="slm-ch-name" maxlength="80" />
|
|
503
|
+
<p class="slm-note">Shown to your team and in reports \u2014 never to buyers.</p>
|
|
504
|
+
</div>
|
|
505
|
+
<div class="slm-field">
|
|
506
|
+
<label>Marker</label>
|
|
507
|
+
<div style="display:flex;gap:8px;align-items:center">
|
|
508
|
+
<span class="slm-ch-mk" data-ch-marker style="background:${c(n.color)};width:28px;height:28px;font-size:13px">${c(n.letter)}</span>
|
|
509
|
+
<span class="slm-note" style="margin:0">Letter comes from the name; colour is chosen automatically from the next available palette. Buyers never see either.</span>
|
|
510
|
+
</div>
|
|
511
|
+
</div>
|
|
512
|
+
<div class="slm-field">
|
|
513
|
+
<label for="slm-ch-ref">Reference <span style="text-transform:none;font-weight:500">(optional)</span></label>
|
|
514
|
+
<input class="slm-input" id="slm-ch-ref" maxlength="120" placeholder="e.g. travel-agency-a" />
|
|
515
|
+
<p class="slm-note">A stable ID for your own system and webhooks.</p>
|
|
516
|
+
</div>
|
|
517
|
+
<p class="slm-ch-err" data-ch-error ${e.error?"":"hidden"}>${c(e.error??"")}</p>
|
|
518
|
+
<div class="foot">
|
|
519
|
+
<button type="button" class="quiet" data-ch-close>Cancel</button>
|
|
520
|
+
<button type="button" class="slm-btn ghost" data-ch-create="plain">Create without allocating</button>
|
|
521
|
+
<button type="button" class="slm-btn" data-ch-create="allocate">Create and allocate seats</button>
|
|
522
|
+
</div>`,s=>{let r=s.querySelector("#slm-ch-name"),o=s.querySelector("[data-ch-marker]");r.addEventListener("input",()=>{let a=ee(r.value,t);o.textContent=a.letter,o.style.background=a.color}),s.querySelectorAll("[data-ch-create]").forEach(a=>{a.addEventListener("click",()=>{let l=a.dataset.chCreate==="allocate";this.createChannel(r.value,o.textContent??"",o.style.background,s.querySelector("#slm-ch-ref")?.value??"",l)})})})}async createChannel(e,t,n,s,r){let o=e.trim();if(!o){this.showDialogError("Give the channel a name your team will recognise.");return}try{let a=await this.host.api.createChannel(this.host.eventKey,{name:o,marker:_(t,"")||null,color:n||null,externalRef:s.trim()||null});this.closeDialog(),await this.refresh(),this.targetChannelId=a.channel.id,this.detailChannelId=r?null:a.channel.id,this.announce(`Channel ${o} created with 0 seats allocated.`),this.host.toast(r?`${o} created. Select seats on the map to allocate them.`:`${o} created.`,"ok"),this.paintRail()}catch(a){let l=a instanceof f?a.code:void 0;this.showDialogError(l==="channel_name_taken"?"That name is already used on this event. Pick another.":"Couldn't create the channel. Try again."),this.host.onError(a)}}showDialogError(e){let t=this.scrimEl?.querySelector("[data-ch-error]");t&&(t.textContent=e,t.hidden=!1)}renderReviewDialog(e){let{labels:t,buckets:n}=this.currentPlan(),s=e.applied,r=s?s.buckets:n,o=this.nameOf(s?.targetChannelId??this.targetChannelId)??E,a=ge(r,o),l=s?s.applied:K(n),h=!s&&t.length>0&&l===0,d=!s&&t.length>$,p=ae(a),w=s?'<div class="foot"><button type="button" class="slm-btn" data-ch-close>Done</button></div>':`<div class="foot">
|
|
523
|
+
<button type="button" class="quiet" data-ch-close>Back</button>
|
|
524
|
+
<button type="button" class="slm-btn" data-ch-apply ${h||d||e.busy?"disabled":""}>
|
|
525
|
+
${e.busy?"Applying\u2026":`Apply ${l.toLocaleString()} change${l===1?"":"s"}`}
|
|
526
|
+
</button>
|
|
527
|
+
</div>`,L=!s&&me(n)?'<p class="slm-note">Applying moves inventory out of another private channel. That is the line marked above.</p>':"",b=h?`<div class="slm-ch-alert warn"><span>\u2139</span><span>Nothing in this selection can move right now \u2014
|
|
528
|
+
every seat is in a buyer's checkout, already sold, or already in ${c(o)}.</span></div>`:"",u=d?`<div class="slm-ch-alert warn" role="alert"><span>\u2139</span><span><b>Choose fewer seats.</b>
|
|
529
|
+
One Apply can cover at most ${$.toLocaleString()} seats.</span></div>`:"";this.renderScrim(`
|
|
530
|
+
<h3 id="slm-ch-dlg-title">${s?`Moved ${s.applied.toLocaleString()} seat${s.applied===1?"":"s"} to ${c(o)}`:`Move ${t.length.toLocaleString()} selected seat${t.length===1?"":"s"} to ${c(o)}`}</h3>
|
|
531
|
+
<p class="sub">${s?"These are the exact counts the server applied.":"Every selected seat is in exactly one line below."}</p>
|
|
532
|
+
${b}
|
|
533
|
+
${u}
|
|
534
|
+
${p||'<div class="slm-empty">Nothing selected.</div>'}
|
|
535
|
+
${L}
|
|
536
|
+
<p class="slm-ch-err" data-ch-error ${e.error?"":"hidden"}>${c(e.error??"")}</p>
|
|
537
|
+
${w}`,m=>{m.querySelector("[data-ch-apply]")?.addEventListener("click",()=>{this.apply()})}),s&&this.announce(`Applied ${s.applied} change${s.applied===1?"":"s"} to ${o}.`)}async apply(){if(!this.dialog||!this.caps.manage)return;let{labels:e}=this.currentPlan();if(e.length){if(e.length>$){this.showDialogError(`Choose at most ${$.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:M(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)??E,n=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
|
+
<span>Assigned <b>${e.applied.toLocaleString()}</b> seat${e.applied===1?"":"s"} to ${c(t)}
|
|
539
|
+
${n?` \xB7 ${n.toLocaleString()} skipped`:""}</span>
|
|
540
|
+
<span class="grow"></span>
|
|
541
|
+
<button type="button" class="drop" data-ch-applied-details>Details</button>`,"done"),this.stagedEl?.querySelector("[data-ch-applied-details]")?.addEventListener("click",()=>{this.openDialog({kind:"review",applied:e})}),this.announce(`Assigned ${e.applied} seat${e.applied===1?"":"s"} to ${t}${n?`; ${n} skipped`:""}.`),this.stagedDoneTimer&&clearTimeout(this.stagedDoneTimer),this.stagedDoneTimer=setTimeout(()=>this.setStaged(null),5e3)}shakeStaged(){let e=this.stagedEl;!e||!e.classList.contains("on")||(e.classList.remove("shake"),e.offsetWidth,e.classList.add("shake"))}renderMenuDialog(e){let t=this.list?.channels.find(s=>s.id===e.channelId);if(!t||!this.caps.manage){this.closeDialog();return}let n=t.state==="paused";this.renderScrim(`
|
|
542
|
+
<h3 id="slm-ch-dlg-title">${c(t.name)}</h3>
|
|
543
|
+
<p class="sub">Open the channel to allocate seats and hand out buyer access.
|
|
544
|
+
These are the rest of its actions.</p>
|
|
545
|
+
<div class="slm-ch-menu">
|
|
546
|
+
<button type="button" class="slm-btn" data-ch-menu-act="open">Open channel</button>
|
|
547
|
+
<button type="button" class="slm-btn ghost" data-ch-menu-act="rename">Rename</button>
|
|
548
|
+
<button type="button" class="slm-btn ghost" data-ch-menu-act="pause">${n?"Resume selling":"Pause selling"}</button>
|
|
549
|
+
<button type="button" class="slm-btn ghost" data-ch-menu-act="archive">Archive\u2026</button>
|
|
550
|
+
</div>
|
|
551
|
+
<p class="slm-note">Pausing stops new buyer access; checkouts already running can finish.
|
|
552
|
+
Archiving closes the channel for good and returns its free seats to a destination you choose.
|
|
553
|
+
Nothing is ever deleted silently.</p>
|
|
554
|
+
<div class="foot"><button type="button" class="quiet" data-ch-close>Cancel</button></div>`,s=>{s.querySelectorAll("[data-ch-menu-act]").forEach(r=>{r.addEventListener("click",()=>{let o=r.dataset.chMenuAct;if(o==="open"){this.closeDialog(),this.openChannel(t.id);return}if(o==="rename"){this.openDialog({kind:"rename",channelId:t.id});return}if(o==="archive"){this.openDialog({kind:"archive",channelId:t.id});return}this.closeDialog(),this.togglePause(t.id)})})})}renderRenameDialog(e){let t=this.list?.channels.find(n=>n.id===e.channelId);if(!t){this.closeDialog();return}this.renderScrim(`
|
|
555
|
+
<h3 id="slm-ch-dlg-title">Rename ${c(t.name)}</h3>
|
|
556
|
+
<p class="sub">Only your team and your reports see this name.</p>
|
|
557
|
+
<div class="slm-field">
|
|
558
|
+
<label for="slm-ch-newname">Name</label>
|
|
559
|
+
<input class="slm-input" id="slm-ch-newname" maxlength="80" value="${c(t.name)}" />
|
|
560
|
+
</div>
|
|
561
|
+
<p class="slm-ch-err" data-ch-error ${e.error?"":"hidden"}>${c(e.error??"")}</p>
|
|
562
|
+
<div class="foot">
|
|
563
|
+
<button type="button" class="quiet" data-ch-close>Cancel</button>
|
|
564
|
+
<button type="button" class="slm-btn" data-ch-rename>Save name</button>
|
|
565
|
+
</div>`,n=>{n.querySelector("[data-ch-rename]")?.addEventListener("click",()=>{let s=n.querySelector("#slm-ch-newname")?.value??"";if(!s.trim()){this.showDialogError("A channel needs a name.");return}this.host.api.renameChannel(this.host.eventKey,t.id,s.trim()).then(()=>(this.closeDialog(),this.refresh())).catch(r=>{this.showDialogError(r instanceof f&&r.code==="channel_name_taken"?"That name is already used on this event.":"Couldn't rename the channel."),this.host.onError(r)})})})}async chooseWebsiteIntegration(){this.detailChannelId&&await this.setAccessIntent("server")}async setAccessIntent(e,t={}){let n=this.detailChannelId;if(!n||!this.caps.manage)return!1;try{let s=await this.host.api.setChannelAccessIntent(this.host.eventKey,n,e,t);return await this.refresh(),this.host.toast(this.intentSavedCopy(e,s?.intentSwitch),"ok"),!0}catch(s){return s instanceof f&&s.code==="channel_intent_switch_blocked"?(this.openDialog({kind:"intentSwitch",channelId:n,intentTo:e,switchBlocked:s.details}),!1):s instanceof f&&s.code==="channel_access_intent_forbids"?(this.host.toast(G(s.details),"err"),!1):(this.host.toast("Couldn't change how this channel reaches buyers.","err"),this.host.onError(s),!1)}}intentSavedCopy(e,t){let n=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 n;let s=t.closedLinks?` ${t.closedLinks.toLocaleString()} buyer link${t.closedLinks===1?"":"s"} closed.`:"",r=t.keptSessions?` ${t.keptSessions.toLocaleString()} buyer${t.keptSessions===1?"":"s"} already in a checkout can still finish.`:"";return`${n}${s}${r}`}async togglePause(e=this.detailChannelId){let t=this.list?.channels.find(s=>s.id===e);if(!t||!this.caps.manage)return;let n=t.state!=="paused";try{await this.host.api.setChannelPaused(this.host.eventKey,t.id,n),await this.refresh(),this.host.toast(n?`${t.name} paused. Existing checkouts can finish; no new buyer access is issued.`:`${t.name} resumed.`,"ok")}catch(s){this.host.toast("Couldn't change that channel.","err"),this.host.onError(s)}}renderArchiveDialog(e){let t=this.list?.channels.find(a=>a.id===e.channelId);if(!t){this.closeDialog();return}let n=e.archiveBlocked??null,s=!n&&t.counts.held>0,r=this.assignableChannels().filter(a=>a.id!==t.id),o=n?`<div class="slm-ch-alert warn" role="alert"><span>\u23F3</span>
|
|
566
|
+
<span><b>${(n.heldUnits??n.activeHolds??0).toLocaleString()} seats are in a buyer's checkout right now.</b>
|
|
567
|
+
Archive is unavailable while seats are held \u2014 try again ${c(be(n))}.</span></div>`:s?`<div class="slm-ch-alert warn"><span>\u23F3</span>
|
|
568
|
+
<span>${t.counts.held.toLocaleString()} seats are in a buyer's checkout right now.
|
|
569
|
+
Archive is refused while any seat is held \u2014 you can try, and we'll tell you when to come back.</span></div>`:"";this.renderScrim(`
|
|
570
|
+
<h3 id="slm-ch-dlg-title">Archive ${c(t.name)}</h3>
|
|
571
|
+
<p class="sub">The channel closes for good. Its seats move to a destination you choose;
|
|
572
|
+
sales history keeps its attribution.</p>
|
|
573
|
+
${o}
|
|
574
|
+
<div class="slm-field">
|
|
575
|
+
<label for="slm-ch-dest">Move the ${t.counts.free.toLocaleString()} remaining free seats to</label>
|
|
576
|
+
<select class="slm-select" id="slm-ch-dest">
|
|
577
|
+
${r.map(a=>`<option value="${c(a.id)}">${c(a.name)}</option>`).join("")}
|
|
578
|
+
</select>
|
|
579
|
+
</div>
|
|
580
|
+
<p class="slm-note">${t.counts.booked.toLocaleString()} sold seats keep "${c(t.name)}" on their sale
|
|
581
|
+
record. If one is cancelled later it returns to the destination above. Any buyer access for this channel
|
|
582
|
+
stops working.</p>
|
|
583
|
+
<p class="slm-ch-err" data-ch-error ${e.error?"":"hidden"}>${c(e.error??"")}</p>
|
|
584
|
+
<div class="foot">
|
|
585
|
+
<button type="button" class="quiet" data-ch-close>Cancel</button>
|
|
586
|
+
<button type="button" class="slm-btn danger" data-ch-archive ${n?"disabled":""}>Archive channel</button>
|
|
587
|
+
</div>`,a=>{a.querySelector("[data-ch-archive]")?.addEventListener("click",()=>{let l=a.querySelector("#slm-ch-dest")?.value??"";this.archive(t.id,l||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(n){if(n instanceof f&&n.status===409&&n.code==="channel_archive_blocked_by_holds"){this.dialog={kind:"archive",channelId:e,archiveBlocked:n.details??{}},this.renderDialog();return}this.showDialogError("Couldn't archive that channel. Try again."),this.host.onError(n)}}renderSeatListDialog(){let e=new Set(this.host.selectionLabels()),t=new Map,n=0;for(let r of this.host.seats()){if(n+=1,n>this.seatListLimit)break;let o=this.host.sectionOfLabel(r.label),a=o?.id??"",l=t.get(a)??{label:o?.label??"Other seats",seats:[]};l.seats.push({label:r.label,status:this.host.statusOf(r.label)??"free"}),t.set(a,l)}let s=[...t.entries()].map(([r,o])=>`
|
|
588
|
+
<div class="slm-ch-seatgroup">
|
|
589
|
+
<span>${c(o.label)}</span>
|
|
590
|
+
${r?`<button type="button" data-ch-section="${c(r)}">Select section</button>`:""}
|
|
591
|
+
</div>
|
|
592
|
+
${o.seats.map(a=>{let l=this.allocation.get(a.label)??C,h=this.nameOf(l)??E;return`<button type="button" class="slm-ch-seatitem" role="checkbox"
|
|
593
|
+
aria-checked="${e.has(a.label)}" data-ch-seat="${c(a.label)}">
|
|
594
|
+
<span class="box" aria-hidden="true">\u2713</span>
|
|
595
|
+
<span>${c(a.label)}</span>
|
|
596
|
+
<span class="meta">${c(h)} \xB7 ${c(a.status)}</span>
|
|
597
|
+
</button>`}).join("")}`).join("");this.renderScrim(`
|
|
598
|
+
<h3 id="slm-ch-dlg-title">Seat list</h3>
|
|
599
|
+
<p class="sub">The same selection as the map, with checkboxes. Space or Enter toggles a seat.</p>
|
|
600
|
+
<div class="slm-ch-seatlist">${s||'<div class="slm-empty">No seats on this chart.</div>'}</div>
|
|
601
|
+
${n>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>`,r=>{r.querySelectorAll("[data-ch-seat]").forEach(o=>{o.addEventListener("click",()=>{let a=o.dataset.chSeat,l=new Set(this.host.selectionLabels());l.has(a)?l.delete(a):l.add(a),this.host.clearSelection(),l.size&&this.host.selectByLabels([...l]),this.renderSeatListDialog()})}),r.querySelectorAll("[data-ch-section]").forEach(o=>{o.addEventListener("click",()=>{this.host.selectSection(o.dataset.chSection),this.renderSeatListDialog()})}),r.querySelector("[data-ch-more]")?.addEventListener("click",()=>{this.seatListLimit+=Ie,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(n=>n.id===e.channelId);if(!t||!this.caps.manage){this.closeDialog();return}this.renderScrim(`
|
|
603
|
+
<h3 id="slm-ch-dlg-title">Create a buyer link for ${c(t.name)}</h3>
|
|
604
|
+
<p class="sub">Anyone who opens the link can buy from this channel's allocation \u2014 and only from it.
|
|
605
|
+
You'll see the link once, right after you create it.</p>
|
|
606
|
+
<div class="slm-field">
|
|
607
|
+
<label for="slm-ch-lk-label">Label <span style="text-transform:none;font-weight:500">(optional)</span></label>
|
|
608
|
+
<input class="slm-input" id="slm-ch-lk-label" maxlength="80" placeholder="e.g. VIP list Nov 14" />
|
|
609
|
+
<p class="slm-note">So you can tell your links apart later. Buyers never see it.</p>
|
|
610
|
+
</div>
|
|
611
|
+
<div class="slm-field">
|
|
612
|
+
<label for="slm-ch-lk-expiry">Stops working</label>
|
|
613
|
+
<select class="slm-select" id="slm-ch-lk-expiry" data-ch-lk-expiry>
|
|
614
|
+
<option value="event" selected>When the event starts</option>
|
|
615
|
+
<option value="custom">On a date I choose</option>
|
|
616
|
+
</select>
|
|
617
|
+
</div>
|
|
618
|
+
<div class="slm-field" data-ch-lk-when-field hidden>
|
|
619
|
+
<label for="slm-ch-lk-when">Date and time</label>
|
|
620
|
+
<input class="slm-input" type="datetime-local" id="slm-ch-lk-when" />
|
|
621
|
+
</div>
|
|
622
|
+
<div class="slm-field">
|
|
623
|
+
<label for="slm-ch-lk-redemptions">How many people can use it</label>
|
|
624
|
+
<input class="slm-input" type="number" id="slm-ch-lk-redemptions" inputmode="numeric"
|
|
625
|
+
value="${te.maxRedemptions}" />
|
|
626
|
+
<p class="slm-note">Each buyer who opens the link uses one.</p>
|
|
627
|
+
</div>
|
|
628
|
+
<div class="slm-field">
|
|
629
|
+
<label for="slm-ch-lk-quantity">Seats per buyer</label>
|
|
630
|
+
<input class="slm-input" type="number" id="slm-ch-lk-quantity" inputmode="numeric"
|
|
631
|
+
value="${te.maxQuantity}" />
|
|
632
|
+
</div>
|
|
633
|
+
<label class="slm-note" style="display:flex;gap:8px;align-items:center;margin:2px 0 6px">
|
|
634
|
+
<input type="checkbox" id="slm-ch-lk-public" />
|
|
635
|
+
Also let this link buy Public sale seats
|
|
636
|
+
</label>
|
|
637
|
+
<p class="slm-ch-err" data-ch-error ${e.error?"":"hidden"}>${c(e.error??"")}</p>
|
|
638
|
+
<div class="foot">
|
|
639
|
+
<button type="button" class="quiet" data-ch-close>Cancel</button>
|
|
640
|
+
<button type="button" class="slm-btn" data-ch-lk-create>Create link</button>
|
|
641
|
+
</div>`,n=>{let s=n.querySelector("[data-ch-lk-expiry]"),r=n.querySelector("[data-ch-lk-when-field]"),o=n.querySelector("#slm-ch-lk-when");s.addEventListener("change",()=>{let a=s.value==="custom";r.hidden=!a,a&&!o.value&&(o.value=Re(Date.now()+7*864e5))}),n.querySelector("[data-ch-lk-create]")?.addEventListener("click",()=>{let a=oe(n,"#slm-ch-lk-redemptions"),l=oe(n,"#slm-ch-lk-quantity");if(a==null||l==null){this.showDialogError("Those two settings need to be whole numbers.");return}let h;if(s.value==="custom"&&(h=Date.parse(o.value),!Number.isFinite(h))){this.showDialogError("Pick the date and time the link should stop working.");return}this.createLink(t.id,{label:n.querySelector("#slm-ch-lk-label")?.value.trim()||null,includePublic:n.querySelector("#slm-ch-lk-public")?.checked??!1,...h===void 0?{}:{expiresAt:h},maxRedemptions:a,maxQuantity:l})})})}async createLink(e,t){await this.ensureHostedLinkRoute(e,t)&&await this.mintLink(e,t)}async mintLink(e,t){try{let n=await this.host.api.createAccessLink(this.host.eventKey,e,t);this.revealLink(n,{channelId:e}),await this.reloadAfterLinkChange(e)}catch(n){this.showDialogError(j(n instanceof f?n:void 0)),n instanceof f||this.host.onError(n)}}async ensureHostedLinkRoute(e,t){if((this.list?.channels.find(s=>s.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(s){return s instanceof f&&s.code==="channel_intent_switch_blocked"?(this.openDialog({kind:"intentSwitch",channelId:e,intentTo:"hosted_link",switchBlocked:s.details,pendingLink:t}),!1):(this.showDialogError(s instanceof f&&s.code==="channel_access_intent_forbids"?G(s.details):"Couldn't set this channel up for buyer links. Try again."),s instanceof f||this.host.onError(s),!1)}}revealLink(e,t){let n=e.url;this.dialog=null;let s=t.rotated?`<div class="slm-ch-alert warn" role="status"><span>\u26A0</span><span>
|
|
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>`:"",r=se(e.link).map(o=>`<div class="slm-ch-lkrow"><span class="k">${c(o.k)}</span>
|
|
643
|
+
<span class="v">${c(o.v)}</span></div>`).join("");this.renderScrim(`
|
|
644
|
+
<h3 id="slm-ch-dlg-title">Copy this link now</h3>
|
|
645
|
+
<p class="sub">This is the only time SeatLayer can show it. We keep just a fingerprint, so it cannot be
|
|
646
|
+
shown again \u2014 if it is lost, rotate the link for a fresh one.</p>
|
|
647
|
+
${s}
|
|
648
|
+
<div class="slm-ch-secret" data-ch-lk-url>${c(n)}</div>
|
|
649
|
+
<div class="slm-ch-row2" style="margin-top:8px">
|
|
650
|
+
<button type="button" class="slm-btn" data-ch-lk-copy>Copy link</button>
|
|
651
|
+
</div>
|
|
652
|
+
<div class="slm-ch-alert warn" style="margin-top:12px"><span>\u26A0</span>
|
|
653
|
+
<span>Anyone who opens this link can buy from this allocation. Send it only to the people it is meant
|
|
654
|
+
for \u2014 forwarding it hands on the same access, and SeatLayer cannot tell the difference.</span></div>
|
|
655
|
+
<p class="slm-eyebrow" style="margin-top:14px">What this link allows</p>
|
|
656
|
+
${r}
|
|
657
|
+
<div class="foot">
|
|
658
|
+
<button type="button" class="slm-btn" data-ch-close data-ch-lk-done>I've copied it</button>
|
|
659
|
+
</div>`,o=>{let a=o.querySelector("[data-ch-lk-copy]");a?.addEventListener("click",()=>{let l=()=>{a.textContent="Copied",this.announce("Buyer link copied.")},h=typeof navigator>"u"?null:navigator.clipboard;if(h?.writeText){h.writeText(n).then(l,()=>re(o));return}re(o)})}),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 n=t.activeSessions??0,s=n?`<div class="slm-ch-alert warn"><span>\u26A0</span>
|
|
660
|
+
<span><b>${n.toLocaleString()} buyer${n===1?"":"s"}</b> got in with the current link
|
|
661
|
+
and still ${n===1?"has":"have"} active access.</span></div>`:"";this.renderScrim(`
|
|
662
|
+
<h3 id="slm-ch-dlg-title">Rotate the ${c(t.label||"buyer")} link?</h3>
|
|
663
|
+
<p class="sub">The current link stops opening immediately and cannot be restored. You will get a new
|
|
664
|
+
link to copy \u2014 shown once.</p>
|
|
665
|
+
${s}
|
|
666
|
+
<label class="slm-ch-radio">
|
|
667
|
+
<input type="radio" name="slm-ch-rot" value="keep" data-ch-rot />
|
|
668
|
+
<span><b>Let them finish</b><span class="why">Access already handed out expires on its own; seats in
|
|
669
|
+
checkout are untouched. Every new visit needs the new link.</span></span>
|
|
670
|
+
</label>
|
|
671
|
+
<label class="slm-ch-radio">
|
|
672
|
+
<input type="radio" name="slm-ch-rot" value="end" data-ch-rot />
|
|
673
|
+
<span><b>End their access now</b><span class="why">All access from the old link ends immediately.
|
|
674
|
+
Buyers part-way through choosing seats lose access.</span></span>
|
|
675
|
+
</label>
|
|
676
|
+
<p class="slm-note">Choose one \u2014 SeatLayer will not decide this for you.</p>
|
|
677
|
+
<p class="slm-ch-err" data-ch-error ${e.error?"":"hidden"}>${c(e.error??"")}</p>
|
|
678
|
+
<div class="foot">
|
|
679
|
+
<button type="button" class="quiet" data-ch-close>Cancel</button>
|
|
680
|
+
<button type="button" class="slm-btn" data-ch-lk-rotate disabled>Rotate and copy new link</button>
|
|
681
|
+
</div>`,r=>{let o=r.querySelector("[data-ch-lk-rotate]");r.querySelectorAll("[data-ch-rot]").forEach(a=>{a.addEventListener("change",()=>{o.disabled=!1})}),o.addEventListener("click",()=>{let a=[...r.querySelectorAll("[data-ch-rot]")].find(l=>l.checked);if(!a){this.showDialogError(j({code:"end_active_sessions_required"}));return}this.rotateLink(e.channelId,t.id,a.value==="end")})})}async rotateLink(e,t,n){try{let s=await this.host.api.rotateAccessLink(this.host.eventKey,e,t,n);this.revealLink(s,{channelId:e,rotated:!0}),await this.reloadAfterLinkChange(e)}catch(s){this.showDialogError(j(s instanceof f?s:void 0)),s instanceof f||this.host.onError(s)}}renderLinkRevokeDialog(e){let t=this.linkById(e.linkId);if(!t||!this.caps.manage){this.closeDialog();return}let n=t.activeSessions??0;this.renderScrim(`
|
|
682
|
+
<h3 id="slm-ch-dlg-title">Revoke the ${c(t.label||"buyer")} link?</h3>
|
|
683
|
+
<p class="sub">It stops opening immediately and cannot be restored \u2014 there is no undo, and no way to
|
|
684
|
+
bring the same URL back. Seats already bought through it keep their sale.</p>
|
|
685
|
+
${n?`<label class="slm-ch-radio">
|
|
686
|
+
<input type="checkbox" data-ch-lk-endsessions />
|
|
687
|
+
<span><b>Also end access for the ${n.toLocaleString()}
|
|
688
|
+
buyer${n===1?"":"s"} already inside</b><span class="why">Leave this off and they can
|
|
689
|
+
finish what they started; new visits are refused either way.</span></span>
|
|
690
|
+
</label>`:""}
|
|
691
|
+
<p class="slm-ch-err" data-ch-error ${e.error?"":"hidden"}>${c(e.error??"")}</p>
|
|
692
|
+
<div class="foot">
|
|
693
|
+
<button type="button" class="quiet" data-ch-close>Cancel</button>
|
|
694
|
+
<button type="button" class="slm-btn danger" data-ch-lk-revoke>Revoke link</button>
|
|
695
|
+
</div>`,s=>{s.querySelector("[data-ch-lk-revoke]")?.addEventListener("click",()=>{let r=s.querySelector("[data-ch-lk-endsessions]")?.checked??!1;this.revokeLink(e.channelId,t.id,r)})})}async revokeLink(e,t,n){try{let s=await this.host.api.revokeAccessLink(this.host.eventKey,e,t,n);this.closeDialog(),await this.reloadAfterLinkChange(e),this.host.toast(s.endedSessions?`Link revoked. ${s.endedSessions.toLocaleString()} buyer${s.endedSessions===1?"":"s"} lost access.`:"Link revoked. It no longer opens for anyone.","ok")}catch(s){this.showDialogError(j(s instanceof f?s:void 0)),s instanceof f||this.host.onError(s)}}applySheetClasses(){let e=this.host.root,t=this.host.isCompact();e.classList.toggle("ch-sheet",t&&this.active);for(let n of["collapsed","medium","full"])e.classList.toggle(`detent-${n}`,t&&this.active&&this.detent===n);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}};export{f as a,Ce as b,C as c,E as d,M as e,_ as f,ee as g,de as h,ue as i,pe as j,K as k,me as l,ge as m,be as n,ve as o,q as p,fe as q,G as r,ye as s,te as t,ke as u,ne as v,se as w,j as x,Ze as y,we as z,ae as A,Qe as B,Ee as C};
|