@zonetrix/viewer 2.10.0 → 2.10.2
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 +146 -2
- package/dist/index.js +1 -1
- package/dist/index.mjs +422 -408
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -106,9 +106,10 @@ function BookingApp() {
|
|
|
106
106
|
| `configUrl` | `string` | No* | URL to fetch configuration from |
|
|
107
107
|
| `floorId` | `string` | No | Filter seats/stages by floor ID (controlled mode) |
|
|
108
108
|
| `onFloorChange` | `(floorId: string) => void` | No | Callback when floor changes |
|
|
109
|
-
| `reservedSeats` | `string[]` | No | Array of seat IDs/numbers to mark as reserved |
|
|
109
|
+
| `reservedSeats` | `string[]` | No | Array of seat IDs/numbers to mark as reserved (others' reservations) |
|
|
110
110
|
| `unavailableSeats` | `string[]` | No | Array of seat IDs/numbers to mark as unavailable |
|
|
111
111
|
| `selectedSeats` | `string[]` | No | Array of seat IDs for controlled selection mode |
|
|
112
|
+
| `myReservedSeats` | `string[]` | No | Array of seat IDs reserved by current user (shown as selected/blue) |
|
|
112
113
|
| `onSeatSelect` | `(seat: SeatData) => void` | No | Callback when a seat is selected |
|
|
113
114
|
| `onSeatDeselect` | `(seat: SeatData) => void` | No | Callback when a seat is deselected |
|
|
114
115
|
| `onSelectionChange` | `(seats: SeatData[]) => void` | No | Callback when selection changes |
|
|
@@ -424,7 +425,150 @@ function CartIntegration() {
|
|
|
424
425
|
}
|
|
425
426
|
```
|
|
426
427
|
|
|
427
|
-
### 8.
|
|
428
|
+
### 8. Firebase Real-time Integration
|
|
429
|
+
|
|
430
|
+
The viewer includes built-in Firebase Realtime Database integration for instant seat state updates across all users.
|
|
431
|
+
|
|
432
|
+
#### Setup
|
|
433
|
+
|
|
434
|
+
```bash
|
|
435
|
+
npm install firebase @zonetrix/shared
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
Initialize Firebase in your app:
|
|
439
|
+
|
|
440
|
+
```tsx
|
|
441
|
+
import { initializeFirebaseForViewer } from '@zonetrix/viewer';
|
|
442
|
+
|
|
443
|
+
// Initialize once at app startup
|
|
444
|
+
initializeFirebaseForViewer({
|
|
445
|
+
apiKey: "your-api-key",
|
|
446
|
+
authDomain: "your-project.firebaseapp.com",
|
|
447
|
+
databaseURL: "https://your-project.firebaseio.com",
|
|
448
|
+
projectId: "your-project",
|
|
449
|
+
});
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
#### Basic Real-time Usage
|
|
453
|
+
|
|
454
|
+
```tsx
|
|
455
|
+
import { useRealtimeSeatMap, SeatMapViewer } from '@zonetrix/viewer';
|
|
456
|
+
|
|
457
|
+
function BookingPage({ seatMapId }) {
|
|
458
|
+
const {
|
|
459
|
+
config,
|
|
460
|
+
otherReservedSeats, // Reserved by others → yellow
|
|
461
|
+
unavailableSeats,
|
|
462
|
+
loading,
|
|
463
|
+
error
|
|
464
|
+
} = useRealtimeSeatMap({ seatMapId });
|
|
465
|
+
|
|
466
|
+
if (loading) return <LoadingSpinner />;
|
|
467
|
+
if (error) return <ErrorMessage error={error} />;
|
|
468
|
+
|
|
469
|
+
return (
|
|
470
|
+
<SeatMapViewer
|
|
471
|
+
config={config}
|
|
472
|
+
reservedSeats={otherReservedSeats}
|
|
473
|
+
unavailableSeats={unavailableSeats}
|
|
474
|
+
onSeatSelect={handleSeatSelect}
|
|
475
|
+
/>
|
|
476
|
+
);
|
|
477
|
+
}
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
#### User-Aware Reservations (Multi-User Booking)
|
|
481
|
+
|
|
482
|
+
When multiple users are booking simultaneously, pass `userId` to show each user's own reservations as "selected" while showing others' reservations as "reserved":
|
|
483
|
+
|
|
484
|
+
```tsx
|
|
485
|
+
import { useRealtimeSeatMap, SeatMapViewer } from '@zonetrix/viewer';
|
|
486
|
+
|
|
487
|
+
function BookingPage({ seatMapId, userId }) {
|
|
488
|
+
const {
|
|
489
|
+
config,
|
|
490
|
+
myReservedSeats, // Seats I reserved → blue (selected)
|
|
491
|
+
otherReservedSeats, // Seats others reserved → yellow (reserved)
|
|
492
|
+
unavailableSeats,
|
|
493
|
+
loading
|
|
494
|
+
} = useRealtimeSeatMap({ seatMapId, userId });
|
|
495
|
+
|
|
496
|
+
if (loading) return <LoadingSpinner />;
|
|
497
|
+
|
|
498
|
+
return (
|
|
499
|
+
<SeatMapViewer
|
|
500
|
+
config={config}
|
|
501
|
+
myReservedSeats={myReservedSeats} // Show as selected (blue)
|
|
502
|
+
reservedSeats={otherReservedSeats} // Show as reserved (yellow)
|
|
503
|
+
unavailableSeats={unavailableSeats}
|
|
504
|
+
onSeatSelect={handleSeatSelect}
|
|
505
|
+
/>
|
|
506
|
+
);
|
|
507
|
+
}
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
#### Firebase Data Structure
|
|
511
|
+
|
|
512
|
+
Seat states are stored at `seat_states/{seatMapId}/{seatId}`:
|
|
513
|
+
|
|
514
|
+
```javascript
|
|
515
|
+
// Reserved seat (with user tracking)
|
|
516
|
+
{
|
|
517
|
+
state: "reserved",
|
|
518
|
+
userId: "user-123",
|
|
519
|
+
timestamp: 1704931200000
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
// Unavailable seat
|
|
523
|
+
{
|
|
524
|
+
state: "unavailable",
|
|
525
|
+
timestamp: 1704931200000
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// Available seats: key doesn't exist (deleted)
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
#### Booking App Integration
|
|
532
|
+
|
|
533
|
+
Your booking backend should update Firebase when users reserve seats:
|
|
534
|
+
|
|
535
|
+
```javascript
|
|
536
|
+
import { getDatabase, ref, set, remove } from 'firebase/database';
|
|
537
|
+
|
|
538
|
+
const db = getDatabase();
|
|
539
|
+
|
|
540
|
+
// Reserve a seat for a user
|
|
541
|
+
async function reserveSeat(seatMapId, seatId, userId) {
|
|
542
|
+
await set(ref(db, `seat_states/${seatMapId}/${seatId}`), {
|
|
543
|
+
state: 'reserved',
|
|
544
|
+
userId: userId,
|
|
545
|
+
timestamp: Date.now()
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
// Release a seat (make available)
|
|
550
|
+
async function releaseSeat(seatMapId, seatId) {
|
|
551
|
+
await remove(ref(db, `seat_states/${seatMapId}/${seatId}`));
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
// Mark seat as unavailable (sold, blocked)
|
|
555
|
+
async function markUnavailable(seatMapId, seatId) {
|
|
556
|
+
await set(ref(db, `seat_states/${seatMapId}/${seatId}`), {
|
|
557
|
+
state: 'unavailable',
|
|
558
|
+
timestamp: Date.now()
|
|
559
|
+
});
|
|
560
|
+
}
|
|
561
|
+
```
|
|
562
|
+
|
|
563
|
+
#### Hooks Reference
|
|
564
|
+
|
|
565
|
+
| Hook | Description |
|
|
566
|
+
|------|-------------|
|
|
567
|
+
| `useFirebaseSeatStates` | Subscribe to real-time seat states only |
|
|
568
|
+
| `useFirebaseConfig` | Load seat map config from Firebase |
|
|
569
|
+
| `useRealtimeSeatMap` | Combined hook (config + real-time states) |
|
|
570
|
+
|
|
571
|
+
### 9. Error Handling
|
|
428
572
|
|
|
429
573
|
```tsx
|
|
430
574
|
import { SeatMapViewer } from '@zonetrix/viewer';
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const r=require("react/jsx-runtime"),t=require("react"),N=require("react-konva"),$=require("firebase/database"),ot=require("@zonetrix/shared");function je(n){const[s,c]=t.useState(null),[o,f]=t.useState(!1),[d,i]=t.useState(null),v=async()=>{if(n){f(!0),i(null);try{const p=await fetch(n);if(!p.ok)throw new Error(`Failed to fetch config: ${p.statusText}`);const a=await p.json();c(a)}catch(p){const a=p instanceof Error?p:new Error("Unknown error occurred");i(a),console.error("Failed to fetch seat map config:",a)}finally{f(!1)}}};return t.useEffect(()=>{v()},[n]),{config:s,loading:o,error:d,refetch:v}}function Re(n){const[s,c]=t.useState({width:0,height:0});return t.useEffect(()=>{const o=n.current;if(!o)return;const{width:f,height:d}=o.getBoundingClientRect();f>0&&d>0&&c({width:f,height:d});const i=new ResizeObserver(v=>{const p=v[0];if(!p)return;const{width:a,height:l}=p.contentRect;a>0&&l>0&&c(h=>h.width===a&&h.height===l?h:{width:a,height:l})});return i.observe(o),()=>{i.disconnect()}},[n]),s}function ve(n,s){return Math.sqrt(Math.pow(s.x-n.x,2)+Math.pow(s.y-n.y,2))}function we(n,s){return{x:(n.x+s.x)/2,y:(n.y+s.y)/2}}function Me(n,s){const c=t.useRef(null),o=t.useRef(null),f=t.useRef(1);t.useEffect(()=>{const d=n.current;if(!d||!s.enabled)return;const i=d.container(),v=l=>{if(l.touches.length===2){l.preventDefault();const h={x:l.touches[0].clientX,y:l.touches[0].clientY},g={x:l.touches[1].clientX,y:l.touches[1].clientY};c.current=ve(h,g),o.current=we(h,g),f.current=s.currentScale}},p=l=>{if(l.touches.length!==2)return;l.preventDefault();const h={x:l.touches[0].clientX,y:l.touches[0].clientY},g={x:l.touches[1].clientX,y:l.touches[1].clientY},w=ve(h,g),m=we(h,g);if(c.current!==null&&o.current!==null){const j=w/c.current,R=Math.min(Math.max(s.currentScale*j,s.minScale),s.maxScale),M=i.getBoundingClientRect(),E=m.x-M.left,k=m.y-M.top,X=s.currentScale,Y={x:(E-s.currentPosition.x)/X,y:(k-s.currentPosition.y)/X},B=m.x-o.current.x,z=m.y-o.current.y,A={x:E-Y.x*R+B,y:k-Y.y*R+z};s.onScaleChange(R,A),c.current=w,o.current=m}},a=l=>{l.touches.length<2&&(c.current=null,o.current=null)};return i.addEventListener("touchstart",v,{passive:!1}),i.addEventListener("touchmove",p,{passive:!1}),i.addEventListener("touchend",a),()=>{i.removeEventListener("touchstart",v),i.removeEventListener("touchmove",p),i.removeEventListener("touchend",a)}},[n,s])}const Ee={canvasBackground:"#1a1a1a",stageColor:"#808080",seatAvailable:"#2C2B30",seatReserved:"#FCEA00",seatSelected:"#3A7DE5",seatUnavailable:"#6b7280",seatHidden:"#4a4a4a",gridLines:"#404040",currency:"KD"},Fe=t.memo(({seat:n,state:s,colors:c,onClick:o,onMouseEnter:f,onMouseLeave:d})=>{const p={available:c.seatAvailable,reserved:c.seatReserved,selected:c.seatSelected,unavailable:c.seatUnavailable,hidden:c.seatHidden}[s],a=s==="available"||s==="selected",l=t.useCallback(()=>{a&&o(n)},[n,o,a]),h=t.useCallback(m=>{f(n,m);const j=m.target.getStage();j&&a&&(j.container().style.cursor="pointer")},[n,f,a]),g=t.useCallback(m=>{d();const j=m.target.getStage();j&&(j.container().style.cursor="grab")},[d]),w={x:n.position.x,y:n.position.y,fill:p,stroke:"#ffffff",strokeWidth:1,onClick:l,onTap:l,onMouseEnter:h,onMouseLeave:g};return n.shape==="circle"?r.jsx(N.Circle,{...w,radius:12}):r.jsx(N.Rect,{...w,width:24,height:24,offsetX:12,offsetY:12,cornerRadius:n.shape==="square"?0:4})});Fe.displayName="ViewerSeat";const ke=t.memo(({stage:n,stageColor:s})=>{const c=d=>({stage:"🎭",table:"⬜",wall:"▬",barrier:"🛡️","dj-booth":"🎵",bar:"🍷","entry-exit":"🚪",custom:"➕"})[d||"stage"]||"🎭",o=n.config.color||s,f=c(n.config.objectType);return r.jsxs(N.Group,{x:n.position.x,y:n.position.y,rotation:n.config.rotation||0,children:[r.jsx(N.Rect,{width:n.config.width,height:n.config.height,fill:o+"80",stroke:"#ffffff",strokeWidth:2,cornerRadius:10}),r.jsx(N.Text,{text:f,x:0,y:0,width:n.config.width,height:n.config.height*.4,fontSize:32,fill:"#ffffff",align:"center",verticalAlign:"middle"}),r.jsx(N.Text,{text:n.config.label,x:0,y:n.config.height*.4,width:n.config.width,height:n.config.height*.6,fontSize:20,fontStyle:"bold",fill:"#ffffff",align:"center",verticalAlign:"middle"})]})});ke.displayName="ViewerStage";const Ie=t.memo(({floors:n,currentFloorId:s,onFloorChange:c,showAllOption:o,allLabel:f,position:d,className:i})=>{const v=t.useMemo(()=>[...n].sort((g,w)=>g.order-w.order),[n]),a={position:"absolute",display:"flex",alignItems:"center",gap:"8px",padding:"8px 12px",backgroundColor:"rgba(26, 26, 26, 0.95)",borderRadius:"8px",margin:"12px",zIndex:10,...{"top-left":{top:0,left:0},"top-right":{top:0,right:0},"bottom-left":{bottom:0,left:0},"bottom-right":{bottom:0,right:0}}[d]},l={padding:"10px 16px",fontSize:"14px",fontWeight:500,border:"1px solid #444",borderRadius:"6px",backgroundColor:"transparent",color:"#fff",cursor:"pointer",transition:"all 0.2s ease",minHeight:"44px",touchAction:"manipulation"},h={...l,backgroundColor:"#3A7DE5",borderColor:"#3A7DE5"};return r.jsxs("div",{className:i,style:a,children:[o&&r.jsx("button",{type:"button",onClick:()=>c(null),style:s===null?h:l,children:f}),v.map(g=>r.jsx("button",{type:"button",onClick:()=>c(g.id),style:s===g.id?h:l,children:g.name},g.id))]})});Ie.displayName="FloorSelectorBar";const Le=t.memo(({scale:n,minScale:s,maxScale:c,onZoomIn:o,onZoomOut:f,position:d,className:i})=>{const p={position:"absolute",display:"flex",flexDirection:"column",gap:"4px",padding:"8px",backgroundColor:"rgba(26, 26, 26, 0.95)",borderRadius:"8px",margin:"12px",zIndex:10,...{"top-left":{top:0,left:0},"top-right":{top:0,right:0},"bottom-left":{bottom:0,left:0},"bottom-right":{bottom:0,right:0}}[d]},a={width:"44px",height:"44px",minWidth:"44px",minHeight:"44px",fontSize:"22px",fontWeight:"bold",border:"1px solid #444",borderRadius:"6px",backgroundColor:"transparent",color:"#fff",cursor:"pointer",display:"flex",alignItems:"center",justifyContent:"center",transition:"all 0.2s ease",touchAction:"manipulation"},l={...a,opacity:.4,cursor:"not-allowed"},h=n<c,g=n>s;return r.jsxs("div",{className:i,style:p,children:[r.jsx("button",{type:"button",onClick:o,disabled:!h,style:h?a:l,title:"Zoom In",children:"+"}),r.jsx("button",{type:"button",onClick:f,disabled:!g,style:g?a:l,title:"Zoom Out",children:"−"})]})});Le.displayName="ZoomControls";const De=t.memo(({visible:n,x:s,y:c,seat:o,currency:f,state:d})=>{if(!n||!o)return null;const i=o.seatNumber||(o.rowLabel&&o.columnLabel?`${o.rowLabel}-${o.columnLabel}`:"N/A"),v={position:"fixed",left:`${s+15}px`,top:`${c+15}px`,zIndex:1e3,pointerEvents:"none"},p={backgroundColor:"rgba(26, 26, 26, 0.95)",color:"#fff",border:"1px solid #444",borderRadius:"8px",padding:"8px 12px",fontSize:"13px",boxShadow:"0 4px 12px rgba(0, 0, 0, 0.3)",minWidth:"140px"},a={color:"#9ca3af",marginRight:"4px"},l={fontWeight:600},h={color:"#4ade80",fontWeight:600},g={fontSize:"11px",color:"#6b7280",textTransform:"capitalize",marginTop:"4px"};return r.jsx("div",{style:v,children:r.jsxs("div",{style:p,children:[o.sectionName&&r.jsxs("div",{style:{marginBottom:"4px"},children:[r.jsx("span",{style:a,children:"Section:"}),r.jsx("span",{style:{...l,color:"#3b82f6"},children:o.sectionName})]}),r.jsxs("div",{style:{marginBottom:"4px"},children:[r.jsx("span",{style:a,children:"Seat:"}),r.jsx("span",{style:l,children:i})]}),o.price!==void 0&&o.price>0&&d==="available"&&r.jsxs("div",{style:{marginBottom:"4px"},children:[r.jsx("span",{style:a,children:"Price:"}),r.jsxs("span",{style:h,children:[f," ",o.price.toFixed(2)]})]}),r.jsxs("div",{style:g,children:["Status: ",d]})]})})});De.displayName="SeatTooltip";const rt=({config:n,configUrl:s,floorId:c,onFloorChange:o,reservedSeats:f=[],unavailableSeats:d=[],selectedSeats:i,myReservedSeats:v=[],onSeatSelect:p,onSeatDeselect:a,onSelectionChange:l,colorOverrides:h,showTooltip:g=!0,zoomEnabled:w=!0,className:m="",onConfigLoad:j,onError:R,showFloorSelector:M,floorSelectorPosition:E="top-left",floorSelectorClassName:k,showAllFloorsOption:X=!0,allFloorsLabel:Y="All",fitToView:B=!0,fitPadding:z=40,showZoomControls:A=!0,zoomControlsPosition:G="bottom-right",zoomControlsClassName:he,minZoom:O,maxZoom:T=3,zoomStep:P=.25,touchEnabled:fe=!0})=>{const K=t.useRef(null),oe=t.useRef(null),F=Re(oe),[H,ye]=t.useState(new Set),[I,Z]=t.useState(1),[L,V]=t.useState({x:0,y:0}),[Ne,Xe]=t.useState(null),[Ye,Ae]=t.useState(1),re=t.useRef({width:0,height:0}),[J,me]=t.useState({visible:!1,x:0,y:0,seat:null,state:"available"}),{config:Pe,loading:We,error:Q}=je(s),x=n||Pe,ge=c!==void 0,D=ge?c||null:Ne,ie=i!==void 0,$e=t.useCallback(e=>{ge||Xe(e),o?.(e)},[ge,o]),pe=x?.floors||[],Be=M!==void 0?M:pe.length>1,ae=t.useMemo(()=>x?{...x.colors,...h}:{...Ee,...h},[x,h]),q=t.useMemo(()=>{if(!x)return[];let e=x.seats.filter(u=>u.state!=="hidden");return D&&(e=e.filter(u=>u.floorId===D||!u.floorId&&D==="floor_default")),e},[x,D]),ce=t.useMemo(()=>x?.stages?D?x.stages.filter(e=>e.floorId===D||!e.floorId&&D==="floor_default"):x.stages:[],[x,D]),W=t.useMemo(()=>{if(!x||q.length===0&&ce.length===0)return null;const e=12;let u=1/0,b=1/0,S=-1/0,y=-1/0;return q.forEach(C=>{u=Math.min(u,C.position.x-e),b=Math.min(b,C.position.y-e),S=Math.max(S,C.position.x+e),y=Math.max(y,C.position.y+e)}),ce.forEach(C=>{u=Math.min(u,C.position.x),b=Math.min(b,C.position.y),S=Math.max(S,C.position.x+(C.config?.width||200)),y=Math.max(y,C.position.y+(C.config?.height||100))}),{minX:u,minY:b,maxX:S,maxY:y,width:S-u,height:y-b}},[x,q,ce]);t.useEffect(()=>{if(!B||!x||!W||F.width===0||F.height===0)return;const e=Math.abs(F.width-re.current.width),u=Math.abs(F.height-re.current.height);if(!(re.current.width===0)&&e<10&&u<10)return;re.current=F;const S=F.width,y=F.height,C=S-z*2,ne=y-z*2,le=C/W.width,be=ne/W.height,ue=Math.min(le,be,T),Je=W.minX+W.width/2,Qe=W.minY+W.height/2,et=S/2,tt=y/2,nt=et-Je*ue,st=tt-Qe*ue;Z(ue),V({x:nt,y:st}),Ae(ue)},[B,x,W,z,T,F,D]);const U=t.useMemo(()=>{const e=new Set(f),u=new Set(d),b=new Set(v);return{reserved:e,unavailable:u,myReserved:b}},[f,d,v]),xe=t.useMemo(()=>i?new Set(i):null,[i]),ee=t.useCallback(e=>{const u=e.id,b=e.seatNumber||"";return U.unavailable.has(u)||U.unavailable.has(b)?"unavailable":U.reserved.has(u)||U.reserved.has(b)?"reserved":U.myReserved.has(u)||U.myReserved.has(b)||H.has(u)?"selected":e.state},[U,H]);t.useEffect(()=>{x&&j&&j(x)},[x,j]),t.useEffect(()=>{Q&&R&&R(Q)},[Q,R]),t.useEffect(()=>{ie&&xe&&ye(xe)},[ie,xe]);const Oe=t.useCallback(e=>{const u=ee(e);if(u!=="available"&&u!=="selected")return;const b=H.has(e.id);ie||ye(S=>{const y=new Set(S);return b?y.delete(e.id):y.add(e.id),y}),b?a?.(e):(p?.(e),p||console.log("Seat selected:",e))},[ee,H,ie,p,a]),te=t.useMemo(()=>x?q.filter(e=>H.has(e.id)):[],[q,H]);t.useEffect(()=>{l?.(te)},[te,l]);const _=O!==void 0?O:Ye,Ve=t.useCallback(()=>{if(!w)return;const e=Math.min(I+P,T);if(e!==I){const u=F.width||x?.canvas.width||800,b=F.height||x?.canvas.height||600,S=u/2,y=b/2,C={x:(S-L.x)/I,y:(y-L.y)/I};Z(e),V({x:S-C.x*e,y:y-C.y*e})}},[w,I,P,T,F,x,L]),Ue=t.useCallback(()=>{if(!w)return;const e=Math.max(I-P,_);if(e!==I){const u=F.width||x?.canvas.width||800,b=F.height||x?.canvas.height||600,S=u/2,y=b/2,C={x:(S-L.x)/I,y:(y-L.y)/I};Z(e),V({x:S-C.x*e,y:y-C.y*e})}},[w,I,P,_,F,x,L]),He=t.useCallback(e=>{V({x:e.target.x(),y:e.target.y()})},[]),qe=t.useCallback(e=>{if(!w)return;e.evt.preventDefault();const u=K.current;if(!u)return;const b=u.scaleX(),S=u.getPointerPosition();if(!S)return;const y=1.1,C=e.evt.deltaY>0?b/y:b*y,ne=Math.min(Math.max(C,_),T),le={x:(S.x-L.x)/b,y:(S.y-L.y)/b},be={x:S.x-le.x*ne,y:S.y-le.y*ne};Z(ne),V(be)},[w,L,_,T]);Me(K,{enabled:fe&&w,minScale:_,maxScale:T,currentScale:I,currentPosition:L,onScaleChange:(e,u)=>{Z(e),V(u)},onPositionChange:e=>{V(e)}});const _e=t.useCallback((e,u)=>{if(!g)return;const b=u.target.getStage();if(!b)return;const S=b.getPointerPosition();if(!S)return;const y=b.container().getBoundingClientRect();me({visible:!0,x:y.left+S.x,y:y.top+S.y,seat:e,state:ee(e)})},[g,ee]),Ge=t.useCallback(()=>{me(e=>({...e,visible:!1}))},[]);if(We)return r.jsx("div",{className:`flex items-center justify-center h-full ${m}`,children:r.jsx("p",{children:"Loading seat map..."})});if(Q)return r.jsx("div",{className:`flex items-center justify-center h-full ${m}`,children:r.jsxs("p",{className:"text-red-500",children:["Error loading seat map: ",Q.message]})});if(!x)return r.jsx("div",{className:`flex items-center justify-center h-full ${m}`,children:r.jsx("p",{children:"No configuration provided"})});const Ke=F.width||x.canvas.width,Ze=F.height||x.canvas.height;return r.jsxs("div",{ref:oe,className:`relative ${m}`,style:{width:"100%",height:"100%"},children:[Be&&pe.length>0&&r.jsx(Ie,{floors:pe,currentFloorId:D,onFloorChange:$e,showAllOption:X,allLabel:Y,position:E,className:k}),r.jsxs(N.Stage,{ref:K,width:Ke,height:Ze,scaleX:I,scaleY:I,x:L.x,y:L.y,draggable:!0,onDragEnd:He,onWheel:qe,style:{backgroundColor:x.canvas.backgroundColor,cursor:"grab"},children:[r.jsx(N.Layer,{listening:!1,children:ce.map(e=>r.jsx(ke,{stage:e,stageColor:ae.stageColor},e.id))}),r.jsx(N.Layer,{children:q.map(e=>r.jsx(Fe,{seat:e,state:ee(e),colors:ae,onClick:Oe,onMouseEnter:_e,onMouseLeave:Ge},e.id))})]}),g&&r.jsx(De,{visible:J.visible,x:J.x,y:J.y,seat:J.seat,currency:ae.currency,state:J.state}),A&&w&&r.jsx(Le,{scale:I,minScale:_,maxScale:T,onZoomIn:Ve,onZoomOut:Ue,position:G,className:he}),te.length>0&&r.jsxs("div",{className:"absolute top-4 right-4 bg-white dark:bg-gray-800 p-4 rounded shadow-lg",children:[r.jsxs("h3",{className:"font-semibold mb-2",children:["Selected Seats (",te.length,")"]}),r.jsx("div",{className:"max-h-48 overflow-y-auto space-y-1",children:te.map(e=>r.jsxs("div",{className:"text-sm",children:[e.seatNumber,e.price&&` - ${ae.currency} ${e.price.toFixed(2)}`]},e.id))})]})]})};let se=null;function it(n){se=n}function Se(){if(!se)throw new Error("Firebase database not initialized. Call initializeFirebaseForViewer(db) first.");return se}function de(){return se!==null}function at(){se=null}function Ce(n,s){const c=[],o=[],f=[];return Object.entries(n).forEach(([d,i])=>{i&&typeof i=="object"&&i.state&&(i.state==="unavailable"?f.push(d):i.state==="reserved"&&(s&&i.userId===s?c.push(d):o.push(d)))}),{myReservedSeats:c,otherReservedSeats:o,unavailableSeats:f}}function ze(n){const{seatMapId:s,currentUserId:c,enabled:o=!0,onStateChange:f,onError:d}=n,[i,v]=t.useState(null),[p,a]=t.useState(!0),[l,h]=t.useState(null),[g,w]=t.useState(null),[m,j]=t.useState([]),[R,M]=t.useState([]),[E,k]=t.useState([]),X=t.useRef(f),Y=t.useRef(d),B=t.useRef(c);return X.current=f,Y.current=d,B.current=c,t.useEffect(()=>{if(!o||!s){a(!1);return}if(!de()){a(!1),h(new Error("Firebase not initialized. Call initializeFirebaseForViewer first."));return}const z=Se(),A=$.ref(z,`seat_states/${s}`);a(!0),h(null);const G=O=>{const P=O.val()||{};v(P),a(!1),w(Date.now());const{myReservedSeats:fe,otherReservedSeats:K,unavailableSeats:oe}=Ce(P,B.current);j(fe),M(K),k(oe),X.current?.(P)},he=O=>{h(O),a(!1),Y.current?.(O)};return $.onValue(A,G,he),()=>{$.off(A)}},[s,o]),t.useEffect(()=>{if(i){const{myReservedSeats:z,otherReservedSeats:A,unavailableSeats:G}=Ce(i,c);j(z),M(A),k(G)}},[c,i]),{states:i,loading:p,error:l,lastUpdated:g,myReservedSeats:m,otherReservedSeats:R,unavailableSeats:E,reservedSeats:R}}function Te(n){const{seatMapId:s,enabled:c=!0,subscribeToChanges:o=!1,onConfigLoad:f,onError:d}=n,[i,v]=t.useState(null),[p,a]=t.useState(!0),[l,h]=t.useState(null),g=t.useRef(f),w=t.useRef(d);g.current=f,w.current=d;const m=t.useCallback(async()=>{if(!s)return;if(!de()){h(new Error("Firebase not initialized. Call initializeFirebaseForViewer first.")),a(!1);return}const j=Se(),R=$.ref(j,`seatmaps/${s}`);try{a(!0),h(null);const E=(await $.get(R)).val();if(E){const k=ot.fromFirebaseSeatMap(E);v(k),g.current?.(k)}else h(new Error(`Seat map ${s} not found in Firebase`))}catch(M){const E=M instanceof Error?M:new Error("Unknown error");h(E),w.current?.(E)}finally{a(!1)}},[s]);return t.useEffect(()=>{if(!c||!s){a(!1);return}if(m(),o&&de()){const j=Se(),R=$.ref(j,`seatmaps/${s}/meta/updated_at`);let M=!0;const E=k=>{if(M){M=!1;return}k.exists()&&m()};return $.onValue(R,E),()=>{$.off(R)}}},[s,c,o,m]),{config:i,loading:p,error:l,refetch:m}}function ct(n){const{seatMapId:s,userId:c,enabled:o=!0,subscribeToDesignChanges:f=!1,onConfigLoad:d,onStateChange:i,onError:v}=n,{config:p,loading:a,error:l,refetch:h}=Te({seatMapId:s,enabled:o,subscribeToChanges:f,onConfigLoad:d,onError:v}),{states:g,loading:w,error:m,lastUpdated:j,myReservedSeats:R,otherReservedSeats:M,unavailableSeats:E,reservedSeats:k}=ze({seatMapId:s,currentUserId:c,enabled:o,onStateChange:i,onError:v});return{config:p,loading:a||w,error:l||m,myReservedSeats:R,otherReservedSeats:M,unavailableSeats:E,reservedSeats:k,seatStates:g,lastUpdated:j,refetch:h}}exports.DEFAULT_COLORS=Ee;exports.SeatMapViewer=rt;exports.clearFirebaseInstance=at;exports.initializeFirebaseForViewer=it;exports.isFirebaseInitialized=de;exports.useConfigFetcher=je;exports.useContainerSize=Re;exports.useFirebaseConfig=Te;exports.useFirebaseSeatStates=ze;exports.useRealtimeSeatMap=ct;exports.useTouchGestures=Me;
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const i=require("react/jsx-runtime"),e=require("react"),O=require("react-konva"),V=require("firebase/database"),it=require("@zonetrix/shared");function Me(n){const[s,o]=e.useState(null),[r,h]=e.useState(!1),[d,c]=e.useState(null),m=async()=>{if(n){h(!0),c(null);try{const p=await fetch(n);if(!p.ok)throw new Error(`Failed to fetch config: ${p.statusText}`);const a=await p.json();o(a)}catch(p){const a=p instanceof Error?p:new Error("Unknown error occurred");c(a),console.error("Failed to fetch seat map config:",a)}finally{h(!1)}}};return e.useEffect(()=>{m()},[n]),{config:s,loading:r,error:d,refetch:m}}function Ee(n){const[s,o]=e.useState({width:0,height:0});return e.useEffect(()=>{const r=n.current;if(!r)return;const{width:h,height:d}=r.getBoundingClientRect();h>0&&d>0&&o({width:h,height:d});const c=new ResizeObserver(m=>{const p=m[0];if(!p)return;const{width:a,height:l}=p.contentRect;a>0&&l>0&&o(f=>f.width===a&&f.height===l?f:{width:a,height:l})});return c.observe(r),()=>{c.disconnect()}},[n]),s}function we(n,s){return Math.sqrt(Math.pow(s.x-n.x,2)+Math.pow(s.y-n.y,2))}function Ce(n,s){return{x:(n.x+s.x)/2,y:(n.y+s.y)/2}}function Fe(n,s){const o=e.useRef(null),r=e.useRef(null),h=e.useRef(1);e.useEffect(()=>{const d=n.current;if(!d||!s.enabled)return;const c=d.container(),m=l=>{if(l.touches.length===2){l.preventDefault();const f={x:l.touches[0].clientX,y:l.touches[0].clientY},g={x:l.touches[1].clientX,y:l.touches[1].clientY};o.current=we(f,g),r.current=Ce(f,g),h.current=s.currentScale}},p=l=>{if(l.touches.length!==2)return;l.preventDefault();const f={x:l.touches[0].clientX,y:l.touches[0].clientY},g={x:l.touches[1].clientX,y:l.touches[1].clientY},v=we(f,g),w=Ce(f,g);if(o.current!==null&&r.current!==null){const R=v/o.current,E=Math.min(Math.max(s.currentScale*R,s.minScale),s.maxScale),F=c.getBoundingClientRect(),j=w.x-F.left,k=w.y-F.top,D=s.currentScale,T={x:(j-s.currentPosition.x)/D,y:(k-s.currentPosition.y)/D},U=w.x-r.current.x,z=w.y-r.current.y,_={x:j-T.x*E+U,y:k-T.y*E+z};s.onScaleChange(E,_),o.current=v,r.current=w}},a=l=>{l.touches.length<2&&(o.current=null,r.current=null)};return c.addEventListener("touchstart",m,{passive:!1}),c.addEventListener("touchmove",p,{passive:!1}),c.addEventListener("touchend",a),()=>{c.removeEventListener("touchstart",m),c.removeEventListener("touchmove",p),c.removeEventListener("touchend",a)}},[n,s])}const ke={canvasBackground:"#1a1a1a",stageColor:"#808080",seatAvailable:"#2C2B30",seatReserved:"#FCEA00",seatSelected:"#3A7DE5",seatUnavailable:"#6b7280",seatHidden:"#4a4a4a",gridLines:"#404040",currency:"KD"},Ie=e.memo(({seat:n,state:s,colors:o,onClick:r,onMouseEnter:h,onMouseLeave:d})=>{const p={available:o.seatAvailable,reserved:o.seatReserved,selected:o.seatSelected,unavailable:o.seatUnavailable,hidden:o.seatHidden}[s],a=s==="available"||s==="selected",l=e.useCallback(()=>{a&&r(n)},[n,r,a]),f=e.useCallback(w=>{h(n,w);const R=w.target.getStage();R&&a&&(R.container().style.cursor="pointer")},[n,h,a]),g=e.useCallback(w=>{d();const R=w.target.getStage();R&&(R.container().style.cursor="grab")},[d]),v={x:n.position.x,y:n.position.y,fill:p,stroke:"#ffffff",strokeWidth:1,onClick:l,onTap:l,onMouseEnter:f,onMouseLeave:g};return n.shape==="circle"?i.jsx(O.Circle,{...v,radius:12}):i.jsx(O.Rect,{...v,width:24,height:24,offsetX:12,offsetY:12,cornerRadius:n.shape==="square"?0:4})});Ie.displayName="ViewerSeat";const De=e.memo(({stage:n,stageColor:s})=>{const o=d=>({stage:"🎭",table:"⬜",wall:"▬",barrier:"🛡️","dj-booth":"🎵",bar:"🍷","entry-exit":"🚪",custom:"➕"})[d||"stage"]||"🎭",r=n.config.color||s,h=o(n.config.objectType);return i.jsxs(O.Group,{x:n.position.x,y:n.position.y,rotation:n.config.rotation||0,children:[i.jsx(O.Rect,{width:n.config.width,height:n.config.height,fill:r+"80",stroke:"#ffffff",strokeWidth:2,cornerRadius:10}),i.jsx(O.Text,{text:h,x:0,y:0,width:n.config.width,height:n.config.height*.4,fontSize:32,fill:"#ffffff",align:"center",verticalAlign:"middle"}),i.jsx(O.Text,{text:n.config.label,x:0,y:n.config.height*.4,width:n.config.width,height:n.config.height*.6,fontSize:20,fontStyle:"bold",fill:"#ffffff",align:"center",verticalAlign:"middle"})]})});De.displayName="ViewerStage";const Le=e.memo(({floors:n,currentFloorId:s,onFloorChange:o,showAllOption:r,allLabel:h,position:d,className:c})=>{const m=e.useMemo(()=>[...n].sort((g,v)=>g.order-v.order),[n]),a={position:"absolute",display:"flex",alignItems:"center",gap:"8px",padding:"8px 12px",backgroundColor:"rgba(26, 26, 26, 0.95)",borderRadius:"8px",margin:"12px",zIndex:10,...{"top-left":{top:0,left:0},"top-right":{top:0,right:0},"bottom-left":{bottom:0,left:0},"bottom-right":{bottom:0,right:0}}[d]},l={padding:"10px 16px",fontSize:"14px",fontWeight:500,border:"1px solid #444",borderRadius:"6px",backgroundColor:"transparent",color:"#fff",cursor:"pointer",transition:"all 0.2s ease",minHeight:"44px",touchAction:"manipulation"},f={...l,backgroundColor:"#3A7DE5",borderColor:"#3A7DE5"};return i.jsxs("div",{className:c,style:a,children:[r&&i.jsx("button",{type:"button",onClick:()=>o(null),style:s===null?f:l,children:h}),m.map(g=>i.jsx("button",{type:"button",onClick:()=>o(g.id),style:s===g.id?f:l,children:g.name},g.id))]})});Le.displayName="FloorSelectorBar";const Te=e.memo(({scale:n,minScale:s,maxScale:o,onZoomIn:r,onZoomOut:h,position:d,className:c})=>{const p={position:"absolute",display:"flex",flexDirection:"column",gap:"4px",padding:"8px",backgroundColor:"rgba(26, 26, 26, 0.95)",borderRadius:"8px",margin:"12px",zIndex:10,...{"top-left":{top:0,left:0},"top-right":{top:0,right:0},"bottom-left":{bottom:0,left:0},"bottom-right":{bottom:0,right:0}}[d]},a={width:"44px",height:"44px",minWidth:"44px",minHeight:"44px",fontSize:"22px",fontWeight:"bold",border:"1px solid #444",borderRadius:"6px",backgroundColor:"transparent",color:"#fff",cursor:"pointer",display:"flex",alignItems:"center",justifyContent:"center",transition:"all 0.2s ease",touchAction:"manipulation"},l={...a,opacity:.4,cursor:"not-allowed"},f=n<o,g=n>s;return i.jsxs("div",{className:c,style:p,children:[i.jsx("button",{type:"button",onClick:r,disabled:!f,style:f?a:l,title:"Zoom In",children:"+"}),i.jsx("button",{type:"button",onClick:h,disabled:!g,style:g?a:l,title:"Zoom Out",children:"−"})]})});Te.displayName="ZoomControls";const ze=e.memo(({visible:n,x:s,y:o,seat:r,currency:h,state:d})=>{if(!n||!r)return null;const c=r.seatNumber||(r.rowLabel&&r.columnLabel?`${r.rowLabel}-${r.columnLabel}`:"N/A"),m={position:"fixed",left:`${s+15}px`,top:`${o+15}px`,zIndex:1e3,pointerEvents:"none"},p={backgroundColor:"rgba(26, 26, 26, 0.95)",color:"#fff",border:"1px solid #444",borderRadius:"8px",padding:"8px 12px",fontSize:"13px",boxShadow:"0 4px 12px rgba(0, 0, 0, 0.3)",minWidth:"140px"},a={color:"#9ca3af",marginRight:"4px"},l={fontWeight:600},f={color:"#4ade80",fontWeight:600},g={fontSize:"11px",color:"#6b7280",textTransform:"capitalize",marginTop:"4px"};return i.jsx("div",{style:m,children:i.jsxs("div",{style:p,children:[r.sectionName&&i.jsxs("div",{style:{marginBottom:"4px"},children:[i.jsx("span",{style:a,children:"Section:"}),i.jsx("span",{style:{...l,color:"#3b82f6"},children:r.sectionName})]}),i.jsxs("div",{style:{marginBottom:"4px"},children:[i.jsx("span",{style:a,children:"Seat:"}),i.jsx("span",{style:l,children:c})]}),r.price!==void 0&&r.price>0&&d==="available"&&i.jsxs("div",{style:{marginBottom:"4px"},children:[i.jsx("span",{style:a,children:"Price:"}),i.jsxs("span",{style:f,children:[h," ",r.price.toFixed(2)]})]}),i.jsxs("div",{style:g,children:["Status: ",d]})]})})});ze.displayName="SeatTooltip";const at=({config:n,configUrl:s,floorId:o,onFloorChange:r,reservedSeats:h=[],unavailableSeats:d=[],selectedSeats:c,myReservedSeats:m=[],onSeatSelect:p,onSeatDeselect:a,onSelectionChange:l,colorOverrides:f,showTooltip:g=!0,zoomEnabled:v=!0,className:w="",onConfigLoad:R,onError:E,showFloorSelector:F,floorSelectorPosition:j="top-left",floorSelectorClassName:k,showAllFloorsOption:D=!0,allFloorsLabel:T="All",fitToView:U=!0,fitPadding:z=40,showZoomControls:_=!0,zoomControlsPosition:X="bottom-right",zoomControlsClassName:G,minZoom:Z,maxZoom:N=3,zoomStep:Y=.25,touchEnabled:ve=!0})=>{const P=e.useRef(null),$=e.useRef(null),M=Ee($),[W,he]=e.useState(new Set),[I,Q]=e.useState(1),[L,H]=e.useState({x:0,y:0}),[Xe,Ye]=e.useState(null),[Pe,We]=e.useState(1),ie=e.useRef({width:0,height:0}),[ee,me]=e.useState({visible:!1,x:0,y:0,seat:null,state:"available"}),{config:Oe,loading:$e,error:te}=Me(s),b=n||Oe,ge=o!==void 0,A=ge?o||null:Xe,ae=c!==void 0,Be=e.useCallback(t=>{ge||Ye(t),r?.(t)},[ge,r]),pe=b?.floors||[],Ve=F!==void 0?F:pe.length>1,ce=e.useMemo(()=>b?{...b.colors,...f}:{...ke,...f},[b,f]),J=e.useMemo(()=>{if(!b)return[];let t=b.seats.filter(u=>u.state!=="hidden");return A&&(t=t.filter(u=>u.floorId===A||!u.floorId&&A==="floor_default")),t},[b,A]),le=e.useMemo(()=>b?.stages?A?b.stages.filter(t=>t.floorId===A||!t.floorId&&A==="floor_default"):b.stages:[],[b,A]),B=e.useMemo(()=>{if(!b||J.length===0&&le.length===0)return null;const t=12;let u=1/0,x=1/0,y=-1/0,S=-1/0;return J.forEach(C=>{u=Math.min(u,C.position.x-t),x=Math.min(x,C.position.y-t),y=Math.max(y,C.position.x+t),S=Math.max(S,C.position.y+t)}),le.forEach(C=>{u=Math.min(u,C.position.x),x=Math.min(x,C.position.y),y=Math.max(y,C.position.x+(C.config?.width||200)),S=Math.max(S,C.position.y+(C.config?.height||100))}),{minX:u,minY:x,maxX:y,maxY:S,width:y-u,height:S-x}},[b,J,le]);e.useEffect(()=>{if(!U||!b||!B||M.width===0||M.height===0)return;const t=Math.abs(M.width-ie.current.width),u=Math.abs(M.height-ie.current.height);if(!(ie.current.width===0)&&t<10&&u<10)return;ie.current=M;const y=M.width,S=M.height,C=y-z*2,re=S-z*2,ue=C/B.width,xe=re/B.height,de=Math.min(ue,xe,N),et=B.minX+B.width/2,tt=B.minY+B.height/2,nt=y/2,st=S/2,rt=nt-et*de,ot=st-tt*de;Q(de),H({x:rt,y:ot}),We(de)},[U,b,B,z,N,M,A]);const q=e.useMemo(()=>{const t=new Set(h),u=new Set(d),x=new Set(m);return{reserved:t,unavailable:u,myReserved:x}},[h,d,m]),be=e.useMemo(()=>c?new Set(c):null,[c]),ne=e.useCallback(t=>{const u=t.id,x=t.seatNumber||"";return q.unavailable.has(u)||q.unavailable.has(x)?"unavailable":q.reserved.has(u)||q.reserved.has(x)?"reserved":q.myReserved.has(u)||q.myReserved.has(x)||W.has(u)?"selected":t.state},[q,W]);e.useEffect(()=>{b&&R&&R(b)},[b,R]),e.useEffect(()=>{te&&E&&E(te)},[te,E]),e.useEffect(()=>{ae&&be&&he(be)},[ae,be]);const Ue=e.useCallback(t=>{const u=ne(t);if(u!=="available"&&u!=="selected")return;const x=W.has(t.id);ae||he(y=>{const S=new Set(y);return x?S.delete(t.id):S.add(t.id),S}),x?a?.(t):(p?.(t),p||console.log("Seat selected:",t))},[ne,W,ae,p,a]),se=e.useMemo(()=>b?J.filter(t=>W.has(t.id)):[],[J,W]);e.useEffect(()=>{l?.(se)},[se,l]);const K=Z!==void 0?Z:Pe,He=e.useCallback(()=>{if(!v)return;const t=Math.min(I+Y,N);if(t!==I){const u=M.width||b?.canvas.width||800,x=M.height||b?.canvas.height||600,y=u/2,S=x/2,C={x:(y-L.x)/I,y:(S-L.y)/I};Q(t),H({x:y-C.x*t,y:S-C.y*t})}},[v,I,Y,N,M,b,L]),qe=e.useCallback(()=>{if(!v)return;const t=Math.max(I-Y,K);if(t!==I){const u=M.width||b?.canvas.width||800,x=M.height||b?.canvas.height||600,y=u/2,S=x/2,C={x:(y-L.x)/I,y:(S-L.y)/I};Q(t),H({x:y-C.x*t,y:S-C.y*t})}},[v,I,Y,K,M,b,L]),_e=e.useCallback(t=>{H({x:t.target.x(),y:t.target.y()})},[]),Ge=e.useCallback(t=>{if(!v)return;t.evt.preventDefault();const u=P.current;if(!u)return;const x=u.scaleX(),y=u.getPointerPosition();if(!y)return;const S=1.1,C=t.evt.deltaY>0?x/S:x*S,re=Math.min(Math.max(C,K),N),ue={x:(y.x-L.x)/x,y:(y.y-L.y)/x},xe={x:y.x-ue.x*re,y:y.y-ue.y*re};Q(re),H(xe)},[v,L,K,N]);Fe(P,{enabled:ve&&v,minScale:K,maxScale:N,currentScale:I,currentPosition:L,onScaleChange:(t,u)=>{Q(t),H(u)},onPositionChange:t=>{H(t)}});const Je=e.useCallback((t,u)=>{if(!g)return;const x=u.target.getStage();if(!x)return;const y=x.getPointerPosition();if(!y)return;const S=x.container().getBoundingClientRect();me({visible:!0,x:S.left+y.x,y:S.top+y.y,seat:t,state:ne(t)})},[g,ne]),Ke=e.useCallback(()=>{me(t=>({...t,visible:!1}))},[]);if($e)return i.jsx("div",{className:`flex items-center justify-center h-full ${w}`,children:i.jsx("p",{children:"Loading seat map..."})});if(te)return i.jsx("div",{className:`flex items-center justify-center h-full ${w}`,children:i.jsxs("p",{className:"text-red-500",children:["Error loading seat map: ",te.message]})});if(!b)return i.jsx("div",{className:`flex items-center justify-center h-full ${w}`,children:i.jsx("p",{children:"No configuration provided"})});const Ze=M.width||b.canvas.width,Qe=M.height||b.canvas.height;return i.jsxs("div",{ref:$,className:`relative ${w}`,style:{width:"100%",height:"100%"},children:[Ve&&pe.length>0&&i.jsx(Le,{floors:pe,currentFloorId:A,onFloorChange:Be,showAllOption:D,allLabel:T,position:j,className:k}),i.jsxs(O.Stage,{ref:P,width:Ze,height:Qe,scaleX:I,scaleY:I,x:L.x,y:L.y,draggable:!0,onDragEnd:_e,onWheel:Ge,style:{backgroundColor:b.canvas.backgroundColor,cursor:"grab"},children:[i.jsx(O.Layer,{listening:!1,children:le.map(t=>i.jsx(De,{stage:t,stageColor:ce.stageColor},t.id))}),i.jsx(O.Layer,{children:J.map(t=>i.jsx(Ie,{seat:t,state:ne(t),colors:ce,onClick:Ue,onMouseEnter:Je,onMouseLeave:Ke},t.id))})]}),g&&i.jsx(ze,{visible:ee.visible,x:ee.x,y:ee.y,seat:ee.seat,currency:ce.currency,state:ee.state}),_&&v&&i.jsx(Te,{scale:I,minScale:K,maxScale:N,onZoomIn:He,onZoomOut:qe,position:X,className:G}),se.length>0&&i.jsxs("div",{className:"absolute top-4 right-4 bg-white dark:bg-gray-800 p-4 rounded shadow-lg",children:[i.jsxs("h3",{className:"font-semibold mb-2",children:["Selected Seats (",se.length,")"]}),i.jsx("div",{className:"max-h-48 overflow-y-auto space-y-1",children:se.map(t=>i.jsxs("div",{className:"text-sm",children:[t.seatNumber,t.price&&` - ${ce.currency} ${t.price.toFixed(2)}`]},t.id))})]})]})};let oe=null;function ct(n){oe=n}function Se(){if(!oe)throw new Error("Firebase database not initialized. Call initializeFirebaseForViewer(db) first.");return oe}function fe(){return oe!==null}function lt(){oe=null}function ye(n,s){if(n.length!==s.length)return!1;for(let o=0;o<n.length;o++)if(n[o]!==s[o])return!1;return!0}function Re(n,s){return ye(n.myReserved,s.myReserved)&&ye(n.otherReserved,s.otherReserved)&&ye(n.unavailable,s.unavailable)}function je(n,s){const o=[],r=[],h=[];return Object.entries(n).forEach(([d,c])=>{c&&typeof c=="object"&&c.state&&(c.state==="unavailable"?h.push(d):c.state==="reserved"&&(s&&c.userId===s?o.push(d):r.push(d)))}),{myReserved:o,otherReserved:r,unavailable:h}}function Ne(n){const{seatMapId:s,currentUserId:o,enabled:r=!0,onStateChange:h,onError:d}=n,[c,m]=e.useState(null),[p,a]=e.useState(!0),[l,f]=e.useState(null),[g,v]=e.useState(null),[w,R]=e.useState([]),[E,F]=e.useState([]),[j,k]=e.useState([]),D=e.useRef(h),T=e.useRef(d),U=e.useRef(o);D.current=h,T.current=d,U.current=o;const z=e.useRef({myReserved:[],otherReserved:[],unavailable:[]}),_=e.useRef(null);return e.useEffect(()=>{if(!r||!s){a(!1);return}if(!fe()){a(!1),f(new Error("Firebase not initialized. Call initializeFirebaseForViewer first."));return}const X=Se(),G=V.ref(X,`seat_states/${s}`);a(!0),f(null);const Z=Y=>{const P=Y.val()||{},$=je(P,U.current),M=z.current,W=JSON.stringify(_.current)!==JSON.stringify(P);W&&(m(P),_.current=P),a(!1),!Re(M,$)&&(v(Date.now()),R($.myReserved),F($.otherReserved),k($.unavailable),z.current=$),W&&D.current?.(P)},N=Y=>{f(Y),a(!1),T.current?.(Y)};return V.onValue(G,Z,N),()=>{V.off(G)}},[s,r]),e.useEffect(()=>{if(c){const X=je(c,o),G=z.current;!Re(G,X)&&(R(X.myReserved),F(X.otherReserved),k(X.unavailable),z.current=X,v(Date.now()))}},[o]),{states:c,loading:p,error:l,lastUpdated:g,myReservedSeats:w,otherReservedSeats:E,unavailableSeats:j,reservedSeats:E}}function Ae(n){const{seatMapId:s,enabled:o=!0,subscribeToChanges:r=!1,onConfigLoad:h,onError:d}=n,[c,m]=e.useState(null),[p,a]=e.useState(!0),[l,f]=e.useState(null),g=e.useRef(h),v=e.useRef(d);g.current=h,v.current=d;const w=e.useCallback(async()=>{if(!s)return;if(!fe()){f(new Error("Firebase not initialized. Call initializeFirebaseForViewer first.")),a(!1);return}const R=Se(),E=V.ref(R,`seatmaps/${s}`);try{a(!0),f(null);const j=(await V.get(E)).val();if(j){const k=it.fromFirebaseSeatMap(j);m(k),g.current?.(k)}else f(new Error(`Seat map ${s} not found in Firebase`))}catch(F){const j=F instanceof Error?F:new Error("Unknown error");f(j),v.current?.(j)}finally{a(!1)}},[s]);return e.useEffect(()=>{if(!o||!s){a(!1);return}if(w(),r&&fe()){const R=Se(),E=V.ref(R,`seatmaps/${s}/meta/updated_at`);let F=!0,j=null;const k=D=>{if(F){F=!1,j=D.val();return}const T=D.val();D.exists()&&T!==j&&(j=T,w())};return V.onValue(E,k),()=>{V.off(E)}}},[s,o,r]),{config:c,loading:p,error:l,refetch:w}}function ut(n){const{seatMapId:s,userId:o,enabled:r=!0,subscribeToDesignChanges:h=!1,onConfigLoad:d,onStateChange:c,onError:m}=n,{config:p,loading:a,error:l,refetch:f}=Ae({seatMapId:s,enabled:r,subscribeToChanges:h,onConfigLoad:d,onError:m}),{states:g,loading:v,error:w,lastUpdated:R,myReservedSeats:E,otherReservedSeats:F,unavailableSeats:j,reservedSeats:k}=Ne({seatMapId:s,currentUserId:o,enabled:r,onStateChange:c,onError:m});return{config:p,loading:a||v,error:l||w,myReservedSeats:E,otherReservedSeats:F,unavailableSeats:j,reservedSeats:k,seatStates:g,lastUpdated:R,refetch:f}}exports.DEFAULT_COLORS=ke;exports.SeatMapViewer=at;exports.clearFirebaseInstance=lt;exports.initializeFirebaseForViewer=ct;exports.isFirebaseInitialized=fe;exports.useConfigFetcher=Me;exports.useContainerSize=Ee;exports.useFirebaseConfig=Ae;exports.useFirebaseSeatStates=Ne;exports.useRealtimeSeatMap=ut;exports.useTouchGestures=Fe;
|