tickera-angular-components 0.0.1-dev.31 → 0.0.1-dev.33

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.
@@ -480,6 +480,41 @@ var GiftBondStatus;
480
480
  GiftBondStatus["CONSUMED"] = "CONSUMED";
481
481
  })(GiftBondStatus || (GiftBondStatus = {}));
482
482
 
483
+ const TICKET_ELEMENT_TYPES = [
484
+ RoomMapElementType.SEAT_BLOCK,
485
+ RoomMapElementType.TABLE,
486
+ RoomMapElementType.ZONE,
487
+ ];
488
+ const DEFAULT_STAGE_CONFIG = {
489
+ width: 800,
490
+ height: 600,
491
+ draggable: true,
492
+ scaleX: 1,
493
+ scaleY: 1,
494
+ x: 0,
495
+ y: 0,
496
+ };
497
+ const TICKET_STATUS_COLORS = {
498
+ [PerformanceTicketStatus.AVAILABLE]: '#22C55E',
499
+ [PerformanceTicketStatus.RESERVED]: '#EAB308',
500
+ [PerformanceTicketStatus.SOLD]: '#EF4444',
501
+ [PerformanceTicketStatus.BLOCKED]: '#6B7280',
502
+ };
503
+ const TICKET_STATUS_FILLS = {
504
+ [PerformanceTicketStatus.AVAILABLE]: '#DCFCE7',
505
+ [PerformanceTicketStatus.RESERVED]: '#FEF08A',
506
+ [PerformanceTicketStatus.SOLD]: '#FECACA',
507
+ [PerformanceTicketStatus.BLOCKED]: '#E5E7EB',
508
+ };
509
+ const TICKET_STATUS_LABELS = {
510
+ [PerformanceTicketStatus.AVAILABLE]: 'Disponible',
511
+ [PerformanceTicketStatus.RESERVED]: 'Reservado',
512
+ [PerformanceTicketStatus.SOLD]: 'Vendido',
513
+ [PerformanceTicketStatus.BLOCKED]: 'Bloqueado',
514
+ };
515
+ const TICKET_MAP_GRID_SIZE = 30;
516
+ const TICKET_MAP_TITLE_HEIGHT = 30;
517
+
483
518
  var OrderItemType;
484
519
  (function (OrderItemType) {
485
520
  OrderItemType["TICKET"] = "TICKET";
@@ -497,6 +532,13 @@ var OrderStatus;
497
532
  OrderStatus["REFUNDED"] = "refunded";
498
533
  })(OrderStatus || (OrderStatus = {}));
499
534
 
535
+ var SelectedDiscountCardType;
536
+ (function (SelectedDiscountCardType) {
537
+ SelectedDiscountCardType["COUPON"] = "COUPON";
538
+ SelectedDiscountCardType["CORPORATE_BOND"] = "CORPORATE_BOND";
539
+ SelectedDiscountCardType["GIFT_BOND"] = "GIFT_BOND";
540
+ })(SelectedDiscountCardType || (SelectedDiscountCardType = {}));
541
+
500
542
  const getCustomerFullname = (customer) => {
501
543
  return `${customer.first_name || ''} ${customer.last_name || ''}`.trim();
502
544
  };
@@ -577,6 +619,1145 @@ const formatCitiesResponseToSelect = (res) => {
577
619
  }));
578
620
  };
579
621
 
