@seatlayer/js 0.12.2 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/dist/index.cjs +292 -52
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +17 -3
- package/dist/index.d.ts +17 -3
- package/dist/index.js +292 -52
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.cts
CHANGED
|
@@ -933,6 +933,10 @@ declare class SeatManager {
|
|
|
933
933
|
private sectionByObject;
|
|
934
934
|
private sectionLabelById;
|
|
935
935
|
private lastSyncedAt;
|
|
936
|
+
private blockedQuery;
|
|
937
|
+
private blockedSection;
|
|
938
|
+
private blockedResultLimit;
|
|
939
|
+
private unblockAllConfirmTimer;
|
|
936
940
|
private readonly onFullscreenChange;
|
|
937
941
|
private readonly onKeyDown;
|
|
938
942
|
constructor(options: SeatManagerOptions);
|
|
@@ -988,8 +992,9 @@ declare class SeatManager {
|
|
|
988
992
|
private onMessage;
|
|
989
993
|
private resnapshot;
|
|
990
994
|
private applySnapshot;
|
|
991
|
-
/** Optimistic local write shared by
|
|
992
|
-
|
|
995
|
+
/** Optimistic local write shared by organizer actions. Paint and tally once,
|
|
996
|
+
* even when an arena-sized operation changes hundreds of seats. */
|
|
997
|
+
private setSeatsLocal;
|
|
993
998
|
/** Keep the canvas painting on hidden/occluded tabs (war-room second monitor). */
|
|
994
999
|
private afterPaint;
|
|
995
1000
|
private flash;
|
|
@@ -1023,8 +1028,17 @@ declare class SeatManager {
|
|
|
1023
1028
|
private paintLegend;
|
|
1024
1029
|
private paintFeed;
|
|
1025
1030
|
private renderBlockRail;
|
|
1026
|
-
private
|
|
1031
|
+
private toggleCategory;
|
|
1032
|
+
/** A category/filter is a real toggle: add the missing seats, or remove the
|
|
1033
|
+
* whole group when every eligible seat in it is already selected. */
|
|
1034
|
+
private toggleLabels;
|
|
1035
|
+
private isBlockSelectable;
|
|
1027
1036
|
private paintSelBar;
|
|
1037
|
+
private paintCategoryControls;
|
|
1038
|
+
private filteredBlockedSeats;
|
|
1039
|
+
private paintBlockedInventory;
|
|
1040
|
+
private confirmUnblockAll;
|
|
1041
|
+
private resetUnblockAllConfirm;
|
|
1028
1042
|
private done;
|
|
1029
1043
|
private toastOk;
|
|
1030
1044
|
private toastErr;
|
package/dist/index.d.ts
CHANGED
|
@@ -933,6 +933,10 @@ declare class SeatManager {
|
|
|
933
933
|
private sectionByObject;
|
|
934
934
|
private sectionLabelById;
|
|
935
935
|
private lastSyncedAt;
|
|
936
|
+
private blockedQuery;
|
|
937
|
+
private blockedSection;
|
|
938
|
+
private blockedResultLimit;
|
|
939
|
+
private unblockAllConfirmTimer;
|
|
936
940
|
private readonly onFullscreenChange;
|
|
937
941
|
private readonly onKeyDown;
|
|
938
942
|
constructor(options: SeatManagerOptions);
|
|
@@ -988,8 +992,9 @@ declare class SeatManager {
|
|
|
988
992
|
private onMessage;
|
|
989
993
|
private resnapshot;
|
|
990
994
|
private applySnapshot;
|
|
991
|
-
/** Optimistic local write shared by
|
|
992
|
-
|
|
995
|
+
/** Optimistic local write shared by organizer actions. Paint and tally once,
|
|
996
|
+
* even when an arena-sized operation changes hundreds of seats. */
|
|
997
|
+
private setSeatsLocal;
|
|
993
998
|
/** Keep the canvas painting on hidden/occluded tabs (war-room second monitor). */
|
|
994
999
|
private afterPaint;
|
|
995
1000
|
private flash;
|
|
@@ -1023,8 +1028,17 @@ declare class SeatManager {
|
|
|
1023
1028
|
private paintLegend;
|
|
1024
1029
|
private paintFeed;
|
|
1025
1030
|
private renderBlockRail;
|
|
1026
|
-
private
|
|
1031
|
+
private toggleCategory;
|
|
1032
|
+
/** A category/filter is a real toggle: add the missing seats, or remove the
|
|
1033
|
+
* whole group when every eligible seat in it is already selected. */
|
|
1034
|
+
private toggleLabels;
|
|
1035
|
+
private isBlockSelectable;
|
|
1027
1036
|
private paintSelBar;
|
|
1037
|
+
private paintCategoryControls;
|
|
1038
|
+
private filteredBlockedSeats;
|
|
1039
|
+
private paintBlockedInventory;
|
|
1040
|
+
private confirmUnblockAll;
|
|
1041
|
+
private resetUnblockAllConfirm;
|
|
1028
1042
|
private done;
|
|
1029
1043
|
private toastOk;
|
|
1030
1044
|
private toastErr;
|
package/dist/index.js
CHANGED
|
@@ -2275,7 +2275,8 @@ var CSS2 = `
|
|
|
2275
2275
|
.slm input{font:inherit}
|
|
2276
2276
|
|
|
2277
2277
|
/* top bar */
|
|
2278
|
-
.slm-bar{display:
|
|
2278
|
+
.slm-bar{display:grid;grid-template-columns:auto auto minmax(0,1fr);align-items:center;column-gap:14px;row-gap:10px;
|
|
2279
|
+
padding:10px 16px;border-bottom:1px solid var(--slm-line);flex:none}
|
|
2279
2280
|
.slm-modes{display:inline-flex;background:var(--slm-surface);border:1px solid var(--slm-line);border-radius:999px;padding:3px}
|
|
2280
2281
|
.slm-mode{padding:6px 16px;border-radius:999px;font-weight:700;font-size:13px;color:var(--slm-muted)}
|
|
2281
2282
|
.slm-mode.on{background:var(--slm-accent);color:var(--slm-accent-ink)}
|
|
@@ -2283,9 +2284,11 @@ var CSS2 = `
|
|
|
2283
2284
|
.slm-live-dot{width:8px;height:8px;border-radius:50%;background:#8b94ac}
|
|
2284
2285
|
.slm.live .slm-live-dot{background:#22a06b;box-shadow:0 0 0 0 rgba(34,160,107,.55);animation:slm-pulse 2s infinite}
|
|
2285
2286
|
@keyframes slm-pulse{0%{box-shadow:0 0 0 0 rgba(34,160,107,.5)}70%{box-shadow:0 0 0 7px rgba(34,160,107,0)}100%{box-shadow:0 0 0 0 rgba(34,160,107,0)}}
|
|
2286
|
-
.slm-kpis{display:
|
|
2287
|
-
|
|
2288
|
-
.slm-kpi
|
|
2287
|
+
.slm-kpis{grid-column:1/-1;display:grid;grid-template-columns:repeat(8,minmax(0,1fr));width:100%;padding-top:10px;
|
|
2288
|
+
border-top:1px solid var(--slm-line)}
|
|
2289
|
+
.slm-kpi{display:flex;min-width:0;flex-direction:column;align-items:center;padding:0 5px;line-height:1.15;text-align:center}
|
|
2290
|
+
.slm-kpi b{display:flex;min-width:0;align-items:baseline;justify-content:center;font-size:17px;font-weight:800;
|
|
2291
|
+
font-variant-numeric:tabular-nums;white-space:nowrap}
|
|
2289
2292
|
.slm-kpi span{font-size:9.5px;letter-spacing:.1em;text-transform:uppercase;color:var(--slm-muted);font-weight:700}
|
|
2290
2293
|
.slm-kpi .dot{display:inline-block;width:8px;height:8px;border-radius:50%;margin-right:5px;vertical-align:baseline}
|
|
2291
2294
|
.slm-barbtn{padding:7px 13px;border-radius:9px;border:1px solid var(--slm-line);color:var(--slm-text);font-weight:700;font-size:12.5px}
|
|
@@ -2339,11 +2342,44 @@ var CSS2 = `
|
|
|
2339
2342
|
font-size:12px;font-weight:700;color:var(--slm-text);display:inline-flex;align-items:center;gap:6px}
|
|
2340
2343
|
.slm-chip:hover{border-color:var(--slm-muted)}
|
|
2341
2344
|
.slm-chip .dot{width:8px;height:8px;border-radius:50%}
|
|
2345
|
+
.slm-chip .slm-chipcount{min-width:18px;padding:1px 5px;border-radius:999px;background:rgba(255,255,255,.07);
|
|
2346
|
+
color:var(--slm-muted);font-size:10px;font-variant-numeric:tabular-nums;text-align:center}
|
|
2347
|
+
.slm-chip .slm-chipcheck{display:none;font-size:11px;line-height:1}
|
|
2348
|
+
.slm-chip.on{border-color:var(--slm-accent);background:color-mix(in srgb,var(--slm-accent) 20%,var(--slm-surface));
|
|
2349
|
+
box-shadow:0 0 0 1px color-mix(in srgb,var(--slm-accent) 45%,transparent)}
|
|
2350
|
+
.slm-chip.on .slm-chipcount{background:var(--slm-accent);color:var(--slm-accent-ink)}
|
|
2351
|
+
.slm-chip.on .slm-chipcheck{display:inline}
|
|
2352
|
+
.slm-chip.partial{border-style:dashed;border-color:var(--slm-accent)}
|
|
2353
|
+
.slm-chip:disabled{opacity:.42;cursor:not-allowed}
|
|
2354
|
+
.slm-selecthelp{margin:-1px 0 9px;color:var(--slm-muted);font-size:11px;line-height:1.4}
|
|
2342
2355
|
.slm-field{margin:14px 0}
|
|
2343
2356
|
.slm-field label{display:block;font-size:11px;font-weight:700;color:var(--slm-muted);margin-bottom:5px}
|
|
2344
2357
|
.slm-input,.slm-select{width:100%;padding:8px 10px;border-radius:9px;border:1px solid var(--slm-line);
|
|
2345
2358
|
background:var(--slm-surface);color:var(--slm-text)}
|
|
2346
2359
|
.slm-note{font-size:11.5px;color:var(--slm-muted);margin-top:5px}
|
|
2360
|
+
.slm-blocked{margin-top:17px;padding-top:15px;border-top:1px solid var(--slm-line)}
|
|
2361
|
+
.slm-blockedhead{display:flex;align-items:baseline;justify-content:space-between;gap:10px;margin-bottom:8px}
|
|
2362
|
+
.slm-blockedhead .slm-eyebrow{margin-bottom:0}.slm-blockedtotal{font-size:11px;color:var(--slm-muted)}
|
|
2363
|
+
.slm-blockedtotal b{color:var(--slm-text);font-variant-numeric:tabular-nums}
|
|
2364
|
+
.slm-blockedtools{display:grid;grid-template-columns:minmax(0,1fr);gap:7px}
|
|
2365
|
+
.slm-blockedsummary{display:flex;align-items:center;justify-content:space-between;gap:8px;margin:9px 0 6px;
|
|
2366
|
+
color:var(--slm-muted);font-size:10.5px}
|
|
2367
|
+
.slm-linkbtn{font-size:11px!important;font-weight:800!important;color:var(--slm-accent)!important;text-align:right}
|
|
2368
|
+
.slm-linkbtn:disabled{opacity:.45;cursor:not-allowed}
|
|
2369
|
+
.slm-blockedlist{max-height:246px;overflow:auto;border:1px solid var(--slm-line);border-radius:10px;background:var(--slm-surface)}
|
|
2370
|
+
.slm-blockeditem{display:grid!important;grid-template-columns:18px minmax(0,1fr);width:100%;gap:8px;padding:8px 9px!important;
|
|
2371
|
+
border-bottom:1px solid var(--slm-line)!important;text-align:left!important}
|
|
2372
|
+
.slm-blockeditem:last-child{border-bottom:0!important}.slm-blockeditem:hover{background:rgba(255,255,255,.035)!important}
|
|
2373
|
+
.slm-blockeditem.on{background:color-mix(in srgb,var(--slm-accent) 13%,var(--slm-surface))!important}
|
|
2374
|
+
.slm-blockedcheck{display:flex;align-items:center;justify-content:center;width:16px;height:16px;margin-top:1px;border-radius:4px;
|
|
2375
|
+
border:1px solid var(--slm-muted);color:transparent;font-size:10px;font-weight:900}
|
|
2376
|
+
.slm-blockeditem.on .slm-blockedcheck{border-color:var(--slm-accent);background:var(--slm-accent);color:var(--slm-accent-ink)}
|
|
2377
|
+
.slm-blockedcopy{min-width:0}.slm-blockedlabel{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
|
|
2378
|
+
font-size:12px;font-weight:800}.slm-blockedmeta{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
|
|
2379
|
+
margin-top:2px;color:var(--slm-muted);font-size:10px}
|
|
2380
|
+
.slm-blockedmore{width:100%;padding:9px!important;color:var(--slm-accent)!important;font-size:11px!important;font-weight:800!important}
|
|
2381
|
+
.slm-blockedempty{padding:12px;color:var(--slm-muted);font-size:11.5px;line-height:1.45}
|
|
2382
|
+
.slm-allnote{margin-top:-4px;margin-bottom:10px}
|
|
2347
2383
|
|
|
2348
2384
|
/* toast */
|
|
2349
2385
|
.slm-toast{position:absolute;left:50%;bottom:16px;transform:translateX(-50%);padding:10px 16px;border-radius:10px;
|
|
@@ -2354,7 +2390,7 @@ var CSS2 = `
|
|
|
2354
2390
|
.slm-toast.ok{background:#1f7a4d;color:#fff;border-color:#1f7a4d}
|
|
2355
2391
|
|
|
2356
2392
|
/* control-room actions + insights */
|
|
2357
|
-
.slm-bar-actions{display:flex;align-items:center;gap:7px}
|
|
2393
|
+
.slm-bar-actions{display:flex;align-items:center;justify-self:end;gap:7px}
|
|
2358
2394
|
.slm-barbtn.on{background:rgba(244,183,64,.13);border-color:#f4b740;color:#f7ca6b}
|
|
2359
2395
|
.slm-sectionlist{display:flex;flex-direction:column;gap:8px;margin-top:4px}
|
|
2360
2396
|
.slm-sectionlist + .slm-eyebrow{margin-top:18px}
|
|
@@ -2372,16 +2408,22 @@ var CSS2 = `
|
|
|
2372
2408
|
.slm-momentumhelp[hidden]{display:none}.slm-momentumscale{display:flex;align-items:center;gap:7px;color:var(--slm-muted);font-size:10px;font-weight:750;text-transform:uppercase;letter-spacing:.07em}
|
|
2373
2409
|
.slm-momentumgradient{height:6px;min-width:64px;flex:1;border-radius:999px;background:linear-gradient(90deg,#f4b740,#ef4444)}
|
|
2374
2410
|
.slm-momentumcopy{margin-top:7px;color:var(--slm-muted);font-size:11px;line-height:1.45}
|
|
2375
|
-
.slm-inspect-card{padding:
|
|
2376
|
-
.slm-inspect-label{font-size:24px;font-weight:850;letter-spacing:-.02em
|
|
2377
|
-
.slm-inspect-grid
|
|
2411
|
+
.slm-inspect-card{padding:16px;border:1px solid var(--slm-line);border-radius:12px;background:var(--slm-surface)}
|
|
2412
|
+
.slm-inspect-label{font-size:24px;font-weight:850;letter-spacing:-.02em;line-height:1.1}
|
|
2413
|
+
.slm-inspect-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:16px 20px;margin-top:18px}
|
|
2414
|
+
.slm-inspect-grid>div{min-width:0}.slm-inspect-grid span{display:block;color:var(--slm-muted);font-size:10px;
|
|
2415
|
+
text-transform:uppercase;letter-spacing:.08em}.slm-inspect-grid b{display:block;margin-top:4px;font-size:13px;line-height:1.35;overflow-wrap:anywhere}
|
|
2378
2416
|
.slm:fullscreen{border-radius:0;min-height:100vh;background:var(--slm-bg)}
|
|
2379
2417
|
.slm:fullscreen .slm-bar{padding:14px 22px}.slm:fullscreen .slm-kpi b{font-size:21px}.slm:fullscreen .slm-rail{width:360px}
|
|
2380
2418
|
|
|
2381
2419
|
.slm.compact .slm-rail{width:100%;border-left:0;border-top:1px solid var(--slm-line);height:44%}
|
|
2382
2420
|
.slm.compact .slm-body{flex-direction:column}
|
|
2383
|
-
.slm.compact .slm-bar{gap:8px;padding:8px}
|
|
2384
|
-
.slm.compact .slm-
|
|
2421
|
+
.slm.compact .slm-bar{grid-template-columns:minmax(0,1fr) auto;gap:8px;padding:8px}
|
|
2422
|
+
.slm.compact .slm-modes{min-width:0}.slm.compact .slm-mode{padding-inline:11px}
|
|
2423
|
+
.slm.compact .slm-live{justify-self:end}.slm.compact .slm-bar-actions{grid-column:1/-1;justify-self:stretch}
|
|
2424
|
+
.slm.compact .slm-barbtn{flex:1;padding:6px 9px}.slm.compact .slm-kpis{grid-template-columns:repeat(4,minmax(0,1fr));gap:8px}
|
|
2425
|
+
.slm.compact .slm-kpi[data-kpi="buyers"],.slm.compact .slm-kpi[data-kpi="active-holds"],
|
|
2426
|
+
.slm.compact .slm-kpi[data-kpi="sold-pct"],.slm.compact .slm-kpi[data-kpi="gross-sales"]{display:none}
|
|
2385
2427
|
`;
|
|
2386
2428
|
function injectStyle() {
|
|
2387
2429
|
if (typeof document === "undefined" || document.getElementById(STYLE_ID2)) return;
|
|
@@ -2457,6 +2499,10 @@ var SeatManager = class {
|
|
|
2457
2499
|
this.sectionByObject = /* @__PURE__ */ new Map();
|
|
2458
2500
|
this.sectionLabelById = /* @__PURE__ */ new Map();
|
|
2459
2501
|
this.lastSyncedAt = null;
|
|
2502
|
+
this.blockedQuery = "";
|
|
2503
|
+
this.blockedSection = "";
|
|
2504
|
+
this.blockedResultLimit = 100;
|
|
2505
|
+
this.unblockAllConfirmTimer = null;
|
|
2460
2506
|
this.onFullscreenChange = () => {
|
|
2461
2507
|
this.paintFullscreenButton();
|
|
2462
2508
|
this.updateContainerLayout();
|
|
@@ -2602,13 +2648,13 @@ var SeatManager = class {
|
|
|
2602
2648
|
const targets = (labels ?? this.selectionLabels()).filter((l) => this.status.get(l) === "free");
|
|
2603
2649
|
if (!targets.length) return;
|
|
2604
2650
|
const releaseAt = opts.releaseAt ?? this.releaseAt ?? void 0;
|
|
2605
|
-
|
|
2651
|
+
this.setSeatsLocal(targets, "blocked");
|
|
2606
2652
|
try {
|
|
2607
2653
|
await this.api.block(this.key, targets, { ...opts, releaseAt });
|
|
2608
2654
|
this.clearSelection();
|
|
2609
2655
|
this.done("block", targets, releaseAt ? `Blocked ${targets.length} \u2014 auto-release ${new Date(releaseAt).toLocaleString()}.` : `Blocked ${targets.length} seat${targets.length === 1 ? "" : "s"}.`);
|
|
2610
2656
|
} catch (err) {
|
|
2611
|
-
|
|
2657
|
+
this.setSeatsLocal(targets, "free");
|
|
2612
2658
|
this.toastErr(err instanceof ManageApiError && err.status === 409 ? "Some seats were just taken. Try again." : "Couldn't block those seats.");
|
|
2613
2659
|
this.opts.onError?.(err);
|
|
2614
2660
|
}
|
|
@@ -2616,13 +2662,13 @@ var SeatManager = class {
|
|
|
2616
2662
|
async unblock(labels) {
|
|
2617
2663
|
const targets = (labels ?? this.selectionLabels()).filter((l) => this.status.get(l) === "blocked");
|
|
2618
2664
|
if (!targets.length) return;
|
|
2619
|
-
|
|
2665
|
+
this.setSeatsLocal(targets, "free");
|
|
2620
2666
|
try {
|
|
2621
2667
|
await this.api.unblock(this.key, targets);
|
|
2622
2668
|
this.clearSelection();
|
|
2623
2669
|
this.done("unblock", targets, `Unblocked ${targets.length} seat${targets.length === 1 ? "" : "s"}.`);
|
|
2624
2670
|
} catch (err) {
|
|
2625
|
-
|
|
2671
|
+
this.setSeatsLocal(targets, "blocked");
|
|
2626
2672
|
this.toastErr("Couldn't unblock those seats.");
|
|
2627
2673
|
this.opts.onError?.(err);
|
|
2628
2674
|
}
|
|
@@ -2630,9 +2676,10 @@ var SeatManager = class {
|
|
|
2630
2676
|
async unblockAll() {
|
|
2631
2677
|
const blocked = [...this.status.entries()].filter(([, s]) => s === "blocked").map(([l]) => l);
|
|
2632
2678
|
if (!blocked.length) return;
|
|
2633
|
-
|
|
2679
|
+
this.setSeatsLocal(blocked, "free");
|
|
2634
2680
|
try {
|
|
2635
2681
|
const res = await this.api.unblockAll(this.key);
|
|
2682
|
+
this.clearSelection();
|
|
2636
2683
|
this.done("unblockAll", blocked, `Unblocked ${res.freed} seat${res.freed === 1 ? "" : "s"}.`);
|
|
2637
2684
|
} catch (err) {
|
|
2638
2685
|
await this.resnapshot();
|
|
@@ -2644,13 +2691,13 @@ var SeatManager = class {
|
|
|
2644
2691
|
async cancelBooking(labels, bookingRef) {
|
|
2645
2692
|
const targets = labels.filter((l) => this.status.get(l) === "booked");
|
|
2646
2693
|
if (!targets.length || !bookingRef) return;
|
|
2647
|
-
|
|
2694
|
+
this.setSeatsLocal(targets, "free");
|
|
2648
2695
|
try {
|
|
2649
2696
|
await this.api.unbook(this.key, targets, bookingRef);
|
|
2650
2697
|
this.clearSelection();
|
|
2651
2698
|
this.done("cancelBooking", targets, `Cancelled ${targets.length} booking${targets.length === 1 ? "" : "s"}.`);
|
|
2652
2699
|
} catch (err) {
|
|
2653
|
-
|
|
2700
|
+
this.setSeatsLocal(targets, "booked");
|
|
2654
2701
|
this.toastErr("Couldn't cancel that booking. Check the reference.");
|
|
2655
2702
|
this.opts.onError?.(err);
|
|
2656
2703
|
}
|
|
@@ -2713,6 +2760,7 @@ var SeatManager = class {
|
|
|
2713
2760
|
if (this.reconnectTimer) clearTimeout(this.reconnectTimer);
|
|
2714
2761
|
if (this.feedTimer) clearInterval(this.feedTimer);
|
|
2715
2762
|
if (this.toastTimer) clearTimeout(this.toastTimer);
|
|
2763
|
+
if (this.unblockAllConfirmTimer) clearTimeout(this.unblockAllConfirmTimer);
|
|
2716
2764
|
if (this.revenueRefreshTimer) clearTimeout(this.revenueRefreshTimer);
|
|
2717
2765
|
if (this.tokenRefreshTimer) clearTimeout(this.tokenRefreshTimer);
|
|
2718
2766
|
this.layoutObserver?.disconnect();
|
|
@@ -2891,11 +2939,16 @@ var SeatManager = class {
|
|
|
2891
2939
|
this.afterPaint();
|
|
2892
2940
|
this.recomputeTallies();
|
|
2893
2941
|
}
|
|
2894
|
-
/** Optimistic local write shared by
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
const
|
|
2898
|
-
|
|
2942
|
+
/** Optimistic local write shared by organizer actions. Paint and tally once,
|
|
2943
|
+
* even when an arena-sized operation changes hundreds of seats. */
|
|
2944
|
+
setSeatsLocal(labels, st) {
|
|
2945
|
+
const ids = [];
|
|
2946
|
+
for (const label of labels) {
|
|
2947
|
+
this.status.set(label, st);
|
|
2948
|
+
const id = this.labelToId.get(label);
|
|
2949
|
+
if (id) ids.push(id);
|
|
2950
|
+
}
|
|
2951
|
+
if (ids.length) this.renderer?.setStatus(ids, toRenderStatus(st));
|
|
2899
2952
|
this.afterPaint();
|
|
2900
2953
|
this.recomputeTallies();
|
|
2901
2954
|
}
|
|
@@ -2978,6 +3031,7 @@ var SeatManager = class {
|
|
|
2978
3031
|
this.paintLegend(t3);
|
|
2979
3032
|
this.paintMonitorInsights();
|
|
2980
3033
|
} else if (this.mode === "inspect") this.renderInspectRail(this.getSelection());
|
|
3034
|
+
else if (this.mode === "block") this.paintSelBar(this.getSelection());
|
|
2981
3035
|
this.opts.onTallies?.(t3);
|
|
2982
3036
|
}
|
|
2983
3037
|
verbFor(prev, next) {
|
|
@@ -3199,15 +3253,15 @@ var SeatManager = class {
|
|
|
3199
3253
|
const rev = t3.revenueStatus === "current" ? fmtMoney(t3.grossRevenue, t3.currency) : "\u2014";
|
|
3200
3254
|
const presence = this.controlRoomSnapshot?.presence;
|
|
3201
3255
|
this.els.kpis.innerHTML = [
|
|
3202
|
-
{ n: t3.booked.toLocaleString(), l: "Sold", dot: "#22a06b" },
|
|
3203
|
-
{ n: t3.held.toLocaleString(), l: "Held", dot: "#f4b740" },
|
|
3204
|
-
{ n: presence ? presence.shoppingSessions.toLocaleString() : "\u2014", l: "
|
|
3205
|
-
{ n: presence ? presence.activeHolds.toLocaleString() : "\u2014", l: "
|
|
3206
|
-
{ n: t3.free.toLocaleString(), l: "Free", dot: "#6e7bff" },
|
|
3207
|
-
{ n: t3.blocked.toLocaleString(), l: "Blocked", dot: "#8b94ac" },
|
|
3208
|
-
{ n: `${t3.capacityPct}%`, l: "
|
|
3209
|
-
{ n: rev, l: "Gross" }
|
|
3210
|
-
].map((k) => `<div class="slm-kpi"><b>${k.dot ? `<span class="dot" style="background:${k.dot}"></span>` : ""}${k.n}</b><span>${k.l}</span></div>`).join("");
|
|
3256
|
+
{ key: "sold-seats", n: t3.booked.toLocaleString(), l: "Sold seats", dot: "#22a06b" },
|
|
3257
|
+
{ key: "held-seats", n: t3.held.toLocaleString(), l: "Held seats", dot: "#f4b740" },
|
|
3258
|
+
{ key: "buyers", n: presence ? presence.shoppingSessions.toLocaleString() : "\u2014", l: "Buyers" },
|
|
3259
|
+
{ key: "active-holds", n: presence ? presence.activeHolds.toLocaleString() : "\u2014", l: "Active holds" },
|
|
3260
|
+
{ key: "free-seats", n: t3.free.toLocaleString(), l: "Free seats", dot: "#6e7bff" },
|
|
3261
|
+
{ key: "blocked", n: t3.blocked.toLocaleString(), l: "Blocked", dot: "#8b94ac" },
|
|
3262
|
+
{ key: "sold-pct", n: `${t3.capacityPct}%`, l: "Sold" },
|
|
3263
|
+
{ key: "gross-sales", n: rev, l: "Gross sales" }
|
|
3264
|
+
].map((k) => `<div class="slm-kpi" data-kpi="${k.key}"><b>${k.dot ? `<span class="dot" style="background:${k.dot}"></span>` : ""}${k.n}</b><span>${k.l}</span></div>`).join("");
|
|
3211
3265
|
}
|
|
3212
3266
|
// ---- DOM: rails -----------------------------------------------------------
|
|
3213
3267
|
paintRail() {
|
|
@@ -3306,9 +3360,9 @@ var SeatManager = class {
|
|
|
3306
3360
|
const seat = seats[seats.length - 1];
|
|
3307
3361
|
if (!seat) {
|
|
3308
3362
|
this.els.rail.innerHTML = `
|
|
3309
|
-
<p class="slm-eyebrow">Inspect</p>
|
|
3310
|
-
<p class="slm-hint">Select
|
|
3311
|
-
<div class="slm-empty">
|
|
3363
|
+
<p class="slm-eyebrow">Inspect seats</p>
|
|
3364
|
+
<p class="slm-hint">Select a seat to see its availability and sales context. Nothing changes in this view.</p>
|
|
3365
|
+
<div class="slm-empty">Select a seat on the map.</div>`;
|
|
3312
3366
|
return;
|
|
3313
3367
|
}
|
|
3314
3368
|
const status = this.status.get(seat.label) ?? "free";
|
|
@@ -3317,17 +3371,20 @@ var SeatManager = class {
|
|
|
3317
3371
|
const sectionLabel = this.sectionLabelById.get(sectionId) ?? "Other seats";
|
|
3318
3372
|
const category = this.doc?.categories.find((item) => item.key === seat.categoryKey);
|
|
3319
3373
|
const sectionMetric = this.controlRoomSnapshot?.revenue.bySection.find((row) => row.sectionId === sectionId);
|
|
3374
|
+
const object = this.doc?.objects.find((item) => item.id === seat.rowId);
|
|
3375
|
+
const location = object?.type === "row" ? { label: "Row", value: object.label } : object?.type === "table" ? { label: "Table", value: object.label } : seat.kind === "booth" ? { label: "Type", value: "Booth" } : null;
|
|
3376
|
+
const itemKind = seat.kind === "booth" ? "Booth" : "Seat";
|
|
3320
3377
|
this.els.rail.innerHTML = `
|
|
3321
|
-
<p class="slm-eyebrow"
|
|
3322
|
-
<p class="slm-hint">Live
|
|
3378
|
+
<p class="slm-eyebrow">${itemKind} details</p>
|
|
3379
|
+
<p class="slm-hint">Live availability and section performance.</p>
|
|
3323
3380
|
<div class="slm-inspect-card">
|
|
3324
3381
|
<div class="slm-inspect-label">${esc(seat.label)}</div>
|
|
3325
3382
|
<div class="slm-inspect-grid">
|
|
3326
3383
|
<div><span>Status</span><b>${statusLabel[status]}</b></div>
|
|
3327
3384
|
<div><span>Section</span><b>${esc(sectionLabel)}</b></div>
|
|
3328
|
-
|
|
3385
|
+
${location ? `<div><span>${location.label}</span><b>${esc(location.value)}</b></div>` : ""}
|
|
3329
3386
|
<div><span>Category</span><b>${esc(category?.label ?? seat.categoryKey)}</b></div>
|
|
3330
|
-
<div><span>
|
|
3387
|
+
<div><span>Sold in section</span><b>${sectionMetric ? `${sectionMetric.booked} of ${sectionMetric.total}` : "\u2014"}</b></div>
|
|
3331
3388
|
<div><span>Section revenue</span><b>${sectionMetric && this.controlRoomSnapshot ? fmtMoney(sectionMetric.bookedRevenue, this.controlRoomSnapshot.currency) : "\u2014"}</b></div>
|
|
3332
3389
|
</div>
|
|
3333
3390
|
</div>`;
|
|
@@ -3354,23 +3411,30 @@ var SeatManager = class {
|
|
|
3354
3411
|
}
|
|
3355
3412
|
renderBlockRail() {
|
|
3356
3413
|
const cats = this.doc?.categories ?? [];
|
|
3357
|
-
const catChips = cats.map((c) => `<button class="slm-chip" data-cat="${esc(c.key)}"
|
|
3414
|
+
const catChips = cats.map((c) => `<button class="slm-chip" type="button" data-cat="${esc(c.key)}" aria-pressed="false">
|
|
3415
|
+
<span class="dot" style="background:${esc(c.color ?? "#6e7bff")}"></span>
|
|
3416
|
+
<span>${esc(c.label ?? c.key)}</span>
|
|
3417
|
+
<span class="slm-chipcount" data-cat-count>0</span>
|
|
3418
|
+
<span class="slm-chipcheck" aria-hidden="true">\u2713</span>
|
|
3419
|
+
</button>`).join("");
|
|
3358
3420
|
const sectionField = this.sectionOptions.length ? `<div class="slm-field"><label>Select a whole section</label>
|
|
3359
3421
|
<select class="slm-select" data-ref="section"><option value="">Choose a section\u2026</option>
|
|
3360
3422
|
${this.sectionOptions.map((s) => `<option value="${esc(s.id)}">${esc(s.label)}</option>`).join("")}</select></div>` : "";
|
|
3423
|
+
const blockedSectionOptions = this.sectionOptions.map((s) => `<option value="${esc(s.id)}">${esc(s.label)}</option>`).join("");
|
|
3361
3424
|
this.els.rail.innerHTML = `
|
|
3362
3425
|
<p class="slm-eyebrow">Block & unblock</p>
|
|
3363
3426
|
<p class="slm-hint">Drag a box on the map to marquee-select, \u2318A for all, or pick a category/section. Booked and held inventory is never actionable here.</p>
|
|
3364
|
-
<div class="slm-selbar"><span class="slm-selnum" data-ref="selnum">0</span><span class="slm-sellabel">selected</span></div>
|
|
3427
|
+
<div class="slm-selbar" aria-live="polite"><span class="slm-selnum" data-ref="selnum">0</span><span class="slm-sellabel" data-ref="selmeta">selected</span></div>
|
|
3365
3428
|
<div class="slm-row">
|
|
3366
3429
|
<button class="slm-btn" data-ref="doblock" disabled>Block</button>
|
|
3367
|
-
<button class="slm-btn ghost" data-ref="dounblock" disabled>
|
|
3430
|
+
<button class="slm-btn ghost" data-ref="dounblock" disabled>Put back on sale</button>
|
|
3368
3431
|
</div>
|
|
3369
3432
|
<div class="slm-row">
|
|
3370
3433
|
<button class="slm-btn ghost" data-ref="selall">Select all</button>
|
|
3371
3434
|
<button class="slm-btn ghost" data-ref="clearsel">Clear</button>
|
|
3372
3435
|
</div>
|
|
3373
|
-
<p class="slm-eyebrow" style="margin-top:8px">
|
|
3436
|
+
<p class="slm-eyebrow" style="margin-top:8px">Select by category</p>
|
|
3437
|
+
<p class="slm-selecthelp">Choose one or more. A checked category is selected; click it again to remove it.</p>
|
|
3374
3438
|
<div class="slm-chiprow">${catChips || '<span class="slm-empty">No categories.</span>'}</div>
|
|
3375
3439
|
${sectionField}
|
|
3376
3440
|
<div class="slm-field">
|
|
@@ -3378,18 +3442,46 @@ var SeatManager = class {
|
|
|
3378
3442
|
<input type="datetime-local" class="slm-input" data-ref="release" />
|
|
3379
3443
|
<p class="slm-note" data-ref="releasenote">Leave empty to block permanently.</p>
|
|
3380
3444
|
</div>
|
|
3381
|
-
<
|
|
3445
|
+
<section class="slm-blocked" aria-labelledby="slm-blocked-title">
|
|
3446
|
+
<div class="slm-blockedhead">
|
|
3447
|
+
<p class="slm-eyebrow" id="slm-blocked-title">Blocked inventory</p>
|
|
3448
|
+
<span class="slm-blockedtotal"><b data-ref="blockedcount">0</b> out of sale</span>
|
|
3449
|
+
</div>
|
|
3450
|
+
<p class="slm-selecthelp">Find blocked seats, select only the ones you need, then use \u201CPut back on sale\u201D.</p>
|
|
3451
|
+
<div class="slm-blockedtools">
|
|
3452
|
+
<input type="search" class="slm-input" data-ref="blockedsearch" placeholder="Find seat, row or category" aria-label="Search blocked seats" />
|
|
3453
|
+
<select class="slm-select" data-ref="blockedsection" aria-label="Filter blocked seats by section">
|
|
3454
|
+
<option value="">All sections</option>${blockedSectionOptions}
|
|
3455
|
+
</select>
|
|
3456
|
+
</div>
|
|
3457
|
+
<div class="slm-blockedsummary">
|
|
3458
|
+
<span data-ref="blockedshowing">No blocked seats</span>
|
|
3459
|
+
<button type="button" class="slm-linkbtn" data-ref="selblocked" disabled>Select results</button>
|
|
3460
|
+
</div>
|
|
3461
|
+
<div class="slm-blockedlist" data-ref="blockedlist"></div>
|
|
3462
|
+
</section>
|
|
3463
|
+
<div class="slm-field">
|
|
3464
|
+
<button class="slm-btn ghost" data-ref="markall" style="width:100%" disabled>Put all blocked seats on sale</button>
|
|
3465
|
+
<p class="slm-note slm-allnote" data-ref="markallnote">For a full reset only. You will be asked to confirm.</p>
|
|
3466
|
+
</div>
|
|
3382
3467
|
`;
|
|
3383
3468
|
const r = (n) => this.els.rail.querySelector(`[data-ref="${n}"]`);
|
|
3384
3469
|
this.els.selnum = r("selnum");
|
|
3385
3470
|
this.els.doblock = r("doblock");
|
|
3386
3471
|
this.els.dounblock = r("dounblock");
|
|
3472
|
+
this.els.selmeta = r("selmeta");
|
|
3473
|
+
this.els.blockedcount = r("blockedcount");
|
|
3474
|
+
this.els.blockedshowing = r("blockedshowing");
|
|
3475
|
+
this.els.blockedlist = r("blockedlist");
|
|
3476
|
+
this.els.selblocked = r("selblocked");
|
|
3477
|
+
this.els.markall = r("markall");
|
|
3478
|
+
this.els.markallnote = r("markallnote");
|
|
3387
3479
|
r("doblock").addEventListener("click", () => void this.block());
|
|
3388
3480
|
r("dounblock").addEventListener("click", () => void this.unblock());
|
|
3389
3481
|
r("selall").addEventListener("click", () => this.selectAll());
|
|
3390
3482
|
r("clearsel").addEventListener("click", () => this.clearSelection());
|
|
3391
|
-
r("markall").addEventListener("click", () =>
|
|
3392
|
-
this.els.rail.querySelectorAll("[data-cat]").forEach((b) => b.addEventListener("click", () => this.
|
|
3483
|
+
r("markall").addEventListener("click", () => this.confirmUnblockAll());
|
|
3484
|
+
this.els.rail.querySelectorAll("[data-cat]").forEach((b) => b.addEventListener("click", () => this.toggleCategory(b.dataset.cat)));
|
|
3393
3485
|
const sectionSel = this.els.rail.querySelector('[data-ref="section"]');
|
|
3394
3486
|
sectionSel?.addEventListener("change", () => {
|
|
3395
3487
|
if (sectionSel.value) {
|
|
@@ -3397,6 +3489,32 @@ var SeatManager = class {
|
|
|
3397
3489
|
sectionSel.value = "";
|
|
3398
3490
|
}
|
|
3399
3491
|
});
|
|
3492
|
+
const blockedSearch = r("blockedsearch");
|
|
3493
|
+
const blockedSection = r("blockedsection");
|
|
3494
|
+
blockedSearch.value = this.blockedQuery;
|
|
3495
|
+
blockedSection.value = this.blockedSection;
|
|
3496
|
+
blockedSearch.addEventListener("input", () => {
|
|
3497
|
+
this.blockedQuery = blockedSearch.value;
|
|
3498
|
+
this.blockedResultLimit = 100;
|
|
3499
|
+
this.paintBlockedInventory();
|
|
3500
|
+
});
|
|
3501
|
+
blockedSection.addEventListener("change", () => {
|
|
3502
|
+
this.blockedSection = blockedSection.value;
|
|
3503
|
+
this.blockedResultLimit = 100;
|
|
3504
|
+
this.paintBlockedInventory();
|
|
3505
|
+
});
|
|
3506
|
+
r("selblocked").addEventListener("click", () => {
|
|
3507
|
+
this.toggleLabels(this.filteredBlockedSeats().map((seat) => seat.label));
|
|
3508
|
+
});
|
|
3509
|
+
r("blockedlist").addEventListener("click", (event) => {
|
|
3510
|
+
const target = event.target;
|
|
3511
|
+
const seatButton = target.closest("[data-blocked-label]");
|
|
3512
|
+
if (seatButton?.dataset.blockedLabel) this.toggleLabels([seatButton.dataset.blockedLabel]);
|
|
3513
|
+
else if (target.closest("[data-blocked-more]")) {
|
|
3514
|
+
this.blockedResultLimit += 100;
|
|
3515
|
+
this.paintBlockedInventory();
|
|
3516
|
+
}
|
|
3517
|
+
});
|
|
3400
3518
|
const rel = r("release");
|
|
3401
3519
|
rel.addEventListener("change", () => {
|
|
3402
3520
|
const ms = rel.value ? new Date(rel.value).getTime() : NaN;
|
|
@@ -3406,18 +3524,140 @@ var SeatManager = class {
|
|
|
3406
3524
|
});
|
|
3407
3525
|
this.paintSelBar(this.getSelection());
|
|
3408
3526
|
}
|
|
3409
|
-
|
|
3527
|
+
toggleCategory(catKey) {
|
|
3410
3528
|
const labels = [];
|
|
3411
|
-
for (const [label, seat] of this.labelToSeat.entries())
|
|
3412
|
-
|
|
3529
|
+
for (const [label, seat] of this.labelToSeat.entries()) {
|
|
3530
|
+
if (seat.categoryKey === catKey && this.isBlockSelectable(label)) labels.push(label);
|
|
3531
|
+
}
|
|
3532
|
+
this.toggleLabels(labels);
|
|
3533
|
+
}
|
|
3534
|
+
/** A category/filter is a real toggle: add the missing seats, or remove the
|
|
3535
|
+
* whole group when every eligible seat in it is already selected. */
|
|
3536
|
+
toggleLabels(labels) {
|
|
3537
|
+
if (!this.renderer) return;
|
|
3538
|
+
const eligible = labels.filter((label) => this.labelToSeat.has(label) && this.isBlockSelectable(label));
|
|
3539
|
+
if (!eligible.length) return;
|
|
3540
|
+
const selected = new Set(this.selectionLabels());
|
|
3541
|
+
const allSelected = eligible.every((label) => selected.has(label));
|
|
3542
|
+
if (allSelected) {
|
|
3543
|
+
const ids = eligible.map((label) => this.labelToId.get(label)).filter((id) => Boolean(id));
|
|
3544
|
+
this.renderer.deselect(ids);
|
|
3545
|
+
} else {
|
|
3546
|
+
this.renderer.selectByLabels(eligible);
|
|
3547
|
+
}
|
|
3548
|
+
this.syncSelection();
|
|
3549
|
+
}
|
|
3550
|
+
isBlockSelectable(label) {
|
|
3551
|
+
const status = this.status.get(label) ?? "free";
|
|
3552
|
+
return status === "free" || status === "blocked";
|
|
3413
3553
|
}
|
|
3414
3554
|
paintSelBar(seats) {
|
|
3415
3555
|
if (!this.els.selnum) return;
|
|
3416
3556
|
this.els.selnum.textContent = seats.length.toLocaleString();
|
|
3417
|
-
const
|
|
3418
|
-
const
|
|
3419
|
-
this.els.
|
|
3420
|
-
this.els.
|
|
3557
|
+
const freeCount = seats.filter((s) => (this.status.get(s.label) ?? "free") === "free").length;
|
|
3558
|
+
const blockedCount = seats.filter((s) => this.status.get(s.label) === "blocked").length;
|
|
3559
|
+
this.els.selmeta.textContent = seats.length ? `${freeCount.toLocaleString()} available \xB7 ${blockedCount.toLocaleString()} blocked` : "selected";
|
|
3560
|
+
const blockButton = this.els.doblock;
|
|
3561
|
+
const unblockButton = this.els.dounblock;
|
|
3562
|
+
blockButton.disabled = freeCount === 0;
|
|
3563
|
+
unblockButton.disabled = blockedCount === 0;
|
|
3564
|
+
blockButton.textContent = freeCount ? `Block ${freeCount.toLocaleString()}` : "Block selected";
|
|
3565
|
+
unblockButton.textContent = blockedCount ? `Put ${blockedCount.toLocaleString()} on sale` : "Put back on sale";
|
|
3566
|
+
this.paintCategoryControls(seats);
|
|
3567
|
+
this.paintBlockedInventory();
|
|
3568
|
+
}
|
|
3569
|
+
paintCategoryControls(seats) {
|
|
3570
|
+
const selected = new Set(seats.map((seat) => seat.label));
|
|
3571
|
+
this.els.rail?.querySelectorAll("[data-cat]").forEach((button) => {
|
|
3572
|
+
const catKey = button.dataset.cat;
|
|
3573
|
+
const labels = [];
|
|
3574
|
+
for (const [label, seat] of this.labelToSeat.entries()) {
|
|
3575
|
+
if (seat.categoryKey === catKey && this.isBlockSelectable(label)) labels.push(label);
|
|
3576
|
+
}
|
|
3577
|
+
const picked = labels.filter((label) => selected.has(label)).length;
|
|
3578
|
+
const full = labels.length > 0 && picked === labels.length;
|
|
3579
|
+
const partial = picked > 0 && !full;
|
|
3580
|
+
button.disabled = labels.length === 0;
|
|
3581
|
+
button.classList.toggle("on", full);
|
|
3582
|
+
button.classList.toggle("partial", partial);
|
|
3583
|
+
button.setAttribute("aria-pressed", full ? "true" : partial ? "mixed" : "false");
|
|
3584
|
+
button.setAttribute("title", full ? `Remove all ${labels.length.toLocaleString()} seats in this category from the selection` : partial ? `Select the remaining ${(labels.length - picked).toLocaleString()} seats in this category` : `Select all ${labels.length.toLocaleString()} seats in this category`);
|
|
3585
|
+
const count = button.querySelector("[data-cat-count]");
|
|
3586
|
+
if (count) count.textContent = picked ? `${picked.toLocaleString()}/${labels.length.toLocaleString()}` : labels.length.toLocaleString();
|
|
3587
|
+
});
|
|
3588
|
+
}
|
|
3589
|
+
filteredBlockedSeats() {
|
|
3590
|
+
const query = this.blockedQuery.trim().toLocaleLowerCase();
|
|
3591
|
+
const seats = [];
|
|
3592
|
+
for (const [label, seat] of this.labelToSeat.entries()) {
|
|
3593
|
+
if (this.status.get(label) !== "blocked") continue;
|
|
3594
|
+
const sectionId = this.sectionByObject.get(seat.rowId) ?? UNGROUPED_ID;
|
|
3595
|
+
if (this.blockedSection && sectionId !== this.blockedSection) continue;
|
|
3596
|
+
if (query) {
|
|
3597
|
+
const category = this.doc?.categories.find((item) => item.key === seat.categoryKey)?.label ?? seat.categoryKey;
|
|
3598
|
+
const section = this.sectionLabelById.get(sectionId) ?? "Other seats";
|
|
3599
|
+
const object = this.doc?.objects.find((item) => item.id === seat.rowId);
|
|
3600
|
+
const objectLabel = object?.type === "row" || object?.type === "table" ? object.label : "";
|
|
3601
|
+
const haystack = `${label} ${category} ${section} ${objectLabel}`.toLocaleLowerCase();
|
|
3602
|
+
if (!haystack.includes(query)) continue;
|
|
3603
|
+
}
|
|
3604
|
+
seats.push(seat);
|
|
3605
|
+
}
|
|
3606
|
+
return seats.sort((a, b) => a.label.localeCompare(b.label, void 0, { numeric: true, sensitivity: "base" }));
|
|
3607
|
+
}
|
|
3608
|
+
paintBlockedInventory() {
|
|
3609
|
+
if (!this.els.blockedlist) return;
|
|
3610
|
+
const allBlocked = [...this.status.entries()].filter(([, status]) => status === "blocked").length;
|
|
3611
|
+
const filtered = this.filteredBlockedSeats();
|
|
3612
|
+
const visible = filtered.slice(0, this.blockedResultLimit);
|
|
3613
|
+
const selected = new Set(this.selectionLabels());
|
|
3614
|
+
const selectedResults = filtered.filter((seat) => selected.has(seat.label)).length;
|
|
3615
|
+
const allResultsSelected = filtered.length > 0 && selectedResults === filtered.length;
|
|
3616
|
+
this.els.blockedcount.textContent = allBlocked.toLocaleString();
|
|
3617
|
+
this.els.blockedshowing.textContent = filtered.length ? `Showing ${visible.length.toLocaleString()} of ${filtered.length.toLocaleString()}` : allBlocked ? "No matches" : "No blocked seats";
|
|
3618
|
+
const selectResults = this.els.selblocked;
|
|
3619
|
+
selectResults.disabled = filtered.length === 0;
|
|
3620
|
+
selectResults.textContent = allResultsSelected ? `Remove ${filtered.length.toLocaleString()} results` : `Select ${filtered.length.toLocaleString()} results`;
|
|
3621
|
+
this.els.blockedlist.innerHTML = visible.length ? visible.map((seat) => {
|
|
3622
|
+
const sectionId = this.sectionByObject.get(seat.rowId) ?? UNGROUPED_ID;
|
|
3623
|
+
const section = this.sectionLabelById.get(sectionId) ?? "Other seats";
|
|
3624
|
+
const category = this.doc?.categories.find((item) => item.key === seat.categoryKey)?.label ?? seat.categoryKey;
|
|
3625
|
+
const isSelected = selected.has(seat.label);
|
|
3626
|
+
return `<button type="button" class="slm-blockeditem${isSelected ? " on" : ""}" data-blocked-label="${esc(seat.label)}" aria-pressed="${isSelected}">
|
|
3627
|
+
<span class="slm-blockedcheck" aria-hidden="true">\u2713</span>
|
|
3628
|
+
<span class="slm-blockedcopy"><span class="slm-blockedlabel">${esc(seat.label)}</span>
|
|
3629
|
+
<span class="slm-blockedmeta">${esc(section)} \xB7 ${esc(category)}</span></span>
|
|
3630
|
+
</button>`;
|
|
3631
|
+
}).join("") + (filtered.length > visible.length ? `<button type="button" class="slm-blockedmore" data-blocked-more>Show 100 more</button>` : "") : `<div class="slm-blockedempty">${allBlocked ? "No blocked seats match this search or section." : "No seats are blocked. Newly blocked seats will appear here."}</div>`;
|
|
3632
|
+
const markAll = this.els.markall;
|
|
3633
|
+
const armed = markAll.dataset.confirm === "true";
|
|
3634
|
+
markAll.disabled = allBlocked === 0;
|
|
3635
|
+
markAll.textContent = armed ? `Confirm: put all ${allBlocked.toLocaleString()} on sale` : `Put all ${allBlocked.toLocaleString()} blocked seats on sale`;
|
|
3636
|
+
}
|
|
3637
|
+
confirmUnblockAll() {
|
|
3638
|
+
const button = this.els.markall;
|
|
3639
|
+
if (!button || button.disabled) return;
|
|
3640
|
+
if (button.dataset.confirm === "true") {
|
|
3641
|
+
this.resetUnblockAllConfirm();
|
|
3642
|
+
void this.unblockAll();
|
|
3643
|
+
return;
|
|
3644
|
+
}
|
|
3645
|
+
button.dataset.confirm = "true";
|
|
3646
|
+
button.classList.add("danger");
|
|
3647
|
+
this.els.markallnote.textContent = "This changes every blocked seat. Click the red button again to confirm.";
|
|
3648
|
+
this.paintBlockedInventory();
|
|
3649
|
+
if (this.unblockAllConfirmTimer) clearTimeout(this.unblockAllConfirmTimer);
|
|
3650
|
+
this.unblockAllConfirmTimer = setTimeout(() => this.resetUnblockAllConfirm(), 6e3);
|
|
3651
|
+
}
|
|
3652
|
+
resetUnblockAllConfirm() {
|
|
3653
|
+
if (this.unblockAllConfirmTimer) clearTimeout(this.unblockAllConfirmTimer);
|
|
3654
|
+
this.unblockAllConfirmTimer = null;
|
|
3655
|
+
const button = this.els.markall;
|
|
3656
|
+
if (!button) return;
|
|
3657
|
+
delete button.dataset.confirm;
|
|
3658
|
+
button.classList.remove("danger");
|
|
3659
|
+
if (this.els.markallnote) this.els.markallnote.textContent = "For a full reset only. You will be asked to confirm.";
|
|
3660
|
+
this.paintBlockedInventory();
|
|
3421
3661
|
}
|
|
3422
3662
|
// ---- toast / done / fail --------------------------------------------------
|
|
3423
3663
|
done(action, labels, msg) {
|