@zonetrix/viewer 2.10.0 → 2.10.1

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 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. Error Handling
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"),t=require("react"),P=require("react-konva"),B=require("firebase/database"),rt=require("@zonetrix/shared");function Re(n){const[s,o]=t.useState(null),[r,h]=t.useState(!1),[d,l]=t.useState(null),v=async()=>{if(n){h(!0),l(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");l(a),console.error("Failed to fetch seat map config:",a)}finally{h(!1)}}};return t.useEffect(()=>{v()},[n]),{config:s,loading:r,error:d,refetch:v}}function je(n){const[s,o]=t.useState({width:0,height:0});return t.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 l=new ResizeObserver(v=>{const p=v[0];if(!p)return;const{width:a,height:c}=p.contentRect;a>0&&c>0&&o(f=>f.width===a&&f.height===c?f:{width:a,height:c})});return l.observe(r),()=>{l.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 Me(n,s){const o=t.useRef(null),r=t.useRef(null),h=t.useRef(1);t.useEffect(()=>{const d=n.current;if(!d||!s.enabled)return;const l=d.container(),v=c=>{if(c.touches.length===2){c.preventDefault();const f={x:c.touches[0].clientX,y:c.touches[0].clientY},g={x:c.touches[1].clientX,y:c.touches[1].clientY};o.current=we(f,g),r.current=Ce(f,g),h.current=s.currentScale}},p=c=>{if(c.touches.length!==2)return;c.preventDefault();const f={x:c.touches[0].clientX,y:c.touches[0].clientY},g={x:c.touches[1].clientX,y:c.touches[1].clientY},m=we(f,g),w=Ce(f,g);if(o.current!==null&&r.current!==null){const R=m/o.current,E=Math.min(Math.max(s.currentScale*R,s.minScale),s.maxScale),F=l.getBoundingClientRect(),j=w.x-F.left,I=w.y-F.top,L=s.currentScale,T={x:(j-s.currentPosition.x)/L,y:(I-s.currentPosition.y)/L},V=w.x-r.current.x,W=w.y-r.current.y,q={x:j-T.x*E+V,y:I-T.y*E+W};s.onScaleChange(E,q),o.current=m,r.current=w}},a=c=>{c.touches.length<2&&(o.current=null,r.current=null)};return l.addEventListener("touchstart",v,{passive:!1}),l.addEventListener("touchmove",p,{passive:!1}),l.addEventListener("touchend",a),()=>{l.removeEventListener("touchstart",v),l.removeEventListener("touchmove",p),l.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: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",c=t.useCallback(()=>{a&&r(n)},[n,r,a]),f=t.useCallback(w=>{h(n,w);const R=w.target.getStage();R&&a&&(R.container().style.cursor="pointer")},[n,h,a]),g=t.useCallback(w=>{d();const R=w.target.getStage();R&&(R.container().style.cursor="grab")},[d]),m={x:n.position.x,y:n.position.y,fill:p,stroke:"#ffffff",strokeWidth:1,onClick:c,onTap:c,onMouseEnter:f,onMouseLeave:g};return n.shape==="circle"?i.jsx(P.Circle,{...m,radius:12}):i.jsx(P.Rect,{...m,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 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(P.Group,{x:n.position.x,y:n.position.y,rotation:n.config.rotation||0,children:[i.jsx(P.Rect,{width:n.config.width,height:n.config.height,fill:r+"80",stroke:"#ffffff",strokeWidth:2,cornerRadius:10}),i.jsx(P.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(P.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:o,showAllOption:r,allLabel:h,position:d,className:l})=>{const v=t.useMemo(()=>[...n].sort((g,m)=>g.order-m.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]},c={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={...c,backgroundColor:"#3A7DE5",borderColor:"#3A7DE5"};return i.jsxs("div",{className:l,style:a,children:[r&&i.jsx("button",{type:"button",onClick:()=>o(null),style:s===null?f:c,children:h}),v.map(g=>i.jsx("button",{type:"button",onClick:()=>o(g.id),style:s===g.id?f:c,children:g.name},g.id))]})});Ie.displayName="FloorSelectorBar";const Le=t.memo(({scale:n,minScale:s,maxScale:o,onZoomIn:r,onZoomOut:h,position:d,className:l})=>{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"},c={...a,opacity:.4,cursor:"not-allowed"},f=n<o,g=n>s;return i.jsxs("div",{className:l,style:p,children:[i.jsx("button",{type:"button",onClick:r,disabled:!f,style:f?a:c,title:"Zoom In",children:"+"}),i.jsx("button",{type:"button",onClick:h,disabled:!g,style:g?a:c,title:"Zoom Out",children:"−"})]})});Le.displayName="ZoomControls";const De=t.memo(({visible:n,x:s,y:o,seat:r,currency:h,state:d})=>{if(!n||!r)return null;const l=r.seatNumber||(r.rowLabel&&r.columnLabel?`${r.rowLabel}-${r.columnLabel}`:"N/A"),v={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"},c={fontWeight:600},f={color:"#4ade80",fontWeight:600},g={fontSize:"11px",color:"#6b7280",textTransform:"capitalize",marginTop:"4px"};return i.jsx("div",{style:v,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:{...c,color:"#3b82f6"},children:r.sectionName})]}),i.jsxs("div",{style:{marginBottom:"4px"},children:[i.jsx("span",{style:a,children:"Seat:"}),i.jsx("span",{style:c,children:l})]}),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]})]})})});De.displayName="SeatTooltip";const ot=({config:n,configUrl:s,floorId:o,onFloorChange:r,reservedSeats:h=[],unavailableSeats:d=[],selectedSeats:l,myReservedSeats:v=[],onSeatSelect:p,onSeatDeselect:a,onSelectionChange:c,colorOverrides:f,showTooltip:g=!0,zoomEnabled:m=!0,className:w="",onConfigLoad:R,onError:E,showFloorSelector:F,floorSelectorPosition:j="top-left",floorSelectorClassName:I,showAllFloorsOption:L=!0,allFloorsLabel:T="All",fitToView:V=!0,fitPadding:W=40,showZoomControls:q=!0,zoomControlsPosition:fe="bottom-right",zoomControlsClassName:se,minZoom:re,maxZoom:z=3,zoomStep:A=.25,touchEnabled:ve=!0})=>{const X=t.useRef(null),O=t.useRef(null),M=je(O),[Y,he]=t.useState(new Set),[k,J]=t.useState(1),[D,U]=t.useState({x:0,y:0}),[Ne,Ae]=t.useState(null),[Xe,Ye]=t.useState(1),oe=t.useRef({width:0,height:0}),[K,me]=t.useState({visible:!1,x:0,y:0,seat:null,state:"available"}),{config:Pe,loading:We,error:Z}=Re(s),b=n||Pe,ge=o!==void 0,N=ge?o||null:Ne,ie=l!==void 0,Oe=t.useCallback(e=>{ge||Ae(e),r?.(e)},[ge,r]),pe=b?.floors||[],$e=F!==void 0?F:pe.length>1,ae=t.useMemo(()=>b?{...b.colors,...f}:{...Ee,...f},[b,f]),_=t.useMemo(()=>{if(!b)return[];let e=b.seats.filter(u=>u.state!=="hidden");return N&&(e=e.filter(u=>u.floorId===N||!u.floorId&&N==="floor_default")),e},[b,N]),ce=t.useMemo(()=>b?.stages?N?b.stages.filter(e=>e.floorId===N||!e.floorId&&N==="floor_default"):b.stages:[],[b,N]),$=t.useMemo(()=>{if(!b||_.length===0&&ce.length===0)return null;const e=12;let u=1/0,x=1/0,y=-1/0,S=-1/0;return _.forEach(C=>{u=Math.min(u,C.position.x-e),x=Math.min(x,C.position.y-e),y=Math.max(y,C.position.x+e),S=Math.max(S,C.position.y+e)}),ce.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,_,ce]);t.useEffect(()=>{if(!V||!b||!$||M.width===0||M.height===0)return;const e=Math.abs(M.width-oe.current.width),u=Math.abs(M.height-oe.current.height);if(!(oe.current.width===0)&&e<10&&u<10)return;oe.current=M;const y=M.width,S=M.height,C=y-W*2,te=S-W*2,le=C/$.width,xe=te/$.height,ue=Math.min(le,xe,z),Ze=$.minX+$.width/2,Qe=$.minY+$.height/2,et=y/2,tt=S/2,nt=et-Ze*ue,st=tt-Qe*ue;J(ue),U({x:nt,y:st}),Ye(ue)},[V,b,$,W,z,M,N]);const H=t.useMemo(()=>{const e=new Set(h),u=new Set(d),x=new Set(v);return{reserved:e,unavailable:u,myReserved:x}},[h,d,v]),be=t.useMemo(()=>l?new Set(l):null,[l]),Q=t.useCallback(e=>{const u=e.id,x=e.seatNumber||"";return H.unavailable.has(u)||H.unavailable.has(x)?"unavailable":H.reserved.has(u)||H.reserved.has(x)?"reserved":H.myReserved.has(u)||H.myReserved.has(x)||Y.has(u)?"selected":e.state},[H,Y]);t.useEffect(()=>{b&&R&&R(b)},[b,R]),t.useEffect(()=>{Z&&E&&E(Z)},[Z,E]),t.useEffect(()=>{ie&&be&&he(be)},[ie,be]);const Be=t.useCallback(e=>{const u=Q(e);if(u!=="available"&&u!=="selected")return;const x=Y.has(e.id);ie||he(y=>{const S=new Set(y);return x?S.delete(e.id):S.add(e.id),S}),x?a?.(e):(p?.(e),p||console.log("Seat selected:",e))},[Q,Y,ie,p,a]),ee=t.useMemo(()=>b?_.filter(e=>Y.has(e.id)):[],[_,Y]);t.useEffect(()=>{c?.(ee)},[ee,c]);const G=re!==void 0?re:Xe,Ve=t.useCallback(()=>{if(!m)return;const e=Math.min(k+A,z);if(e!==k){const u=M.width||b?.canvas.width||800,x=M.height||b?.canvas.height||600,y=u/2,S=x/2,C={x:(y-D.x)/k,y:(S-D.y)/k};J(e),U({x:y-C.x*e,y:S-C.y*e})}},[m,k,A,z,M,b,D]),Ue=t.useCallback(()=>{if(!m)return;const e=Math.max(k-A,G);if(e!==k){const u=M.width||b?.canvas.width||800,x=M.height||b?.canvas.height||600,y=u/2,S=x/2,C={x:(y-D.x)/k,y:(S-D.y)/k};J(e),U({x:y-C.x*e,y:S-C.y*e})}},[m,k,A,G,M,b,D]),He=t.useCallback(e=>{U({x:e.target.x(),y:e.target.y()})},[]),qe=t.useCallback(e=>{if(!m)return;e.evt.preventDefault();const u=X.current;if(!u)return;const x=u.scaleX(),y=u.getPointerPosition();if(!y)return;const S=1.1,C=e.evt.deltaY>0?x/S:x*S,te=Math.min(Math.max(C,G),z),le={x:(y.x-D.x)/x,y:(y.y-D.y)/x},xe={x:y.x-le.x*te,y:y.y-le.y*te};J(te),U(xe)},[m,D,G,z]);Me(X,{enabled:ve&&m,minScale:G,maxScale:z,currentScale:k,currentPosition:D,onScaleChange:(e,u)=>{J(e),U(u)},onPositionChange:e=>{U(e)}});const _e=t.useCallback((e,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:e,state:Q(e)})},[g,Q]),Ge=t.useCallback(()=>{me(e=>({...e,visible:!1}))},[]);if(We)return i.jsx("div",{className:`flex items-center justify-center h-full ${w}`,children:i.jsx("p",{children:"Loading seat map..."})});if(Z)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: ",Z.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 Je=M.width||b.canvas.width,Ke=M.height||b.canvas.height;return i.jsxs("div",{ref:O,className:`relative ${w}`,style:{width:"100%",height:"100%"},children:[$e&&pe.length>0&&i.jsx(Ie,{floors:pe,currentFloorId:N,onFloorChange:Oe,showAllOption:L,allLabel:T,position:j,className:I}),i.jsxs(P.Stage,{ref:X,width:Je,height:Ke,scaleX:k,scaleY:k,x:D.x,y:D.y,draggable:!0,onDragEnd:He,onWheel:qe,style:{backgroundColor:b.canvas.backgroundColor,cursor:"grab"},children:[i.jsx(P.Layer,{listening:!1,children:ce.map(e=>i.jsx(ke,{stage:e,stageColor:ae.stageColor},e.id))}),i.jsx(P.Layer,{children:_.map(e=>i.jsx(Fe,{seat:e,state:Q(e),colors:ae,onClick:Be,onMouseEnter:_e,onMouseLeave:Ge},e.id))})]}),g&&i.jsx(De,{visible:K.visible,x:K.x,y:K.y,seat:K.seat,currency:ae.currency,state:K.state}),q&&m&&i.jsx(Le,{scale:k,minScale:G,maxScale:z,onZoomIn:Ve,onZoomOut:Ue,position:fe,className:se}),ee.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 (",ee.length,")"]}),i.jsx("div",{className:"max-h-48 overflow-y-auto space-y-1",children:ee.map(e=>i.jsxs("div",{className:"text-sm",children:[e.seatNumber,e.price&&` - ${ae.currency} ${e.price.toFixed(2)}`]},e.id))})]})]})};let ne=null;function it(n){ne=n}function Se(){if(!ne)throw new Error("Firebase database not initialized. Call initializeFirebaseForViewer(db) first.");return ne}function de(){return ne!==null}function at(){ne=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 ct(n,s){return ye(n.myReserved,s.myReserved)&&ye(n.otherReserved,s.otherReserved)&&ye(n.unavailable,s.unavailable)}function lt(n,s){const o=[],r=[],h=[];return Object.entries(n).forEach(([d,l])=>{l&&typeof l=="object"&&l.state&&(l.state==="unavailable"?h.push(d):l.state==="reserved"&&(s&&l.userId===s?o.push(d):r.push(d)))}),{myReserved:o,otherReserved:r,unavailable:h}}function Te(n){const{seatMapId:s,currentUserId:o,enabled:r=!0,onStateChange:h,onError:d}=n,[l,v]=t.useState(null),[p,a]=t.useState(!0),[c,f]=t.useState(null),[g,m]=t.useState(null),[w,R]=t.useState([]),[E,F]=t.useState([]),[j,I]=t.useState([]),L=t.useRef(h),T=t.useRef(d),V=t.useRef(o);L.current=h,T.current=d,V.current=o;const W=t.useRef({myReserved:[],otherReserved:[],unavailable:[]}),q=t.useRef(null);return t.useEffect(()=>{if(!r||!s){a(!1);return}if(!de()){a(!1),f(new Error("Firebase not initialized. Call initializeFirebaseForViewer first."));return}const fe=Se(),se=B.ref(fe,`seat_states/${s}`);a(!0),f(null);const re=A=>{const X=A.val()||{},O=lt(X,V.current),M=W.current,Y=JSON.stringify(q.current)!==JSON.stringify(X);Y&&(v(X),q.current=X),a(!1),!ct(M,O)&&(m(Date.now()),R(O.myReserved),F(O.otherReserved),I(O.unavailable),W.current=O),Y&&L.current?.(X)},z=A=>{f(A),a(!1),T.current?.(A)};return B.onValue(se,re,z),()=>{B.off(se)}},[s,r]),{states:l,loading:p,error:c,lastUpdated:g,myReservedSeats:w,otherReservedSeats:E,unavailableSeats:j,reservedSeats:E}}function ze(n){const{seatMapId:s,enabled:o=!0,subscribeToChanges:r=!1,onConfigLoad:h,onError:d}=n,[l,v]=t.useState(null),[p,a]=t.useState(!0),[c,f]=t.useState(null),g=t.useRef(h),m=t.useRef(d);g.current=h,m.current=d;const w=t.useCallback(async()=>{if(!s)return;if(!de()){f(new Error("Firebase not initialized. Call initializeFirebaseForViewer first.")),a(!1);return}const R=Se(),E=B.ref(R,`seatmaps/${s}`);try{a(!0),f(null);const j=(await B.get(E)).val();if(j){const I=rt.fromFirebaseSeatMap(j);v(I),g.current?.(I)}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),m.current?.(j)}finally{a(!1)}},[s]);return t.useEffect(()=>{if(!o||!s){a(!1);return}if(w(),r&&de()){const R=Se(),E=B.ref(R,`seatmaps/${s}/meta/updated_at`);let F=!0,j=null;const I=L=>{if(F){F=!1,j=L.val();return}const T=L.val();L.exists()&&T!==j&&(j=T,w())};return B.onValue(E,I),()=>{B.off(E)}}},[s,o,r]),{config:l,loading:p,error:c,refetch:w}}function ut(n){const{seatMapId:s,userId:o,enabled:r=!0,subscribeToDesignChanges:h=!1,onConfigLoad:d,onStateChange:l,onError:v}=n,{config:p,loading:a,error:c,refetch:f}=ze({seatMapId:s,enabled:r,subscribeToChanges:h,onConfigLoad:d,onError:v}),{states:g,loading:m,error:w,lastUpdated:R,myReservedSeats:E,otherReservedSeats:F,unavailableSeats:j,reservedSeats:I}=Te({seatMapId:s,currentUserId:o,enabled:r,onStateChange:l,onError:v});return{config:p,loading:a||m,error:c||w,myReservedSeats:E,otherReservedSeats:F,unavailableSeats:j,reservedSeats:I,seatStates:g,lastUpdated:R,refetch:f}}exports.DEFAULT_COLORS=Ee;exports.SeatMapViewer=ot;exports.clearFirebaseInstance=at;exports.initializeFirebaseForViewer=it;exports.isFirebaseInitialized=de;exports.useConfigFetcher=Re;exports.useContainerSize=je;exports.useFirebaseConfig=ze;exports.useFirebaseSeatStates=Te;exports.useRealtimeSeatMap=ut;exports.useTouchGestures=Me;