622
+ function drawRoundedRect(con, shape, x, y, width, height, cornerRadius) {
623
+ con.beginPath();
624
+ con.moveTo(x + cornerRadius, y);
625
+ con.lineTo(x + width - cornerRadius, y);
626
+ con.quadraticCurveTo(x + width, y, x + width, y + cornerRadius);
627
+ con.lineTo(x + width, y + height - cornerRadius);
628
+ con.quadraticCurveTo(x + width, y + height, x + width - cornerRadius, y + height);
629
+ con.lineTo(x + cornerRadius, y + height);
630
+ con.quadraticCurveTo(x, y + height, x, y + height - cornerRadius);
631
+ con.lineTo(x, y + cornerRadius);
632
+ con.quadraticCurveTo(x, y, x + cornerRadius, y);
633
+ con.closePath();
634
+ con.fillStrokeShape(shape);
635
+ }
636
+
637
+ const generateExitScene = (con, shape) => {
638
+ const w = shape.width();
639
+ const h = shape.height();
640
+ const padding = 4;
641
+ con.save();
642
+ const exitX = padding;
643
+ const exitY = padding;
644
+ const exitWidth = w - padding * 2;
645
+ const exitHeight = h - padding * 2;
646
+ const cornerRadius = Math.min(exitWidth, exitHeight) * 0.2;
647
+ drawRoundedRect(con, shape, exitX, exitY, exitWidth, exitHeight, cornerRadius);
648
+ // Stage text - check if parent has element_name, otherwise use "ESCENARIO"
649
+ // const fontSize = Math.min(w, h) * 0.35;
650
+ con.font = `11px Arial`;
651
+ con.fillStyle = '#333333';
652
+ con.textAlign = 'center';
653
+ con.textBaseline = 'middle';
654
+ const centerX = w / 2;
655
+ const centerY = h / 2;
656
+ // Get parent element and check for element_name
657
+ const stageText = '🚪' + (shape?.attrs?.element_name || 'Salida');
658
+ con.fillText(stageText, centerX, centerY);
659
+ con.restore();
660
+ };
661
+
662
+ const generateHallwayScene = (con, shape) => {
663
+ const w = shape.width();
664
+ const h = shape.height();
665
+ const padding = 4;
666
+ con.save();
667
+ const exitX = padding;
668
+ const exitY = padding;
669
+ const exitWidth = w - padding * 2;
670
+ const exitHeight = h - padding * 2;
671
+ const cornerRadius = Math.min(exitWidth, exitHeight) * 0.2;
672
+ drawRoundedRect(con, shape, exitX, exitY, exitWidth, exitHeight, cornerRadius);
673
+ // Stage text - check if parent has element_name, otherwise use "ESCENARIO"
674
+ // const fontSize = Math.min(w, h) * 0.35;
675
+ con.font = `11px Arial`;
676
+ con.fillStyle = '#333333';
677
+ con.textAlign = 'center';
678
+ con.textBaseline = 'middle';
679
+ const centerX = w / 2;
680
+ const centerY = h / 2;
681
+ // Get parent element and check for element_name
682
+ const stageText = '🚶🏽' + (shape?.attrs?.element_name || 'Pasillo');
683
+ con.fillText(stageText, centerX, centerY);
684
+ con.restore();
685
+ };
686
+
687
+ const generateProductionAreaScene = (con, shape) => {
688
+ const w = shape.width();
689
+ const h = shape.height();
690
+ const padding = 4;
691
+ con.save();
692
+ const exitX = padding;
693
+ const exitY = padding;
694
+ const exitWidth = w - padding * 2;
695
+ const exitHeight = h - padding * 2;
696
+ const cornerRadius = Math.min(exitWidth, exitHeight) * 0.2;
697
+ drawRoundedRect(con, shape, exitX, exitY, exitWidth, exitHeight, cornerRadius);
698
+ // Stage text - check if parent has element_name, otherwise use "ESCENARIO"
699
+ // const fontSize = Math.min(w, h) * 0.35;
700
+ con.font = `11px Arial`;
701
+ con.fillStyle = '#333333';
702
+ con.textAlign = 'center';
703
+ con.textBaseline = 'middle';
704
+ const centerX = w / 2;
705
+ const centerY = h / 2;
706
+ // Get parent element and check for element_name
707
+ const stageText = '🛠️' + (shape?.attrs?.element_name || 'Área de producción');
708
+ con.fillText(stageText, centerX, centerY);
709
+ con.restore();
710
+ };
711
+
712
+ function tintColor(hex, amount = 0.7) {
713
+ const cleanHex = hex.replace('#', '');
714
+ const r = parseInt(cleanHex.substring(0, 2), 16);
715
+ const g = parseInt(cleanHex.substring(2, 4), 16);
716
+ const b = parseInt(cleanHex.substring(4, 6), 16);
717
+ const tintedR = Math.round(r + (255 - r) * amount);
718
+ const tintedG = Math.round(g + (255 - g) * amount);
719
+ const tintedB = Math.round(b + (255 - b) * amount);
720
+ return `#${tintedR.toString(16).padStart(2, '0')}${tintedG.toString(16).padStart(2, '0')}${tintedB.toString(16).padStart(2, '0')}`;
721
+ }
722
+
723
+ const drawChairIcon$1 = (con, cx, cy, size) => {
724
+ con.beginPath();
725
+ const backWidth = size * 0.7;
726
+ const backHeight = size * 0.6;
727
+ const backX = cx - backWidth / 2;
728
+ const backY = cy - backHeight / 2 - size * 0.1;
729
+ con.moveTo(backX + backWidth * 0.2, backY);
730
+ con.lineTo(backX + backWidth * 0.8, backY);
731
+ con.quadraticCurveTo(backX + backWidth, backY, backX + backWidth, backY + backHeight * 0.2);
732
+ con.lineTo(backX + backWidth, backY + backHeight * 0.8);
733
+ con.quadraticCurveTo(backX + backWidth, backY + backHeight, backX + backWidth * 0.8, backY + backHeight);
734
+ con.lineTo(backX + backWidth * 0.2, backY + backHeight);
735
+ con.quadraticCurveTo(backX, backY + backHeight, backX, backY + backHeight * 0.8);
736
+ con.lineTo(backX, backY + backHeight * 0.2);
737
+ con.quadraticCurveTo(backX, backY, backX + backWidth * 0.2, backY);
738
+ const chairSeatWidth = size * 0.8;
739
+ const chairSeatHeight = size * 0.3;
740
+ const seatX = cx - chairSeatWidth / 2;
741
+ const seatY = cy + backHeight * 0.3;
742
+ con.moveTo(seatX, seatY);
743
+ con.lineTo(seatX + chairSeatWidth, seatY);
744
+ con.lineTo(seatX + chairSeatWidth * 0.9, seatY + chairSeatHeight);
745
+ con.lineTo(seatX + chairSeatWidth * 0.1, seatY + chairSeatHeight);
746
+ con.closePath();
747
+ const legWidth = size * 0.1;
748
+ const legHeight = size * 0.2;
749
+ con.moveTo(seatX + chairSeatWidth * 0.1, seatY + chairSeatHeight);
750
+ con.lineTo(seatX + chairSeatWidth * 0.1 + legWidth, seatY + chairSeatHeight);
751
+ con.lineTo(seatX + chairSeatWidth * 0.1 + legWidth, seatY + chairSeatHeight + legHeight);
752
+ con.lineTo(seatX + chairSeatWidth * 0.1, seatY + chairSeatHeight + legHeight);
753
+ con.moveTo(seatX + chairSeatWidth * 0.9 - legWidth, seatY + chairSeatHeight);
754
+ con.lineTo(seatX + chairSeatWidth * 0.9, seatY + chairSeatHeight);
755
+ con.lineTo(seatX + chairSeatWidth * 0.9, seatY + chairSeatHeight + legHeight);
756
+ con.lineTo(seatX + chairSeatWidth * 0.9 - legWidth, seatY + chairSeatHeight + legHeight);
757
+ con.closePath();
758
+ };
759
+
760
+ const drawHappyFace = (con, cx, cy, size) => {
761
+ const radius = size * 0.45;
762
+ con.beginPath();
763
+ con.arc(cx, cy, radius, 0, Math.PI * 2);
764
+ con.closePath();
765
+ con.fillStyle = '#DCFCE7';
766
+ con.fill();
767
+ con.strokeStyle = '#166534';
768
+ con.lineWidth = 2;
769
+ con.stroke();
770
+ const eyeR = radius * 0.12;
771
+ const eyeOffX = radius * 0.3;
772
+ const eyeOffY = radius * 0.15;
773
+ con.beginPath();
774
+ con.arc(cx - eyeOffX, cy - eyeOffY, eyeR, 0, Math.PI * 2);
775
+ con.closePath();
776
+ con.fillStyle = '#166534';
777
+ con.fill();
778
+ con.beginPath();
779
+ con.arc(cx + eyeOffX, cy - eyeOffY, eyeR, 0, Math.PI * 2);
780
+ con.closePath();
781
+ con.fill();
782
+ con.beginPath();
783
+ con.moveTo(cx - radius * 0.35, cy + radius * 0.05);
784
+ con.quadraticCurveTo(cx, cy + radius * 0.4, cx + radius * 0.35, cy + radius * 0.05);
785
+ con.strokeStyle = '#166534';
786
+ con.lineWidth = 2;
787
+ con.stroke();
788
+ };
789
+
790
+ const drawWheelchairIcon = (con, shape, cx, cy, size, fillColor = '#DBEAFE', strokeColor = '#60A5FA') => {
791
+ const reactSize = size * 0.75;
792
+ const x = cx - size / 2 + size * 0.1;
793
+ const y = cy - size / 2 + size * 0.1833;
794
+ const origFill = shape.getAttr('fill');
795
+ const origStroke = shape.getAttr('stroke');
796
+ shape.setAttr('fill', fillColor);
797
+ shape.setAttr('stroke', strokeColor);
798
+ con.beginPath();
799
+ con.rect(x, y, reactSize, reactSize);
800
+ con.closePath();
801
+ con.fillStrokeShape(shape);
802
+ shape.setAttr('fill', origFill);
803
+ shape.setAttr('stroke', origStroke);
804
+ con.font = `${size * 0.6}px serif`;
805
+ con.textAlign = 'center';
806
+ con.textBaseline = 'middle';
807
+ con.fillText('♿', cx + size * 0.0233, cy + size * 0.1417);
808
+ };
809
+
810
+ const numberToLetter$1 = (num) => {
811
+ if (num < 0)
812
+ return 'A';
813
+ let result = '';
814
+ let n = num;
815
+ while (n >= 0) {
816
+ result = String.fromCharCode(65 + (n % 26)) + result;
817
+ n = Math.floor(n / 26) - 1;
818
+ }
819
+ return result;
820
+ };
821
+
822
+ const getItemTypeIcon = (ticket) => {
823
+ if ([RoomMapElementType.SEAT_BLOCK, RoomMapElementType.TABLE].includes(ticket.element_type)) {
824
+ return ticket.is_accessible ? '♿️' : '🪑';
825
+ }
826
+ if (ticket.element_type === RoomMapElementType.ZONE) {
827
+ return '🗺️';
828
+ }
829
+ return '🛒';
830
+ };
831
+
832
+ const generateSeatBlockTicketScene = (con, shape, numerationConfig, ticketStatusMap) => {
833
+ const w = shape.width();
834
+ const h = shape.height();
835
+ const seatSize = TICKET_MAP_GRID_SIZE;
836
+ const chairScale = 0.8;
837
+ const elementName = shape?.attrs?.element_name || 'Asientos';
838
+ const showElementName = shape?.attrs?.show_element_name ?? true;
839
+ const seatsData = shape?.attrs?.seats;
840
+ const hasTitle = !!(elementName && showElementName);
841
+ const chairsStartY = hasTitle ? TICKET_MAP_GRID_SIZE : 0;
842
+ const cols = Math.floor(w / seatSize);
843
+ const rows = Math.floor((h - chairsStartY) / seatSize);
844
+ const config = numerationConfig || {
845
+ start_row_letter: 'A',
846
+ row_order: 'TOP_TO_BOTTOM',
847
+ start_column_number: 1,
848
+ column_order: 'LEFT_TO_RIGHT',
849
+ };
850
+ const startRowLetter = config?.start_row_letter?.toUpperCase() || 'A';
851
+ const startRowNum = startRowLetter.charCodeAt(0) - 65;
852
+ const origFill = shape.getAttr('fill');
853
+ const origStroke = shape.getAttr('stroke');
854
+ if (hasTitle) {
855
+ con.save();
856
+ const textFont = `bold 10px Arial`;
857
+ con.font = textFont;
858
+ const textWidth = con.measureText(elementName).width;
859
+ const paddingX = 10;
860
+ const containerHeight = 18;
861
+ const containerWidth = textWidth + paddingX * 2;
862
+ const containerX = (w - containerWidth) / 2;
863
+ const containerY = (TICKET_MAP_GRID_SIZE - containerHeight) / 2;
864
+ const lineY = TICKET_MAP_GRID_SIZE;
865
+ const strokeColor = shape.getAttr('stroke') || '#999';
866
+ con.beginPath();
867
+ con.moveTo(0, lineY);
868
+ con.lineTo(w, lineY);
869
+ con.strokeStyle = strokeColor;
870
+ con.lineWidth = 1;
871
+ con.stroke();
872
+ con.beginPath();
873
+ con.moveTo(containerX + 4, containerY);
874
+ con.lineTo(containerX + containerWidth - 4, containerY);
875
+ con.quadraticCurveTo(containerX + containerWidth, containerY, containerX + containerWidth, containerY + 4);
876
+ con.lineTo(containerX + containerWidth, containerY + containerHeight - 4);
877
+ con.quadraticCurveTo(containerX + containerWidth, containerY + containerHeight, containerX + containerWidth - 4, containerY + containerHeight);
878
+ con.lineTo(containerX + 4, containerY + containerHeight);
879
+ con.quadraticCurveTo(containerX, containerY + containerHeight, containerX, containerY + containerHeight - 4);
880
+ con.lineTo(containerX, containerY + 4);
881
+ con.quadraticCurveTo(containerX, containerY, containerX + 4, containerY);
882
+ con.closePath();
883
+ con.fillStrokeShape(shape);
884
+ con.font = textFont;
885
+ con.fillStyle = strokeColor;
886
+ con.textAlign = 'center';
887
+ con.textBaseline = 'middle';
888
+ con.fillText(elementName, w / 2, containerY + containerHeight / 2);
889
+ con.restore();
890
+ }
891
+ for (let x = 0; x < w; x += seatSize) {
892
+ for (let y = chairsStartY; y < h; y += seatSize) {
893
+ const colIndex = Math.floor(x / seatSize);
894
+ const rowIndex = Math.floor((y - chairsStartY) / seatSize);
895
+ let actualRowIndex = rowIndex;
896
+ if (config.row_order === 'BOTTOM_TO_TOP') {
897
+ actualRowIndex = rows - 1 - rowIndex;
898
+ }
899
+ let actualColIndex = colIndex;
900
+ if (config.column_order === 'RIGHT_TO_LEFT') {
901
+ actualColIndex = cols - 1 - colIndex;
902
+ }
903
+ const rowLetter = numberToLetter$1(startRowNum + actualRowIndex);
904
+ const colNumber = Number(config.start_column_number) + Number(actualColIndex);
905
+ const seatNumber = `${rowLetter}${colNumber}`;
906
+ const centerX = x + seatSize / 2 + 1;
907
+ const centerY = y + seatSize / 2 - 2;
908
+ const size = seatSize * chairScale;
909
+ const seatState = seatsData?.[rowIndex]?.[colIndex];
910
+ if (seatState?.is_non_existent)
911
+ continue;
912
+ con.save();
913
+ const selectedNumerations = shape.getAttr('selectedNumerations');
914
+ const isSelected = selectedNumerations?.has(seatNumber);
915
+ const ticketStatus = ticketStatusMap?.get(seatNumber);
916
+ const isLocked = ticketStatus != null && ticketStatus !== PerformanceTicketStatus.AVAILABLE;
917
+ if (isSelected) {
918
+ drawHappyFace(con, x + seatSize / 2, y + seatSize / 2, size);
919
+ }
920
+ else if (isLocked && seatState?.is_accessible) {
921
+ drawWheelchairIcon(con, shape, centerX, centerY, size, TICKET_STATUS_FILLS[ticketStatus], TICKET_STATUS_COLORS[ticketStatus]);
922
+ }
923
+ else if (isLocked) {
924
+ shape.setAttr('fill', TICKET_STATUS_FILLS[ticketStatus]);
925
+ shape.setAttr('stroke', TICKET_STATUS_COLORS[ticketStatus]);
926
+ drawChairIcon$1(con, centerX, centerY, size);
927
+ con.fillStrokeShape(shape);
928
+ shape.setAttr('fill', origFill);
929
+ shape.setAttr('stroke', origStroke);
930
+ con.fillStyle = '#333333';
931
+ con.font = `${size * 0.3}px Arial`;
932
+ con.textAlign = 'center';
933
+ con.textBaseline = 'middle';
934
+ con.fillText(seatNumber, centerX, centerY - 1);
935
+ }
936
+ else if (seatState?.is_accessible) {
937
+ drawWheelchairIcon(con, shape, centerX, centerY, size);
938
+ }
939
+ else {
940
+ drawChairIcon$1(con, centerX, centerY, size);
941
+ con.fillStrokeShape(shape);
942
+ shape.setAttr('fill', origFill);
943
+ shape.setAttr('stroke', origStroke);
944
+ con.fillStyle = '#333333';
945
+ con.font = `${size * 0.3}px Arial`;
946
+ con.textAlign = 'center';
947
+ con.textBaseline = 'middle';
948
+ con.fillText(seatNumber, centerX, centerY - 1);
949
+ }
950
+ con.restore();
951
+ }
952
+ }
953
+ shape.setAttr('fill', origFill);
954
+ shape.setAttr('stroke', origStroke);
955
+ };
956
+
957
+ const generateTableTicketScene = (con, shape, chairTicketStatuses, tableSeats) => {
958
+ const w = shape.width();
959
+ const h = shape.height();
960
+ const fillColor = shape.fill();
961
+ const strokeColor = shape.stroke();
962
+ const totalCapacity = shape?.attrs?.total_capacity ?? 0;
963
+ const zoneText = shape?.attrs?.element_name || 'Mesa';
964
+ const cx = w / 2;
965
+ const cy = h / 2;
966
+ const chairRadius = Math.min(w, h) * 0.1;
967
+ const gap = chairRadius * 0.4;
968
+ const tableRadius = Math.min(w, h) / 2 - chairRadius * 2 - gap;
969
+ con.save();
970
+ if (totalCapacity > 0) {
971
+ const orbitRadius = tableRadius + gap + chairRadius;
972
+ const selectedNumerations = shape.getAttr('selectedNumerations');
973
+ for (let i = 0; i < totalCapacity; i++) {
974
+ const seatState = tableSeats?.[0]?.[i];
975
+ if (seatState?.is_non_existent) {
976
+ continue;
977
+ }
978
+ const seatKey = `S${i + 1}`;
979
+ const isSelected = selectedNumerations?.has(seatKey);
980
+ const angle = (2 * Math.PI * i) / totalCapacity - Math.PI / 2;
981
+ const scx = cx + orbitRadius * Math.cos(angle);
982
+ const scy = cy + orbitRadius * Math.sin(angle);
983
+ const chairStatus = chairTicketStatuses?.[i];
984
+ const isLocked = chairStatus != null && chairStatus !== PerformanceTicketStatus.AVAILABLE;
985
+ if (isSelected) {
986
+ drawHappyFace(con, scx, scy, chairRadius * 2);
987
+ }
988
+ else if (isLocked && seatState?.is_accessible) {
989
+ drawWheelchairIcon(con, shape, scx, scy, chairRadius * 2, TICKET_STATUS_FILLS[chairStatus], TICKET_STATUS_COLORS[chairStatus]);
990
+ }
991
+ else if (isLocked) {
992
+ const origFill = shape.getAttr('fill');
993
+ const origStroke = shape.getAttr('stroke');
994
+ shape.setAttr('fill', TICKET_STATUS_FILLS[PerformanceTicketStatus.BLOCKED]);
995
+ shape.setAttr('stroke', TICKET_STATUS_COLORS[PerformanceTicketStatus.BLOCKED]);
996
+ con.beginPath();
997
+ con.arc(scx, scy, chairRadius, 0, Math.PI * 2);
998
+ con.closePath();
999
+ con.lineWidth = Math.max(1, chairRadius * 0.15);
1000
+ con.stroke();
1001
+ con.fillStrokeShape(shape);
1002
+ shape.setAttr('fill', origFill);
1003
+ shape.setAttr('stroke', origStroke);
1004
+ }
1005
+ else if (seatState?.is_accessible) {
1006
+ drawWheelchairIcon(con, shape, scx, scy, chairRadius * 2);
1007
+ }
1008
+ else {
1009
+ con.beginPath();
1010
+ con.arc(scx, scy, chairRadius, 0, Math.PI * 2);
1011
+ con.closePath();
1012
+ con.lineWidth = Math.max(1, chairRadius * 0.15);
1013
+ con.stroke();
1014
+ con.fillStrokeShape(shape);
1015
+ }
1016
+ }
1017
+ }
1018
+ con.beginPath();
1019
+ con.arc(cx, cy, tableRadius, 0, Math.PI * 2);
1020
+ con.closePath();
1021
+ con.fillStrokeShape(shape);
1022
+ const fontSize = tableRadius * 0.45;
1023
+ con.font = `${fontSize}px Arial`;
1024
+ con.fillStyle = strokeColor;
1025
+ con.textAlign = 'center';
1026
+ con.textBaseline = 'middle';
1027
+ con.fillText(zoneText, cx, cy);
1028
+ con.restore();
1029
+ };
1030
+
1031
+ const KONVA_SHAPE_MAPPINGS = {
1032
+ [RoomMapElementType.STAGE]: {
1033
+ config: {
1034
+ width: TICKET_MAP_GRID_SIZE * 10,
1035
+ height: TICKET_MAP_GRID_SIZE * 2,
1036
+ fill: '#9C27B0',
1037
+ stroke: '#731d82',
1038
+ strokeWidth: 1,
1039
+ draggable: true,
1040
+ perfectDrawEnabled: false,
1041
+ sceneFunc: generateStageScene,
1042
+ },
1043
+ layer: 'background',
1044
+ },
1045
+ [RoomMapElementType.SEAT_BLOCK]: {
1046
+ config: {
1047
+ width: TICKET_MAP_GRID_SIZE * 5,
1048
+ height: TICKET_MAP_GRID_SIZE,
1049
+ fill: '#ffffff',
1050
+ stroke: '#909090',
1051
+ strokeWidth: 1,
1052
+ draggable: true,
1053
+ perfectDrawEnabled: false,
1054
+ sceneFunc: generateSeatBlockScene,
1055
+ },
1056
+ layer: 'elements',
1057
+ },
1058
+ [RoomMapElementType.TABLE]: {
1059
+ config: {
1060
+ width: TICKET_MAP_GRID_SIZE * 3,
1061
+ height: TICKET_MAP_GRID_SIZE * 2,
1062
+ fill: '#ffffff',
1063
+ stroke: '#909090',
1064
+ strokeWidth: 2,
1065
+ cornerRadius: 5,
1066
+ draggable: true,
1067
+ perfectDrawEnabled: false,
1068
+ sceneFunc: generateTableScene,
1069
+ },
1070
+ layer: 'elements',
1071
+ },
1072
+ [RoomMapElementType.ZONE]: {
1073
+ config: {
1074
+ width: TICKET_MAP_GRID_SIZE * 10,
1075
+ height: TICKET_MAP_GRID_SIZE * 3,
1076
+ fill: '#ffffff',
1077
+ stroke: '#909090',
1078
+ strokeWidth: 1,
1079
+ cornerRadius: 2,
1080
+ draggable: true,
1081
+ opacity: 1,
1082
+ perfectDrawEnabled: false,
1083
+ sceneFunc: generateZoneScene,
1084
+ },
1085
+ layer: 'background',
1086
+ },
1087
+ [RoomMapElementType.EXIT]: {
1088
+ config: {
1089
+ width: TICKET_MAP_GRID_SIZE * 3,
1090
+ height: TICKET_MAP_GRID_SIZE,
1091
+ fill: '#efefef',
1092
+ stroke: '#aaaaaa',
1093
+ strokeWidth: 1,
1094
+ draggable: true,
1095
+ perfectDrawEnabled: false,
1096
+ sceneFunc: generateExitScene,
1097
+ },
1098
+ layer: 'foreground',
1099
+ },
1100
+ [RoomMapElementType.PRODUCTION_AREA]: {
1101
+ config: {
1102
+ width: TICKET_MAP_GRID_SIZE * 4,
1103
+ height: TICKET_MAP_GRID_SIZE,
1104
+ fill: '#efefef',
1105
+ stroke: '#aaaaaa',
1106
+ strokeWidth: 1,
1107
+ draggable: true,
1108
+ perfectDrawEnabled: false,
1109
+ sceneFunc: generateProductionAreaScene,
1110
+ },
1111
+ layer: 'background',
1112
+ },
1113
+ [RoomMapElementType.UNAVAILABLE_SPACE]: {
1114
+ config: {
1115
+ width: TICKET_MAP_GRID_SIZE * 4,
1116
+ height: TICKET_MAP_GRID_SIZE,
1117
+ fill: '#efefef',
1118
+ stroke: '#aaaaaa',
1119
+ strokeWidth: 1,
1120
+ draggable: true,
1121
+ perfectDrawEnabled: false,
1122
+ sceneFunc: generateUnavailableSpaceScene,
1123
+ },
1124
+ layer: 'background',
1125
+ },
1126
+ [RoomMapElementType.STAIR]: {
1127
+ config: {
1128
+ width: TICKET_MAP_GRID_SIZE * 3,
1129
+ height: TICKET_MAP_GRID_SIZE,
1130
+ fill: '#efefef',
1131
+ stroke: '#aaaaaa',
1132
+ strokeWidth: 1,
1133
+ draggable: true,
1134
+ perfectDrawEnabled: false,
1135
+ sceneFunc: generateStairScene,
1136
+ },
1137
+ layer: 'background',
1138
+ },
1139
+ [RoomMapElementType.HALLWAY]: {
1140
+ config: {
1141
+ width: TICKET_MAP_GRID_SIZE * 3,
1142
+ height: TICKET_MAP_GRID_SIZE,
1143
+ fill: '#efefef',
1144
+ stroke: '#aaaaaa',
1145
+ strokeWidth: 1,
1146
+ draggable: true,
1147
+ perfectDrawEnabled: false,
1148
+ sceneFunc: generateHallwayScene,
1149
+ },
1150
+ layer: 'background',
1151
+ },
1152
+ };
1153
+
1154
+ function buildSeatLabel(rowIndex, colIndex, rows, cols, config) {
1155
+ let actualRowIndex = rowIndex;
1156
+ if (config.row_order === 'BOTTOM_TO_TOP') {
1157
+ actualRowIndex = rows - 1 - rowIndex;
1158
+ }
1159
+ let actualColIndex = colIndex;
1160
+ if (config.column_order === 'RIGHT_TO_LEFT') {
1161
+ actualColIndex = cols - 1 - colIndex;
1162
+ }
1163
+ const startRowNum = (config.start_row_letter?.toUpperCase() || 'A').charCodeAt(0) - 65;
1164
+ const rowLetter = numberToLetter$1(startRowNum + actualRowIndex);
1165
+ const colNumber = Number(config.start_column_number) + Number(actualColIndex);
1166
+ return `${rowLetter}${colNumber}`;
1167
+ }
1168
+ function buildSeatPositions(element) {
1169
+ const config = element.numeration_config || {
1170
+ start_row_letter: 'A',
1171
+ row_order: 'TOP_TO_BOTTOM',
1172
+ start_column_number: 1,
1173
+ column_order: 'LEFT_TO_RIGHT',
1174
+ };
1175
+ const w = element.konva_shape?.config?.width || 300;
1176
+ const h = element.konva_shape?.config?.height || 300;
1177
+ const hasTitle = !!(element.element_name && element.show_element_name !== false);
1178
+ const chairsStartY = hasTitle ? TICKET_MAP_TITLE_HEIGHT : 0;
1179
+ const cols = Math.floor(w / TICKET_MAP_GRID_SIZE);
1180
+ const rows = Math.floor((h - chairsStartY) / TICKET_MAP_GRID_SIZE);
1181
+ const positions = [];
1182
+ for (let rowIndex = 0; rowIndex < rows; rowIndex++) {
1183
+ const row = [];
1184
+ for (let colIndex = 0; colIndex < cols; colIndex++) {
1185
+ const seatLabel = buildSeatLabel(rowIndex, colIndex, rows, cols, config);
1186
+ const seatState = element.seats?.[rowIndex]?.[colIndex];
1187
+ if (seatState?.is_non_existent) {
1188
+ row.push({ rowIndex, colIndex, seatLabel });
1189
+ continue;
1190
+ }
1191
+ row.push({ rowIndex, colIndex, seatLabel });
1192
+ }
1193
+ positions.push(row);
1194
+ }
1195
+ return positions;
1196
+ }
1197
+ function buildChairPositions(element) {
1198
+ const w = element.konva_shape?.config?.width || 150;
1199
+ const h = element.konva_shape?.config?.height || 150;
1200
+ const totalCapacity = element.total_capacity || 0;
1201
+ const chairRadius = Math.min(w, h) * 0.1;
1202
+ const gap = chairRadius * 0.4;
1203
+ const tableRadius = Math.min(w, h) / 2 - chairRadius * 2 - gap;
1204
+ const cx = w / 2;
1205
+ const cy = h / 2;
1206
+ const positions = [];
1207
+ if (totalCapacity > 0) {
1208
+ const orbitRadius = tableRadius + gap + chairRadius;
1209
+ for (let i = 0; i < totalCapacity; i++) {
1210
+ const angle = (2 * Math.PI * i) / totalCapacity - Math.PI / 2;
1211
+ const scx = cx + orbitRadius * Math.cos(angle);
1212
+ const scy = cy + orbitRadius * Math.sin(angle);
1213
+ positions.push({ index: i, angle, x: scx, y: scy });
1214
+ }
1215
+ }
1216
+ return positions;
1217
+ }
1218
+ function processElementsToRenderData(elements, tickets, priceZones) {
1219
+ const ticketByElement = new Map();
1220
+ for (const ticket of tickets) {
1221
+ const existing = ticketByElement.get(ticket.room_map_element_id) || [];
1222
+ existing.push(ticket);
1223
+ ticketByElement.set(ticket.room_map_element_id, existing);
1224
+ }
1225
+ const result = [];
1226
+ for (const element of elements) {
1227
+ if (element.id == null)
1228
+ continue;
1229
+ const elementTickets = ticketByElement.get(element.id) || [];
1230
+ const elementBaseConfig = KONVA_SHAPE_MAPPINGS[element.type];
1231
+ let overrideFill;
1232
+ let overrideStroke;
1233
+ if (element.price_zone_id != null && priceZones) {
1234
+ const priceZone = priceZones.find((pz) => pz.id === element.price_zone_id);
1235
+ if (priceZone?.color) {
1236
+ overrideStroke = priceZone.color;
1237
+ overrideFill = tintColor(priceZone.color);
1238
+ }
1239
+ }
1240
+ let shapeConfig = {
1241
+ ...element?.konva_shape?.config,
1242
+ draggable: false,
1243
+ ...(overrideFill ? { fill: overrideFill } : {}),
1244
+ ...(overrideStroke ? { stroke: overrideStroke } : {}),
1245
+ element_name: element.element_name || element.name,
1246
+ show_element_name: element.show_element_name ?? true,
1247
+ total_capacity: element.total_capacity,
1248
+ };
1249
+ const ticketByNumeration = new Map();
1250
+ for (const ticket of elementTickets) {
1251
+ if (ticket.seat_numeration != null) {
1252
+ ticketByNumeration.set(ticket.seat_numeration, ticket);
1253
+ }
1254
+ }
1255
+ let seatPositions;
1256
+ let chairPositions;
1257
+ if (element.type === RoomMapElementType.SEAT_BLOCK) {
1258
+ seatPositions = buildSeatPositions(element);
1259
+ const ticketStatusMap = new Map();
1260
+ for (const [numeration, ticket] of ticketByNumeration) {
1261
+ ticketStatusMap.set(numeration, ticket.status);
1262
+ }
1263
+ shapeConfig = {
1264
+ ...shapeConfig,
1265
+ name: String(element.id),
1266
+ sceneFunc: (con, shape) => {
1267
+ generateSeatBlockTicketScene(con, shape, element.numeration_config, ticketStatusMap);
1268
+ },
1269
+ seats: element.seats,
1270
+ };
1271
+ }
1272
+ if (element.type === RoomMapElementType.TABLE) {
1273
+ chairPositions = buildChairPositions(element);
1274
+ const chairStatuses = [];
1275
+ for (let i = 0; i < (element.total_capacity || 0); i++) {
1276
+ const key = `S${i + 1}`;
1277
+ const ticket = ticketByNumeration.get(key);
1278
+ chairStatuses.push(ticket?.status ?? PerformanceTicketStatus.AVAILABLE);
1279
+ }
1280
+ shapeConfig = {
1281
+ ...shapeConfig,
1282
+ name: String(element.id),
1283
+ sceneFunc: (con, shape) => {
1284
+ generateTableTicketScene(con, shape, chairStatuses, element.seats);
1285
+ },
1286
+ seats: element.seats,
1287
+ };
1288
+ }
1289
+ if (![RoomMapElementType.SEAT_BLOCK, RoomMapElementType.TABLE].includes(element.type)) {
1290
+ const defaultSceneFunc = elementBaseConfig?.config?.sceneFunc;
1291
+ if (defaultSceneFunc && !shapeConfig['sceneFunc']) {
1292
+ shapeConfig['sceneFunc'] = defaultSceneFunc;
1293
+ }
1294
+ }
1295
+ let elementName = element.name ?? '';
1296
+ if (!!element.element_name) {
1297
+ elementName += `: ${element.element_name}`;
1298
+ }
1299
+ result.push({
1300
+ elementId: element.id,
1301
+ hallFloorId: element.hall_floor_id ?? null,
1302
+ type: element.type,
1303
+ shapeConfig,
1304
+ tickets: elementTickets,
1305
+ ticketByNumeration,
1306
+ elementName,
1307
+ seatPositions,
1308
+ chairPositions,
1309
+ zoneCapacity: element.total_capacity,
1310
+ });
1311
+ }
1312
+ return result;
1313
+ }
1314
+ function findTicketAtPosition(renderData, relX, relY) {
1315
+ if (renderData.type === RoomMapElementType.SEAT_BLOCK && renderData.seatPositions) {
1316
+ const config = renderData.shapeConfig;
1317
+ const w = config['width'] || 300;
1318
+ const hasTitle = !!(renderData.elementName && config['show_element_name'] !== false);
1319
+ const chairsStartY = hasTitle ? TICKET_MAP_TITLE_HEIGHT : 0;
1320
+ const col = Math.floor(relX / TICKET_MAP_GRID_SIZE);
1321
+ const row = Math.floor((relY - chairsStartY) / TICKET_MAP_GRID_SIZE);
1322
+ if (col < 0 ||
1323
+ row < 0 ||
1324
+ row >= renderData.seatPositions.length ||
1325
+ col >= (renderData.seatPositions[0]?.length || 0)) {
1326
+ return null;
1327
+ }
1328
+ const seat = renderData.seatPositions[row]?.[col];
1329
+ if (!seat)
1330
+ return null;
1331
+ const seatState = renderData.shapeConfig['seats']?.[row]?.[col];
1332
+ if (seatState?.is_non_existent)
1333
+ return null;
1334
+ return renderData.ticketByNumeration.get(seat.seatLabel) || null;
1335
+ }
1336
+ if (renderData.type === RoomMapElementType.TABLE && renderData.chairPositions) {
1337
+ for (const chair of renderData.chairPositions) {
1338
+ const seatState = renderData.shapeConfig['seats']?.[0]?.[chair.index];
1339
+ if (seatState?.is_non_existent)
1340
+ continue;
1341
+ const dx = relX - chair.x;
1342
+ const dy = relY - chair.y;
1343
+ const distance = Math.sqrt(dx * dx + dy * dy);
1344
+ const w = renderData.shapeConfig['width'] || 150;
1345
+ const h = renderData.shapeConfig['height'] || 150;
1346
+ const chairRadius = Math.min(w, h) * 0.1;
1347
+ if (distance <= chairRadius + 5) {
1348
+ const key = `S${chair.index + 1}`;
1349
+ return renderData.ticketByNumeration.get(key) || null;
1350
+ }
1351
+ }
1352
+ return null;
1353
+ }
1354
+ if (renderData.type === RoomMapElementType.ZONE) {
1355
+ for (const ticket of renderData.ticketByNumeration.values()) {
1356
+ return ticket;
1357
+ }
1358
+ return renderData.tickets[0] || null;
1359
+ }
1360
+ return null;
1361
+ }
1362
+ function findElementAtPosition(renderDataList, stageX, stageY) {
1363
+ for (let i = renderDataList.length - 1; i >= 0; i--) {
1364
+ const el = renderDataList[i];
1365
+ const cfg = el.shapeConfig;
1366
+ const ex = cfg['x'] || 0;
1367
+ const ey = cfg['y'] || 0;
1368
+ const ew = cfg['width'] || 0;
1369
+ const eh = cfg['height'] || 0;
1370
+ const rotation = cfg['rotation'] || 0;
1371
+ if (rotation) {
1372
+ const rad = rotation * (Math.PI / 180);
1373
+ const cos = Math.cos(-rad);
1374
+ const sin = Math.sin(-rad);
1375
+ const dx = stageX - ex;
1376
+ const dy = stageY - ey;
1377
+ const localX = dx * cos - dy * sin;
1378
+ const localY = dx * sin + dy * cos;
1379
+ if (localX >= 0 && localX <= ew && localY >= 0 && localY <= eh) {
1380
+ return { element: el, relX: localX, relY: localY };
1381
+ }
1382
+ }
1383
+ else {
1384
+ if (stageX >= ex && stageX <= ex + ew && stageY >= ey && stageY <= ey + eh) {
1385
+ return { element: el, relX: stageX - ex, relY: stageY - ey };
1386
+ }
1387
+ }
1388
+ }
1389
+ return null;
1390
+ }
1391
+
1392
+ // Helper function to convert number to multi-letter label (A, B, ..., Z, AA, AB, ...)
1393
+ const numberToLetter = (num) => {
1394
+ if (num < 0)
1395
+ return 'A';
1396
+ let result = '';
1397
+ let n = num;
1398
+ while (n >= 0) {
1399
+ result = String.fromCharCode(65 + (n % 26)) + result;
1400
+ n = Math.floor(n / 26) - 1;
1401
+ }
1402
+ return result;
1403
+ };
1404
+ const drawChairIcon = (con, cx, cy, size) => {
1405
+ con.beginPath();
1406
+ const backWidth = size * 0.7;
1407
+ const backHeight = size * 0.6;
1408
+ const backX = cx - backWidth / 2;
1409
+ const backY = cy - backHeight / 2 - size * 0.1;
1410
+ con.moveTo(backX + backWidth * 0.2, backY);
1411
+ con.lineTo(backX + backWidth * 0.8, backY);
1412
+ con.quadraticCurveTo(backX + backWidth, backY, backX + backWidth, backY + backHeight * 0.2);
1413
+ con.lineTo(backX + backWidth, backY + backHeight * 0.8);
1414
+ con.quadraticCurveTo(backX + backWidth, backY + backHeight, backX + backWidth * 0.8, backY + backHeight);
1415
+ con.lineTo(backX + backWidth * 0.2, backY + backHeight);
1416
+ con.quadraticCurveTo(backX, backY + backHeight, backX, backY + backHeight * 0.8);
1417
+ con.lineTo(backX, backY + backHeight * 0.2);
1418
+ con.quadraticCurveTo(backX, backY, backX + backWidth * 0.2, backY);
1419
+ const chairSeatWidth = size * 0.8;
1420
+ const chairSeatHeight = size * 0.3;
1421
+ const seatX = cx - chairSeatWidth / 2;
1422
+ const seatY = cy + backHeight * 0.3;
1423
+ con.moveTo(seatX, seatY);
1424
+ con.lineTo(seatX + chairSeatWidth, seatY);
1425
+ con.lineTo(seatX + chairSeatWidth * 0.9, seatY + chairSeatHeight);
1426
+ con.lineTo(seatX + chairSeatWidth * 0.1, seatY + chairSeatHeight);
1427
+ con.closePath();
1428
+ const legWidth = size * 0.1;
1429
+ const legHeight = size * 0.2;
1430
+ con.moveTo(seatX + chairSeatWidth * 0.1, seatY + chairSeatHeight);
1431
+ con.lineTo(seatX + chairSeatWidth * 0.1 + legWidth, seatY + chairSeatHeight);
1432
+ con.lineTo(seatX + chairSeatWidth * 0.1 + legWidth, seatY + chairSeatHeight + legHeight);
1433
+ con.lineTo(seatX + chairSeatWidth * 0.1, seatY + chairSeatHeight + legHeight);
1434
+ con.moveTo(seatX + chairSeatWidth * 0.9 - legWidth, seatY + chairSeatHeight);
1435
+ con.lineTo(seatX + chairSeatWidth * 0.9, seatY + chairSeatHeight);
1436
+ con.lineTo(seatX + chairSeatWidth * 0.9, seatY + chairSeatHeight + legHeight);
1437
+ con.lineTo(seatX + chairSeatWidth * 0.9 - legWidth, seatY + chairSeatHeight + legHeight);
1438
+ con.closePath();
1439
+ };
1440
+ const generateSeatBlockScene = (con, shape, numerationConfig) => {
1441
+ const w = shape.width();
1442
+ const h = shape.height();
1443
+ const seatSize = TICKET_MAP_GRID_SIZE;
1444
+ const chairScale = 0.8;
1445
+ const elementName = shape?.attrs?.element_name || 'Asientos';
1446
+ const showElementName = shape?.attrs?.show_element_name ?? true;
1447
+ const seatsData = shape?.attrs?.seats;
1448
+ const hasTitle = !!(elementName && showElementName);
1449
+ const titleHeight = hasTitle ? TICKET_MAP_GRID_SIZE : 0;
1450
+ const chairsStartY = titleHeight;
1451
+ const cols = Math.floor(w / seatSize);
1452
+ const rows = Math.floor((h - chairsStartY) / seatSize);
1453
+ const config = numerationConfig || {
1454
+ start_row_letter: 'A',
1455
+ row_order: 'TOP_TO_BOTTOM',
1456
+ start_column_number: 1,
1457
+ column_order: 'LEFT_TO_RIGHT',
1458
+ };
1459
+ const startRowLetter = config?.start_row_letter?.toUpperCase() || 'A';
1460
+ const startRowNum = startRowLetter.charCodeAt(0) - 65;
1461
+ if (hasTitle) {
1462
+ con.save();
1463
+ const textFont = `bold 10px Arial`;
1464
+ con.font = textFont;
1465
+ const textWidth = con.measureText(elementName).width;
1466
+ const paddingX = 10;
1467
+ const containerHeight = 18;
1468
+ const containerWidth = textWidth + paddingX * 2;
1469
+ const containerX = (w - containerWidth) / 2;
1470
+ const containerY = (titleHeight - containerHeight) / 2;
1471
+ const borderRadius = 4;
1472
+ const lineY = titleHeight;
1473
+ const strokeColor = shape.getAttr('stroke') || '#999';
1474
+ con.beginPath();
1475
+ con.moveTo(0, lineY);
1476
+ con.lineTo(w, lineY);
1477
+ con.strokeStyle = strokeColor;
1478
+ con.lineWidth = 1;
1479
+ con.stroke();
1480
+ con.beginPath();
1481
+ con.moveTo(containerX + borderRadius, containerY);
1482
+ con.lineTo(containerX + containerWidth - borderRadius, containerY);
1483
+ con.quadraticCurveTo(containerX + containerWidth, containerY, containerX + containerWidth, containerY + borderRadius);
1484
+ con.lineTo(containerX + containerWidth, containerY + containerHeight - borderRadius);
1485
+ con.quadraticCurveTo(containerX + containerWidth, containerY + containerHeight, containerX + containerWidth - borderRadius, containerY + containerHeight);
1486
+ con.lineTo(containerX + borderRadius, containerY + containerHeight);
1487
+ con.quadraticCurveTo(containerX, containerY + containerHeight, containerX, containerY + containerHeight - borderRadius);
1488
+ con.lineTo(containerX, containerY + borderRadius);
1489
+ con.quadraticCurveTo(containerX, containerY, containerX + borderRadius, containerY);
1490
+ con.closePath();
1491
+ con.fillStrokeShape(shape);
1492
+ con.font = textFont;
1493
+ con.fillStyle = strokeColor;
1494
+ con.textAlign = 'center';
1495
+ con.textBaseline = 'middle';
1496
+ con.fillText(elementName, w / 2, containerY + containerHeight / 2);
1497
+ con.restore();
1498
+ }
1499
+ for (let x = 0; x < w; x += seatSize) {
1500
+ for (let y = chairsStartY; y < h; y += seatSize) {
1501
+ const colIndex = Math.floor(x / seatSize);
1502
+ const rowIndex = Math.floor((y - chairsStartY) / seatSize);
1503
+ let actualRowIndex = rowIndex;
1504
+ if (config.row_order === 'BOTTOM_TO_TOP') {
1505
+ actualRowIndex = rows - 1 - rowIndex;
1506
+ }
1507
+ let actualColIndex = colIndex;
1508
+ if (config.column_order === 'RIGHT_TO_LEFT') {
1509
+ actualColIndex = cols - 1 - colIndex;
1510
+ }
1511
+ const rowLetter = numberToLetter(startRowNum + actualRowIndex);
1512
+ const colNumber = Number(config.start_column_number) + Number(actualColIndex);
1513
+ const seatNumber = `${rowLetter}${colNumber}`;
1514
+ const centerX = x + seatSize / 2 + 1;
1515
+ const centerY = y + seatSize / 2 - 2;
1516
+ const size = seatSize * chairScale;
1517
+ // Check seat state
1518
+ const seatState = seatsData?.[rowIndex]?.[colIndex];
1519
+ // Skip non-existent seats
1520
+ if (seatState?.is_non_existent) {
1521
+ continue;
1522
+ }
1523
+ con.save();
1524
+ if (seatState?.is_accessible) {
1525
+ drawWheelchairIcon(con, shape, centerX, centerY, size);
1526
+ }
1527
+ else {
1528
+ drawChairIcon(con, centerX, centerY, size);
1529
+ con.fillStrokeShape(shape);
1530
+ // Draw seat number
1531
+ con.fillStyle = '#333333';
1532
+ con.font = `${size * 0.3}px Arial`;
1533
+ con.textAlign = 'center';
1534
+ con.textBaseline = 'middle';
1535
+ con.fillText(seatNumber, centerX, centerY - 1);
1536
+ }
1537
+ con.restore();
1538
+ }
1539
+ }
1540
+ };
1541
+
1542
+ const generateStageScene = (con, shape) => {
1543
+ const w = shape.width();
1544
+ const h = shape.height();
1545
+ const padding = 4;
1546
+ con.save();
1547
+ // Stage background (rectangle with rounded corners)
1548
+ const exitX = padding;
1549
+ const exitY = padding;
1550
+ const exitWidth = w - padding * 2;
1551
+ const exitHeight = h - padding * 2;
1552
+ const cornerRadius = Math.min(exitWidth, exitHeight) * 0.2;
1553
+ con.beginPath();
1554
+ con.moveTo(exitX + cornerRadius, exitY);
1555
+ con.lineTo(exitX + exitWidth - cornerRadius, exitY);
1556
+ con.quadraticCurveTo(exitX + exitWidth, exitY, exitX + exitWidth, exitY + cornerRadius);
1557
+ con.lineTo(exitX + exitWidth, exitY + exitHeight);
1558
+ con.lineTo(exitX, exitY + exitHeight);
1559
+ con.lineTo(exitX, exitY + cornerRadius);
1560
+ con.quadraticCurveTo(exitX, exitY, exitX + cornerRadius, exitY);
1561
+ con.closePath();
1562
+ con.fillStrokeShape(shape);
1563
+ // Stage text
1564
+ const centerX = w / 2;
1565
+ const centerY = h / 2;
1566
+ con.font = `12px Arial`;
1567
+ con.fillStyle = shape.getAttr('stroke');
1568
+ con.textAlign = 'center';
1569
+ con.textBaseline = 'middle';
1570
+ const stageText = shape?.attrs?.element_name || 'Escenario';
1571
+ const dotRadius = 2.5;
1572
+ const dotGap = 10;
1573
+ const textWidth = con.measureText(stageText).width;
1574
+ // Dot before text
1575
+ con.beginPath();
1576
+ con.arc(centerX - textWidth / 2 - dotGap, centerY, dotRadius, 0, Math.PI * 2);
1577
+ con.fillStyle = shape.getAttr('stroke');
1578
+ con.fill();
1579
+ // Text
1580
+ con.fillText(stageText, centerX, centerY);
1581
+ // Dot after text
1582
+ con.beginPath();
1583
+ con.arc(centerX + textWidth / 2 + dotGap, centerY, dotRadius, 0, Math.PI * 2);
1584
+ con.fillStyle = shape.getAttr('stroke');
1585
+ con.fill();
1586
+ // Footer below the element: line and "▼ Vista del público"
1587
+ const lineColor = '#b0b0b0';
1588
+ const footerCenterY = h + TICKET_MAP_GRID_SIZE / 2;
1589
+ con.beginPath();
1590
+ con.moveTo(0, footerCenterY);
1591
+ con.lineTo(w, footerCenterY);
1592
+ con.strokeStyle = lineColor;
1593
+ con.lineWidth = 1;
1594
+ con.stroke();
1595
+ const labelText = '▼ Vista del público';
1596
+ const textFont = `bold 10px Arial`;
1597
+ con.font = textFont;
1598
+ const labelWidth = con.measureText(labelText).width;
1599
+ const paddingX = 10;
1600
+ const containerHeight = 18;
1601
+ const containerWidth = labelWidth + paddingX * 2;
1602
+ const containerX = (w - containerWidth) / 2;
1603
+ const containerY = footerCenterY - containerHeight / 2;
1604
+ const borderRadius = 4;
1605
+ con.beginPath();
1606
+ con.moveTo(containerX + borderRadius, containerY);
1607
+ con.lineTo(containerX + containerWidth - borderRadius, containerY);
1608
+ con.quadraticCurveTo(containerX + containerWidth, containerY, containerX + containerWidth, containerY + borderRadius);
1609
+ con.lineTo(containerX + containerWidth, containerY + containerHeight - borderRadius);
1610
+ con.quadraticCurveTo(containerX + containerWidth, containerY + containerHeight, containerX + containerWidth - borderRadius, containerY + containerHeight);
1611
+ con.lineTo(containerX + borderRadius, containerY + containerHeight);
1612
+ con.quadraticCurveTo(containerX, containerY + containerHeight, containerX, containerY + containerHeight - borderRadius);
1613
+ con.lineTo(containerX, containerY + borderRadius);
1614
+ con.quadraticCurveTo(containerX, containerY, containerX + borderRadius, containerY);
1615
+ con.closePath();
1616
+ con.fillStyle = '#ededed';
1617
+ con.fill();
1618
+ con.strokeStyle = lineColor;
1619
+ con.lineWidth = 1;
1620
+ con.stroke();
1621
+ con.font = textFont;
1622
+ con.fillStyle = lineColor;
1623
+ con.textAlign = 'center';
1624
+ con.textBaseline = 'middle';
1625
+ con.fillText(labelText, w / 2, footerCenterY);
1626
+ con.restore();
1627
+ };
1628
+
1629
+ const generateStairScene = (con, shape) => {
1630
+ const w = shape.width();
1631
+ const h = shape.height();
1632
+ const padding = 4;
1633
+ con.save();
1634
+ const exitX = padding;
1635
+ const exitY = padding;
1636
+ const exitWidth = w - padding * 2;
1637
+ const exitHeight = h - padding * 2;
1638
+ const cornerRadius = Math.min(exitWidth, exitHeight) * 0.2;
1639
+ drawRoundedRect(con, shape, exitX, exitY, exitWidth, exitHeight, cornerRadius);
1640
+ // Stage text - check if parent has element_name, otherwise use "ESCENARIO"
1641
+ // const fontSize = Math.min(w, h) * 0.35;
1642
+ con.font = `11px Arial`;
1643
+ con.fillStyle = '#333333';
1644
+ con.textAlign = 'center';
1645
+ con.textBaseline = 'middle';
1646
+ const centerX = w / 2;
1647
+ const centerY = h / 2;
1648
+ // Get parent element and check for element_name
1649
+ const stageText = '🪜' + (shape?.attrs?.element_name || 'Escalera');
1650
+ con.fillText(stageText, centerX, centerY);
1651
+ con.restore();
1652
+ };
1653
+
1654
+ const generateTableScene = (con, shape) => {
1655
+ const w = shape.width();
1656
+ const h = shape.height();
1657
+ const fillColor = shape.fill();
1658
+ const strokeColor = shape.stroke();
1659
+ const totalCapacity = shape?.attrs?.total_capacity ?? 0;
1660
+ const zoneText = shape?.attrs?.element_name || 'Mesa';
1661
+ const seatsData = shape?.attrs?.seats;
1662
+ const cx = w / 2;
1663
+ const cy = h / 2;
1664
+ // Radio de la mesa: el más pequeño entre ancho y alto, con un poco de margen
1665
+ // para dejar espacio a las sillas alrededor
1666
+ const chairRadius = Math.min(w, h) * 0.1;
1667
+ const gap = chairRadius * 0.4; // espacio entre mesa y sillas
1668
+ const tableRadius = Math.min(w, h) / 2 - chairRadius * 2 - gap;
1669
+ con.save();
1670
+ // ── Sillas (se dibujan primero para quedar detrás) ──────────────────────────
1671
+ if (totalCapacity > 0) {
1672
+ const orbitRadius = tableRadius + gap + chairRadius;
1673
+ for (let i = 0; i < totalCapacity; i++) {
1674
+ const seatState = seatsData?.[0]?.[i];
1675
+ if (seatState?.is_non_existent) {
1676
+ continue;
1677
+ }
1678
+ const angle = (2 * Math.PI * i) / totalCapacity - Math.PI / 2;
1679
+ const scx = cx + orbitRadius * Math.cos(angle);
1680
+ const scy = cy + orbitRadius * Math.sin(angle);
1681
+ if (seatState?.is_accessible) {
1682
+ drawWheelchairIcon(con, shape, scx, scy, chairRadius * 2);
1683
+ continue;
1684
+ }
1685
+ con.beginPath();
1686
+ con.arc(scx, scy, chairRadius, 0, Math.PI * 2);
1687
+ con.closePath();
1688
+ // Color de relleno de las sillas = stroke del shape
1689
+ con.fillStyle = strokeColor;
1690
+ con.fill();
1691
+ // Borde de las sillas = fill del shape
1692
+ con.strokeStyle = fillColor;
1693
+ con.lineWidth = Math.max(1, chairRadius * 0.15);
1694
+ con.stroke();
1695
+ }
1696
+ }
1697
+ // ── Mesa (círculo central) ──────────────────────────────────────────────────
1698
+ con.beginPath();
1699
+ con.arc(cx, cy, tableRadius, 0, Math.PI * 2);
1700
+ con.closePath();
1701
+ con.fillStrokeShape(shape); // usa fill y stroke propios del shape de Konva
1702
+ // ── Texto centrado ──────────────────────────────────────────────────────────
1703
+ const fontSize = tableRadius * 0.45;
1704
+ con.font = `${fontSize}px Arial`;
1705
+ con.fillStyle = strokeColor;
1706
+ con.textAlign = 'center';
1707
+ con.textBaseline = 'middle';
1708
+ con.fillText(zoneText, cx, cy);
1709
+ con.restore();
1710
+ };
1711
+
1712
+ const generateUnavailableSpaceScene = (con, shape) => {
1713
+ const w = shape.width();
1714
+ const h = shape.height();
1715
+ const padding = 4;
1716
+ con.save();
1717
+ const exitX = padding;
1718
+ const exitY = padding;
1719
+ const exitWidth = w - padding * 2;
1720
+ const exitHeight = h - padding * 2;
1721
+ const cornerRadius = Math.min(exitWidth, exitHeight) * 0.2;
1722
+ drawRoundedRect(con, shape, exitX, exitY, exitWidth, exitHeight, cornerRadius);
1723
+ // Stage text - check if parent has element_name, otherwise use "ESCENARIO"
1724
+ // const fontSize = Math.min(w, h) * 0.35;
1725
+ con.font = `11px Arial`;
1726
+ con.fillStyle = '#333333';
1727
+ con.textAlign = 'center';
1728
+ con.textBaseline = 'middle';
1729
+ const centerX = w / 2;
1730
+ const centerY = h / 2;
1731
+ // Get parent element and check for element_name
1732
+ const stageText = '🚫 ' + (shape?.attrs?.element_name || 'No disponible');
1733
+ con.fillText(stageText, centerX, centerY);
1734
+ con.restore();
1735
+ };
1736
+
1737
+ const generateZoneScene = (con, shape) => {
1738
+ const w = shape.width();
1739
+ const h = shape.height();
1740
+ const padding = 4;
1741
+ con.save();
1742
+ const zoneX = padding;
1743
+ const zoneY = padding;
1744
+ const zoneWidth = w - padding * 2;
1745
+ const zoneHeight = h - padding * 2;
1746
+ const cornerRadius = Math.min(zoneWidth, zoneHeight) * 0.05;
1747
+ drawRoundedRect(con, shape, zoneX, zoneY, zoneWidth, zoneHeight, cornerRadius);
1748
+ // Zone text - check if parent has element_name, otherwise use "Zona"
1749
+ con.font = `11px Arial`;
1750
+ con.fillStyle = shape.getAttr('stroke');
1751
+ con.textAlign = 'center';
1752
+ con.textBaseline = 'middle';
1753
+ const centerX = w / 2;
1754
+ const centerY = h / 2;
1755
+ // Get parent element and check for element_name
1756
+ const zoneText = shape?.attrs?.element_name || '';
1757
+ con.fillText(zoneText, centerX, centerY);
1758
+ con.restore();
1759
+ };
1760
+
580
1761
  function MinTodayValidator(controlName) {
581
1762
  return (formGroup) => {
582
1763
  const group = formGroup;
@@ -1813,11 +2994,11 @@ class FeedbackModalComponent {
1813
2994
  }
1814
2995
  }
1815
2996
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: FeedbackModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1816
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: FeedbackModalComponent, isStandalone: true, selector: "feedback-modal", inputs: { isOpen: "isOpen", type: "type", title: "title", message: "message", showHeader: "showHeader", showCloseButton: "showCloseButton", showTitle: "showTitle", autoClose: "autoClose", autoCloseDuration: "autoCloseDuration", buttonText: "buttonText" }, outputs: { closed: "closed" }, usesOnChanges: true, ngImport: i0, template: "<app-modal\n [showHeader]=\"showHeader\"\n [showCloseButton]=\"showCloseButton\"\n [showTitle]=\"showTitle\"\n [isOpen]=\"isOpen\"\n size=\"sm\"\n (closeModal)=\"closeModal()\"\n>\n <div class=\"feedback-modal-body\" [ngClass]=\"'feedback-modal-body--' + type\">\n <div class=\"feedback-modal-icon-wrapper\">\n @switch (type) {\n @case ('loading') {\n <div class=\"feedback-modal-spinner\"></div>\n }\n @case ('success') {\n <i class=\"ri-check-line feedback-modal-icon feedback-modal-icon--success\"></i>\n }\n @case ('error') {\n <i class=\"ri-error-warning-line feedback-modal-icon feedback-modal-icon--error\"></i>\n }\n @case ('warning') {\n <i class=\"ri-alert-line feedback-modal-icon feedback-modal-icon--warning\"></i>\n }\n @case ('info') {\n <i class=\"ri-info-i feedback-modal-icon feedback-modal-icon--info\"></i>\n }\n }\n </div>\n <h3 class=\"feedback-modal-title\">{{ title }}</h3>\n @if (message) {\n <p class=\"feedback-modal-message\">{{ message }}</p>\n }\n <app-button [text]=\"resolvedButtonText\" [variant]=\"buttonVariant\" (clicked)=\"closeModal()\" />\n </div>\n</app-modal>\n", styles: [".feedback-modal-body{display:flex;flex-direction:column;align-items:center;gap:1rem;padding:1.5rem 0;text-align:center}.feedback-modal-icon-wrapper{display:flex;align-items:center;justify-content:center;width:3.5rem;height:3.5rem;border-radius:9999px}.feedback-modal-body--loading .feedback-modal-icon-wrapper{width:auto;height:auto}.feedback-modal-body--success .feedback-modal-icon-wrapper{background-color:#f0fdf4}.feedback-modal-body--error .feedback-modal-icon-wrapper{background-color:#fef2f2}.feedback-modal-body--warning .feedback-modal-icon-wrapper{background-color:#fefce8}.feedback-modal-body--info .feedback-modal-icon-wrapper{background-color:#eff6ff}.feedback-modal-icon{font-size:2.5rem}.feedback-modal-icon--success{color:#22c55e}.feedback-modal-icon--error{color:#ef4444}.feedback-modal-icon--warning{color:#eab308}.feedback-modal-icon--info{color:#3b82f6}.feedback-modal-spinner{height:3rem;width:3rem;border-radius:9999px;border:6px solid #fecaca;border-top-color:#ef4444;animation:feedback-modal-spin .8s linear infinite}.feedback-modal-title{font-size:1.125rem;font-weight:600;color:#1f2937;margin:0}.feedback-modal-message{font-size:.875rem;color:#6b7280;margin:0}@keyframes feedback-modal-spin{to{transform:rotate(360deg)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: AppModalComponent, selector: "app-modal", inputs: ["title", "disableClose", "showHeader", "showCloseButton", "showTitle", "isOpen", "size"], outputs: ["closeModal"] }, { kind: "component", type: AppButtonComponent, selector: "app-button", inputs: ["disabled", "loading", "type", "variant", "text", "size", "loadingText", "icon"], outputs: ["clicked"] }], encapsulation: i0.ViewEncapsulation.None });
2997
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: FeedbackModalComponent, isStandalone: true, selector: "feedback-modal", inputs: { isOpen: "isOpen", type: "type", title: "title", message: "message", showHeader: "showHeader", showCloseButton: "showCloseButton", showTitle: "showTitle", autoClose: "autoClose", autoCloseDuration: "autoCloseDuration", buttonText: "buttonText" }, outputs: { closed: "closed" }, usesOnChanges: true, ngImport: i0, template: "<app-modal\n [showHeader]=\"showHeader\"\n [showCloseButton]=\"showCloseButton\"\n [showTitle]=\"showTitle\"\n [isOpen]=\"isOpen\"\n size=\"sm\"\n (closeModal)=\"closeModal()\"\n>\n <div class=\"feedback-modal-body\" [ngClass]=\"'feedback-modal-body--' + type\">\n <div class=\"feedback-modal-icon-wrapper\">\n @switch (type) {\n @case ('loading') {\n <div class=\"feedback-modal-spinner\"></div>\n }\n @case ('success') {\n <i class=\"ri-check-line feedback-modal-icon feedback-modal-icon--success\"></i>\n }\n @case ('error') {\n <i class=\"ri-error-warning-line feedback-modal-icon feedback-modal-icon--error\"></i>\n }\n @case ('warning') {\n <i class=\"ri-alert-line feedback-modal-icon feedback-modal-icon--warning\"></i>\n }\n @case ('info') {\n <i class=\"ri-info-i feedback-modal-icon feedback-modal-icon--info\"></i>\n }\n }\n </div>\n\n <h3 class=\"feedback-modal-title\">{{ title }}</h3>\n\n @if (message) {\n <p class=\"feedback-modal-message\">{{ message }}</p>\n }\n\n @if (resolvedButtonText && showCloseButton) {\n <app-button [text]=\"resolvedButtonText\" [variant]=\"buttonVariant\" (clicked)=\"closeModal()\" />\n }\n </div>\n</app-modal>\n", styles: [".feedback-modal-body{display:flex;flex-direction:column;align-items:center;gap:1rem;padding:1.5rem 0;text-align:center}.feedback-modal-icon-wrapper{display:flex;align-items:center;justify-content:center;width:3.5rem;height:3.5rem;border-radius:9999px}.feedback-modal-body--loading .feedback-modal-icon-wrapper{width:auto;height:auto}.feedback-modal-body--success .feedback-modal-icon-wrapper{background-color:#f0fdf4}.feedback-modal-body--error .feedback-modal-icon-wrapper{background-color:#fef2f2}.feedback-modal-body--warning .feedback-modal-icon-wrapper{background-color:#fefce8}.feedback-modal-body--info .feedback-modal-icon-wrapper{background-color:#eff6ff}.feedback-modal-icon{font-size:2.5rem}.feedback-modal-icon--success{color:#22c55e}.feedback-modal-icon--error{color:#ef4444}.feedback-modal-icon--warning{color:#eab308}.feedback-modal-icon--info{color:#3b82f6}.feedback-modal-spinner{height:3rem;width:3rem;border-radius:9999px;border:6px solid #fecaca;border-top-color:#ef4444;animation:feedback-modal-spin .8s linear infinite}.feedback-modal-title{font-size:1.125rem;font-weight:600;color:#1f2937;margin:0}.feedback-modal-message{font-size:.875rem;color:#6b7280;margin:0}@keyframes feedback-modal-spin{to{transform:rotate(360deg)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: AppModalComponent, selector: "app-modal", inputs: ["title", "disableClose", "showHeader", "showCloseButton", "showTitle", "isOpen", "size"], outputs: ["closeModal"] }, { kind: "component", type: AppButtonComponent, selector: "app-button", inputs: ["disabled", "loading", "type", "variant", "text", "size", "loadingText", "icon"], outputs: ["clicked"] }], encapsulation: i0.ViewEncapsulation.None });
1817
2998
  }
1818
2999
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: FeedbackModalComponent, decorators: [{
1819
3000
  type: Component,
1820
- args: [{ selector: 'feedback-modal', standalone: true, imports: [CommonModule, AppModalComponent, AppButtonComponent], encapsulation: ViewEncapsulation.None, template: "<app-modal\n [showHeader]=\"showHeader\"\n [showCloseButton]=\"showCloseButton\"\n [showTitle]=\"showTitle\"\n [isOpen]=\"isOpen\"\n size=\"sm\"\n (closeModal)=\"closeModal()\"\n>\n <div class=\"feedback-modal-body\" [ngClass]=\"'feedback-modal-body--' + type\">\n <div class=\"feedback-modal-icon-wrapper\">\n @switch (type) {\n @case ('loading') {\n <div class=\"feedback-modal-spinner\"></div>\n }\n @case ('success') {\n <i class=\"ri-check-line feedback-modal-icon feedback-modal-icon--success\"></i>\n }\n @case ('error') {\n <i class=\"ri-error-warning-line feedback-modal-icon feedback-modal-icon--error\"></i>\n }\n @case ('warning') {\n <i class=\"ri-alert-line feedback-modal-icon feedback-modal-icon--warning\"></i>\n }\n @case ('info') {\n <i class=\"ri-info-i feedback-modal-icon feedback-modal-icon--info\"></i>\n }\n }\n </div>\n <h3 class=\"feedback-modal-title\">{{ title }}</h3>\n @if (message) {\n <p class=\"feedback-modal-message\">{{ message }}</p>\n }\n <app-button [text]=\"resolvedButtonText\" [variant]=\"buttonVariant\" (clicked)=\"closeModal()\" />\n </div>\n</app-modal>\n", styles: [".feedback-modal-body{display:flex;flex-direction:column;align-items:center;gap:1rem;padding:1.5rem 0;text-align:center}.feedback-modal-icon-wrapper{display:flex;align-items:center;justify-content:center;width:3.5rem;height:3.5rem;border-radius:9999px}.feedback-modal-body--loading .feedback-modal-icon-wrapper{width:auto;height:auto}.feedback-modal-body--success .feedback-modal-icon-wrapper{background-color:#f0fdf4}.feedback-modal-body--error .feedback-modal-icon-wrapper{background-color:#fef2f2}.feedback-modal-body--warning .feedback-modal-icon-wrapper{background-color:#fefce8}.feedback-modal-body--info .feedback-modal-icon-wrapper{background-color:#eff6ff}.feedback-modal-icon{font-size:2.5rem}.feedback-modal-icon--success{color:#22c55e}.feedback-modal-icon--error{color:#ef4444}.feedback-modal-icon--warning{color:#eab308}.feedback-modal-icon--info{color:#3b82f6}.feedback-modal-spinner{height:3rem;width:3rem;border-radius:9999px;border:6px solid #fecaca;border-top-color:#ef4444;animation:feedback-modal-spin .8s linear infinite}.feedback-modal-title{font-size:1.125rem;font-weight:600;color:#1f2937;margin:0}.feedback-modal-message{font-size:.875rem;color:#6b7280;margin:0}@keyframes feedback-modal-spin{to{transform:rotate(360deg)}}\n"] }]
3001
+ args: [{ selector: 'feedback-modal', standalone: true, imports: [CommonModule, AppModalComponent, AppButtonComponent], encapsulation: ViewEncapsulation.None, template: "<app-modal\n [showHeader]=\"showHeader\"\n [showCloseButton]=\"showCloseButton\"\n [showTitle]=\"showTitle\"\n [isOpen]=\"isOpen\"\n size=\"sm\"\n (closeModal)=\"closeModal()\"\n>\n <div class=\"feedback-modal-body\" [ngClass]=\"'feedback-modal-body--' + type\">\n <div class=\"feedback-modal-icon-wrapper\">\n @switch (type) {\n @case ('loading') {\n <div class=\"feedback-modal-spinner\"></div>\n }\n @case ('success') {\n <i class=\"ri-check-line feedback-modal-icon feedback-modal-icon--success\"></i>\n }\n @case ('error') {\n <i class=\"ri-error-warning-line feedback-modal-icon feedback-modal-icon--error\"></i>\n }\n @case ('warning') {\n <i class=\"ri-alert-line feedback-modal-icon feedback-modal-icon--warning\"></i>\n }\n @case ('info') {\n <i class=\"ri-info-i feedback-modal-icon feedback-modal-icon--info\"></i>\n }\n }\n </div>\n\n <h3 class=\"feedback-modal-title\">{{ title }}</h3>\n\n @if (message) {\n <p class=\"feedback-modal-message\">{{ message }}</p>\n }\n\n @if (resolvedButtonText && showCloseButton) {\n <app-button [text]=\"resolvedButtonText\" [variant]=\"buttonVariant\" (clicked)=\"closeModal()\" />\n }\n </div>\n</app-modal>\n", styles: [".feedback-modal-body{display:flex;flex-direction:column;align-items:center;gap:1rem;padding:1.5rem 0;text-align:center}.feedback-modal-icon-wrapper{display:flex;align-items:center;justify-content:center;width:3.5rem;height:3.5rem;border-radius:9999px}.feedback-modal-body--loading .feedback-modal-icon-wrapper{width:auto;height:auto}.feedback-modal-body--success .feedback-modal-icon-wrapper{background-color:#f0fdf4}.feedback-modal-body--error .feedback-modal-icon-wrapper{background-color:#fef2f2}.feedback-modal-body--warning .feedback-modal-icon-wrapper{background-color:#fefce8}.feedback-modal-body--info .feedback-modal-icon-wrapper{background-color:#eff6ff}.feedback-modal-icon{font-size:2.5rem}.feedback-modal-icon--success{color:#22c55e}.feedback-modal-icon--error{color:#ef4444}.feedback-modal-icon--warning{color:#eab308}.feedback-modal-icon--info{color:#3b82f6}.feedback-modal-spinner{height:3rem;width:3rem;border-radius:9999px;border:6px solid #fecaca;border-top-color:#ef4444;animation:feedback-modal-spin .8s linear infinite}.feedback-modal-title{font-size:1.125rem;font-weight:600;color:#1f2937;margin:0}.feedback-modal-message{font-size:.875rem;color:#6b7280;margin:0}@keyframes feedback-modal-spin{to{transform:rotate(360deg)}}\n"] }]
1821
3002
  }], propDecorators: { isOpen: [{
1822
3003
  type: Input
1823
3004
  }], type: [{
@@ -5228,5 +6409,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
5228
6409
  * Generated bundle index. Do not edit.
5229
6410
  */
5230
6411
 
5231
- export { ADMIN_API_ROUTES, ALERT_ICONS, AdminSelectComponent, AdminService, ApiService, AppAlertComponent, AppButtonComponent, AppLinkButtonComponent, AppModalComponent, AsyncSelectComponent, AuditInformationComponent, BASE_BUTTON_CLASSES, BASE_INPUT_CLASSES, BUTTON_SIZES_CLASSES, BUTTON_VARIANT_CLASSES, BaseModalService, CITY_API_ENDPOINTS, COUNTRY_API_ENDPOINTS, CUSTOMERS_API_ROUTES, ChangePasswordFormComponent, ChangePasswordFormService, CitiesService, CitySelectComponent, ColorPickerComponent, CorporateBondStatus, CountrySelectComponent, CountryService, CouponApplicability, CouponDiscountType, CouponStatus, CustomerSelectComponent, CustomerService, DOCUMENT_TYPES_OPTIONS, DateInputComponent, DateService, DaySelectorGridComponent, DeleteConfirmationComponent, DeleteConfirmationService, DynamicTableComponent, ERROR_INPUT_CLASSES, EndDateGreaterThanStartValidator, FORM_ERROR_MESSAGES, FeedbackModalComponent, FeedbackModalService, FileType, FileUploadComponent, FileUploadPreviewComponent, FormEditorComponent, FormInputComponent, FormSelectComponent, FormTextareaComponent, GiftBondStatus, LanguageSwitcherComponent, MinTodayValidator, MustMatchValidator, OrderItemType, OrderStatus, PAYMENT_METHODS_OPTIONS, PERFORMANCES_API_ROUTES, PRICE_ZONES_API_ROUTES, PRODUCTS_API_ROUTES, PRODUCT_CATEGORIES_API_ROUTES, PRODUCT_TAGS_API_ROUTES, PRODUCT_TYPES_API_ROUTES, PerformanceCardComponent, PerformanceCardListComponent, PerformanceMultiSelectComponent, PerformanceSelectComponent, PerformanceService, PerformanceStatus, PerformanceStepperComponent, PerformanceTicketStatus, PerformancesListEventsService, PriceZoneEventService, PriceZoneFormModalService, PriceZoneSelectComponent, PriceZoneService, ProductCategoryService, ProductMultiSelectComponent, ProductSelectComponent, ProductService, ProductTagService, ProductTypeService, RoomMapElementOrientation, RoomMapElementType, SHOWS_API_ROUTES, STATE_API_ENDPOINTS, ShowMultiSelectComponent, ShowSelectComponent, ShowService, ShowsFilterComponent, StateSelectComponent, StatesService, TICKERA_COMPONENTS_CONFIG, TickeraTranslocoLoader, ToastService, ToggleSwitchComponent, VENUES_API_ROUTES, ZonePriceItemComponent, ZonePriceListComponent, authInterceptor, formatCitiesResponseToSelect, getBrowserLanguage, getCustomerFullname, getStoredLanguage, parseJsonToFormDataAdvanced, provideTickeraComponents, resolveLanguage, setStoredLanguage, transformImageToFile };
6412
+ export { ADMIN_API_ROUTES, ALERT_ICONS, AdminSelectComponent, AdminService, ApiService, AppAlertComponent, AppButtonComponent, AppLinkButtonComponent, AppModalComponent, AsyncSelectComponent, AuditInformationComponent, BASE_BUTTON_CLASSES, BASE_INPUT_CLASSES, BUTTON_SIZES_CLASSES, BUTTON_VARIANT_CLASSES, BaseModalService, CITY_API_ENDPOINTS, COUNTRY_API_ENDPOINTS, CUSTOMERS_API_ROUTES, ChangePasswordFormComponent, ChangePasswordFormService, CitiesService, CitySelectComponent, ColorPickerComponent, CorporateBondStatus, CountrySelectComponent, CountryService, CouponApplicability, CouponDiscountType, CouponStatus, CustomerSelectComponent, CustomerService, DEFAULT_STAGE_CONFIG, DOCUMENT_TYPES_OPTIONS, DateInputComponent, DateService, DaySelectorGridComponent, DeleteConfirmationComponent, DeleteConfirmationService, DynamicTableComponent, ERROR_INPUT_CLASSES, EndDateGreaterThanStartValidator, FORM_ERROR_MESSAGES, FeedbackModalComponent, FeedbackModalService, FileType, FileUploadComponent, FileUploadPreviewComponent, FormEditorComponent, FormInputComponent, FormSelectComponent, FormTextareaComponent, GiftBondStatus, LanguageSwitcherComponent, MinTodayValidator, MustMatchValidator, OrderItemType, OrderStatus, PAYMENT_METHODS_OPTIONS, PERFORMANCES_API_ROUTES, PRICE_ZONES_API_ROUTES, PRODUCTS_API_ROUTES, PRODUCT_CATEGORIES_API_ROUTES, PRODUCT_TAGS_API_ROUTES, PRODUCT_TYPES_API_ROUTES, PerformanceCardComponent, PerformanceCardListComponent, PerformanceMultiSelectComponent, PerformanceSelectComponent, PerformanceService, PerformanceStatus, PerformanceStepperComponent, PerformanceTicketStatus, PerformancesListEventsService, PriceZoneEventService, PriceZoneFormModalService, PriceZoneSelectComponent, PriceZoneService, ProductCategoryService, ProductMultiSelectComponent, ProductSelectComponent, ProductService, ProductTagService, ProductTypeService, RoomMapElementOrientation, RoomMapElementType, SHOWS_API_ROUTES, STATE_API_ENDPOINTS, SelectedDiscountCardType, ShowMultiSelectComponent, ShowSelectComponent, ShowService, ShowsFilterComponent, StateSelectComponent, StatesService, TICKERA_COMPONENTS_CONFIG, TICKET_ELEMENT_TYPES, TICKET_MAP_GRID_SIZE, TICKET_MAP_TITLE_HEIGHT, TICKET_STATUS_COLORS, TICKET_STATUS_FILLS, TICKET_STATUS_LABELS, TickeraTranslocoLoader, ToastService, ToggleSwitchComponent, VENUES_API_ROUTES, ZonePriceItemComponent, ZonePriceListComponent, authInterceptor, drawChairIcon$1 as drawChairIcon, drawHappyFace, drawRoundedRect, drawWheelchairIcon, findElementAtPosition, findTicketAtPosition, formatCitiesResponseToSelect, generateExitScene, generateHallwayScene, generateProductionAreaScene, generateSeatBlockScene, generateSeatBlockTicketScene, generateStageScene, generateStairScene, generateTableScene, generateTableTicketScene, generateUnavailableSpaceScene, generateZoneScene, getBrowserLanguage, getCustomerFullname, getItemTypeIcon, getStoredLanguage, numberToLetter$1 as numberToLetter, parseJsonToFormDataAdvanced, processElementsToRenderData, provideTickeraComponents, resolveLanguage, setStoredLanguage, tintColor, transformImageToFile };
5232
6413
  //# sourceMappingURL=tickera-angular-components.mjs.map