iticket-seatingplan-dev 1.5.2 → 1.5.4
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/dist/components/Badge.js +9 -16
- package/dist/components/Button.js +17 -20
- package/dist/components/Flexi.tsx +59 -0
- package/dist/components/Map.js +3 -7
- package/dist/components/Map.tsx +756 -0
- package/dist/components/PriceButton.tsx +74 -0
- package/dist/components/PricingPopup.tsx +178 -0
- package/dist/components/SeatingPlan.tsx +786 -0
- package/dist/components/assets/encodedSvgs.js +26 -11
- package/dist/components/icons/CloseIcon.tsx +26 -0
- package/dist/components/icons/FlexiIcon.tsx +32 -0
- package/dist/components/icons/TicketIcon.tsx +32 -0
- package/dist/components/styles/flexi.css +180 -0
- package/dist/components/styles/index.css +111 -21
- package/dist/components/styles/pos.css +233 -0
- package/dist/index.js +5 -27
- package/dist/utils/enums.js +14 -2
- package/dist/utils/helpers.js +10 -1
- package/dist/utils/index.js +2 -27
- package/dist/utils/types.d.js +5 -0
- package/package.json +12 -3
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { useEffect, useRef, useState } from "react";
|
|
2
|
+
import { NZDollar } from "../utils";
|
|
3
|
+
import { Price } from "../utils/types";
|
|
4
|
+
import Flexi from "./Flexi";
|
|
5
|
+
|
|
6
|
+
export default function PriceButton({
|
|
7
|
+
price,
|
|
8
|
+
addToCart,
|
|
9
|
+
panMap,
|
|
10
|
+
}: {
|
|
11
|
+
price: Price;
|
|
12
|
+
addToCart: (amount: number) => void;
|
|
13
|
+
panMap: () => void;
|
|
14
|
+
}) {
|
|
15
|
+
const [flexiOpen, setFlexiOpen] = useState(false);
|
|
16
|
+
const flexiRef = useRef<HTMLDivElement | null>(null);
|
|
17
|
+
const focusRef = useRef<Element | null>(null);
|
|
18
|
+
|
|
19
|
+
const isFlexi = !!price.pMax && !!price.pMin && price.pMax > price.pMin;
|
|
20
|
+
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
if (flexiOpen) {
|
|
23
|
+
focusRef.current = flexiRef.current?.querySelectorAll(
|
|
24
|
+
`button, [href], input, select, textarea,
|
|
25
|
+
[tabindex]:not([tabindex="-1"]),video`
|
|
26
|
+
)[0];
|
|
27
|
+
|
|
28
|
+
(focusRef.current as HTMLElement)?.focus();
|
|
29
|
+
}
|
|
30
|
+
}, [flexiOpen]);
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<div className="priceage-container">
|
|
34
|
+
<button
|
|
35
|
+
className={isFlexi ? "priceage flexi" : "priceage"}
|
|
36
|
+
onClick={() => {
|
|
37
|
+
if (isFlexi) {
|
|
38
|
+
setFlexiOpen((prev) => !prev);
|
|
39
|
+
} else {
|
|
40
|
+
addToCart(price.p);
|
|
41
|
+
}
|
|
42
|
+
}}
|
|
43
|
+
disabled={price.is_available === false}
|
|
44
|
+
>
|
|
45
|
+
<span className="price">
|
|
46
|
+
<span className="price-name">
|
|
47
|
+
<span>{price.paName}</span>
|
|
48
|
+
{isFlexi && <span className="flexi-badge">FLEXi</span>}
|
|
49
|
+
</span>
|
|
50
|
+
<span className="amount">
|
|
51
|
+
{isFlexi
|
|
52
|
+
? `${NZDollar.format(price.pMin)} - ${NZDollar.format(
|
|
53
|
+
price.pMax
|
|
54
|
+
)}`
|
|
55
|
+
: NZDollar.format(price.p)}
|
|
56
|
+
</span>
|
|
57
|
+
</span>
|
|
58
|
+
{price.is_available === false && (
|
|
59
|
+
<span className="unavailable">Allocation exhausted</span>
|
|
60
|
+
)}
|
|
61
|
+
</button>
|
|
62
|
+
{isFlexi && (
|
|
63
|
+
<Flexi
|
|
64
|
+
price={price}
|
|
65
|
+
onClose={() => setFlexiOpen(false)}
|
|
66
|
+
onConfirm={addToCart}
|
|
67
|
+
buttonText="Add to cart"
|
|
68
|
+
isOpen={flexiOpen}
|
|
69
|
+
panMap={panMap}
|
|
70
|
+
/>
|
|
71
|
+
)}
|
|
72
|
+
</div>
|
|
73
|
+
);
|
|
74
|
+
}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { useState } from "react";
|
|
2
|
+
import { NZDollar } from "../utils";
|
|
3
|
+
import { Price, Seat } from "../utils/types";
|
|
4
|
+
import { closeIcon, flexiIcon } from "./assets/encodedSvgs";
|
|
5
|
+
import Flexi from "./Flexi";
|
|
6
|
+
|
|
7
|
+
interface SeatGroup {
|
|
8
|
+
seats: Seat[];
|
|
9
|
+
priceage?: Price;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const PriceSelect = ({
|
|
13
|
+
seatGroup,
|
|
14
|
+
seatPriceages,
|
|
15
|
+
updateSeatGroupPriceage,
|
|
16
|
+
}: {
|
|
17
|
+
seatGroup: SeatGroup;
|
|
18
|
+
seatPriceages: Price[];
|
|
19
|
+
updateSeatGroupPriceage: (price: Price) => void;
|
|
20
|
+
}) => {
|
|
21
|
+
const [flexiOpen, setFlexiOpen] = useState(false);
|
|
22
|
+
|
|
23
|
+
const isSelectedPriceFlexi =
|
|
24
|
+
!!seatGroup.priceage.pMax &&
|
|
25
|
+
!!seatGroup.priceage.pMin &&
|
|
26
|
+
seatGroup.priceage.pMax > seatGroup.priceage.pMin;
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<div className="price-select">
|
|
30
|
+
<select
|
|
31
|
+
onChange={(e) => {
|
|
32
|
+
const newPriceage = seatPriceages.find(
|
|
33
|
+
(p) => p.paId.toString() === e.target.value
|
|
34
|
+
);
|
|
35
|
+
updateSeatGroupPriceage(newPriceage);
|
|
36
|
+
}}
|
|
37
|
+
value={seatGroup.priceage.paId}
|
|
38
|
+
>
|
|
39
|
+
{seatPriceages.map((price) => (
|
|
40
|
+
<option
|
|
41
|
+
key={price.paId}
|
|
42
|
+
disabled={price.is_available === false}
|
|
43
|
+
value={price.paId}
|
|
44
|
+
>
|
|
45
|
+
{`${price.paName} ${NZDollar.format(
|
|
46
|
+
price.paId === seatGroup.priceage.paId
|
|
47
|
+
? seatGroup.priceage.p
|
|
48
|
+
: price.p
|
|
49
|
+
)}${price.is_available === false ? " - Allocation exhausted" : ""}`}
|
|
50
|
+
</option>
|
|
51
|
+
))}
|
|
52
|
+
</select>
|
|
53
|
+
{isSelectedPriceFlexi && (
|
|
54
|
+
<>
|
|
55
|
+
<button className="flexi-button" onClick={() => setFlexiOpen(true)}>
|
|
56
|
+
<img src={flexiIcon} height="18px" width="18px" />
|
|
57
|
+
<span>Edit FLEXi price</span>
|
|
58
|
+
</button>
|
|
59
|
+
{flexiOpen && (
|
|
60
|
+
<div className="flexi-popup" role="dialog" aria-modal="true">
|
|
61
|
+
<Flexi
|
|
62
|
+
price={seatGroup.priceage}
|
|
63
|
+
onClose={() => setFlexiOpen(false)}
|
|
64
|
+
onConfirm={(value) => {
|
|
65
|
+
const newPriceage = { ...seatGroup.priceage, p: value };
|
|
66
|
+
updateSeatGroupPriceage(newPriceage);
|
|
67
|
+
}}
|
|
68
|
+
buttonText="Confirm"
|
|
69
|
+
/>
|
|
70
|
+
</div>
|
|
71
|
+
)}
|
|
72
|
+
</>
|
|
73
|
+
)}
|
|
74
|
+
</div>
|
|
75
|
+
);
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
interface Props {
|
|
79
|
+
cancelPriceSelect: () => void;
|
|
80
|
+
groupedSelectedSeats: {
|
|
81
|
+
[key: number]: SeatGroup;
|
|
82
|
+
};
|
|
83
|
+
pricing: Price[];
|
|
84
|
+
priceSectionIds?: number[];
|
|
85
|
+
batchAddTicketsToCart: (seats: { seat: Seat; priceage: Price }[]) => void;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export default function PricingPopup({
|
|
89
|
+
cancelPriceSelect,
|
|
90
|
+
groupedSelectedSeats,
|
|
91
|
+
pricing,
|
|
92
|
+
priceSectionIds,
|
|
93
|
+
batchAddTicketsToCart,
|
|
94
|
+
}: Props) {
|
|
95
|
+
const [seatsToBook, setSeatsToBook] = useState(groupedSelectedSeats);
|
|
96
|
+
|
|
97
|
+
return (
|
|
98
|
+
<>
|
|
99
|
+
<div className="pricing-popup-backdrop" onClick={cancelPriceSelect} />
|
|
100
|
+
<div className="pricing-popup" role="dialog" aria-modal="true">
|
|
101
|
+
<div className="popup-header">
|
|
102
|
+
<h3>Choose prices</h3>
|
|
103
|
+
<button
|
|
104
|
+
className="close-button"
|
|
105
|
+
aria-label="close popup"
|
|
106
|
+
onClick={cancelPriceSelect}
|
|
107
|
+
>
|
|
108
|
+
<img src={closeIcon} alt="close" height="20px" width="20px" />
|
|
109
|
+
</button>
|
|
110
|
+
</div>
|
|
111
|
+
<div className="seat-list">
|
|
112
|
+
{Object.entries(seatsToBook).map(([id, seatGroup]) => {
|
|
113
|
+
const seatPriceages = pricing.filter(
|
|
114
|
+
(price) =>
|
|
115
|
+
(!priceSectionIds || priceSectionIds.includes(price.psId)) &&
|
|
116
|
+
price.psId === parseInt(id)
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
return (
|
|
120
|
+
<div key={id} className="seat-group">
|
|
121
|
+
<div className="seat-group-header">
|
|
122
|
+
<h4>{seatGroup.seats[0].psName}</h4>
|
|
123
|
+
<p>{`${seatGroup.seats.length} seat${
|
|
124
|
+
seatGroup.seats.length !== 1 ? "s" : ""
|
|
125
|
+
}`}</p>
|
|
126
|
+
</div>
|
|
127
|
+
<p className="seat-names">
|
|
128
|
+
{seatGroup.seats
|
|
129
|
+
.map((s) => `${s.r}-${s.c}`)
|
|
130
|
+
.sort()
|
|
131
|
+
.join(", ")}
|
|
132
|
+
</p>
|
|
133
|
+
{seatPriceages.filter((price) => price.is_available !== false)
|
|
134
|
+
.length > 0 ? (
|
|
135
|
+
<PriceSelect
|
|
136
|
+
seatGroup={seatGroup}
|
|
137
|
+
seatPriceages={seatPriceages}
|
|
138
|
+
updateSeatGroupPriceage={(newPriceage) => {
|
|
139
|
+
const newSeats = { ...seatsToBook };
|
|
140
|
+
newSeats[id].priceage = newPriceage;
|
|
141
|
+
setSeatsToBook(newSeats);
|
|
142
|
+
}}
|
|
143
|
+
/>
|
|
144
|
+
) : (
|
|
145
|
+
<p className="no-prices-available">
|
|
146
|
+
Oops, looks like there are no prices available for this
|
|
147
|
+
section.
|
|
148
|
+
</p>
|
|
149
|
+
)}
|
|
150
|
+
</div>
|
|
151
|
+
);
|
|
152
|
+
})}
|
|
153
|
+
</div>
|
|
154
|
+
<div className="buttons">
|
|
155
|
+
<button className="cancel" onClick={cancelPriceSelect}>
|
|
156
|
+
Cancel
|
|
157
|
+
</button>
|
|
158
|
+
<button
|
|
159
|
+
className="add-cart"
|
|
160
|
+
onClick={() => {
|
|
161
|
+
batchAddTicketsToCart(
|
|
162
|
+
Object.values(seatsToBook)
|
|
163
|
+
.filter((v) => !!v.priceage)
|
|
164
|
+
.flatMap((s) =>
|
|
165
|
+
s.seats.map((seat) => ({ seat, priceage: s.priceage }))
|
|
166
|
+
)
|
|
167
|
+
);
|
|
168
|
+
cancelPriceSelect();
|
|
169
|
+
}}
|
|
170
|
+
disabled={Object.values(seatsToBook).every((v) => !v.priceage)}
|
|
171
|
+
>
|
|
172
|
+
Add to cart
|
|
173
|
+
</button>
|
|
174
|
+
</div>
|
|
175
|
+
</div>
|
|
176
|
+
</>
|
|
177
|
+
);
|
|
178
|
+
}
|