diff --git a/client/src/inject.ts b/client/src/inject.ts index 3555af4..614c762 100644 --- a/client/src/inject.ts +++ b/client/src/inject.ts @@ -44,7 +44,6 @@ function messagesHandler(e: any) { break; } case "child_needCheckoutResponse": { - // const inSelectedSeat: I.JSCSelectedSeat = data.message.selectedSeat; const inUrl: string = data.message.url; axios.get(inUrl, { @@ -66,6 +65,7 @@ function messagesHandler(e: any) { message: { "isValidSeatSelection": isValidSeatSelection, "parsedHTML": parsedHTML, + "url": inUrl, // "selectedSeat": inSelectedSeat }, from: "parent", @@ -80,6 +80,19 @@ function messagesHandler(e: any) { }); break; } + case "child_hide_dialog_titlebar": { + jQuery(".ui-dialog-titlebar").hide(); + break; + } + case "child_show_dialog_titlebar": { + jQuery(".ui-dialog-titlebar").show(); + break; + } + case "child_click_checkout": { + const inUrl: string = data.message.url; + window.location.href = inUrl; + break; + } default: break; diff --git a/client/src/modules/config.ts b/client/src/modules/config.ts index 19dc31a..e292b09 100644 --- a/client/src/modules/config.ts +++ b/client/src/modules/config.ts @@ -14,5 +14,6 @@ export const config: I.Config = { urlCSSJSCStaging: "https://staging.tickets.zinomedia.de/libs/jQuery-Seat-Charts/jquery.seat-charts.css", urlCSSJSCMaster: "https://tickets.zinomedia.de/libs/jQuery-Seat-Charts/jquery.seat-charts.css", urlCSSjQueryUI: "https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css", - childHasVenueXML: false + childHasVenueXML: false, + maxSelectedSeats: 10 } \ No newline at end of file diff --git a/client/src/modules/jsc.ts b/client/src/modules/jsc.ts index 6cdfb83..c9c46a7 100644 --- a/client/src/modules/jsc.ts +++ b/client/src/modules/jsc.ts @@ -1,4 +1,5 @@ import * as I from "../types/types"; +import { state } from "../seatmap"; export function getSeats(inXML: any): I.JSCSeats { const pricescaleArr: I.SeatmapPricescale[] = inXML.seatmap[0].pricescale_config[0].pricescale; @@ -29,13 +30,84 @@ export function activateSeatsBySectionID(inXML: any, seatmap: any, inValue: stri }); } -export function getRows(inXML: any): number[] { +export function getRowsNaming(inXML: any): string[] { const layout: I.SeatmapLayout = inXML.seatmap[0].layouts[0].layout[0]; const height: number = parseInt(layout.height[0]); + // return Array.from({ length: height }, (_, i: number) => i + 1); + const namingArr = Array.from({ length: height }, (_) => ""); + const rowsArr = layout.rows[0].row; - return Array.from({ length: height }, (_, i: number) => i + 1); + rowsArr.forEach(element => { + const index: number = parseInt(element.y_cell_coord[0]); + const seatsArr: string[] = element.seats[0].split("|"); + const seatArr: string[] = splitSeatStr(seatsArr[0]); + const row = seatArr[4]; + namingArr[index] = row; + }); + + return namingArr; } +// export function getColumnNaming(inXML: any): string[] { +// const layout: I.SeatmapLayout = inXML.seatmap[0].layouts[0].layout[0]; + + + +// const width: number = parseInt(layout.width[0]); + + +// const namingArr = Array.from({ length: width }, (_) => ""); +// const rowsArr = layout.rows[0].row; + +// // let seats: number[] = []; +// // let x: number[] = []; +// rowsArr.forEach(element => { +// // const index: number = parseInt(element.y_cell_coord[0]); +// const seatsArr: string[] = element.seats[0].split("|"); + +// seatsArr.forEach(seatStr => { +// const seatArr: string[] = splitSeatStr(seatStr); +// // seats.push(parseInt(seatArr[5])); +// // x.push(parseInt(seatArr[2])); + +// const x = parseInt(seatArr[2]); + +// // Form: +// // 0: "568528" -> ID +// // 1: "568528" -> ID +// // 2: "21" -> X-Coord benutzen +// // 3: "7024" -> section ID +// // 4: "13" -> Reihenbezeichnung +// // 5: "4" -> Platznummer +// namingArr[x] = seatArr[5]; + + +// }); + +// // const row = seatArr[4]; +// // namingArr[index] = row; +// }); + +// // console.log(seats); +// // console.log(x); +// // console.log(Math.min.apply(Math, seats)); +// // console.log(Math.max.apply(Math, seats)); +// // console.log(Math.min.apply(Math, x)); +// // console.log(Math.max.apply(Math, x)); + +// // const minimum = Math.min.apply(Math, seats); +// // let i = 0; +// // for (let index = Math.min.apply(Math, x); index < Math.max.apply(Math, x); index++) { +// // // const element = array[index]; + +// // namingArr[index] = (minimum + i).toString(); +// // i++; +// // } + +// console.log(namingArr); +// return namingArr; +// } + export function generateMap(inXML: any): string[] { const layout: I.SeatmapLayout = inXML.seatmap[0].layouts[0].layout[0]; const rows: I.LayoutRow2[] = layout.rows[0].row; @@ -58,11 +130,12 @@ export function generateLegend(inVenueXML: I.VenueXML, inSeatmapXML: any, inNode items: [] } + // todo: dynamic function to get a pricescale property by id for (let key in pricescaleArr) { const id: string = pricescaleArr[key].id[0]; const seatsKey: string = String.fromCharCode(97 + parseInt(key)).toLocaleUpperCase(); - // get pricescale desc from venueXML + // get pricescale desc from venueXML const desc = venuePricescaleArr.find(obj => { const pricecaleID = obj.id[0]; return pricecaleID === id; @@ -118,7 +191,7 @@ function enterSeatsInMatrix(inRows: I.LayoutRow2[], inArrMatrix: string[][], inP // 2: "21" -> X-Coord benutzen // 3: "7024" -> section ID // 4: "13" -> Reihenbezeichnung - // 5: "4" -> ? + // 5: "4" -> Platznummer // skip blacked out seats if (!availableArr.includes(seatArr[0]) && !unavailableArr.includes(seatArr[0])) @@ -130,6 +203,9 @@ function enterSeatsInMatrix(inRows: I.LayoutRow2[], inArrMatrix: string[][], inP if (seatsKey) inArrMatrix[Y][X] = generateSeatStr(seatsKey, seatArr); + + // save seatArr in state with seatID as key + state.layoutRows[seatArr[0]] = seatArr; }); }); diff --git a/client/src/seatmap.ts b/client/src/seatmap.ts index 2355342..2d37231 100644 --- a/client/src/seatmap.ts +++ b/client/src/seatmap.ts @@ -15,10 +15,20 @@ let seatmap: any; let panzoom: PanzoomObject | undefined; let inVenueXML: I.VenueXML; let seatmapXML: any; -let state: I.State = { +export let state: I.State = { priceOverall: "", + cartChanged: false, selectedSeatsArr: [], - selectedSeatsObj: {} + selectedSeatsObj: {}, + layoutRows: {} +} + +function getTrimCoord(arr: I.Trim) { + let [xTrim, yTrim] = arr.coord[0].split(",").map(Number); + xTrim -= 20; + yTrim -= 21; + + return [xTrim, yTrim]; } function messagesHandler(inE: any) { @@ -68,27 +78,55 @@ function messagesHandler(inE: any) { case "parent_sendSeatmapXML": { seatmapXML = data.message.map_response; const map: string[] = JSC.generateMap(seatmapXML); - const rows: number[] = JSC.getRows(seatmapXML); + const rowsNaming: string[] = JSC.getRowsNaming(seatmapXML); + // const columnNaming: string[] = JSC.getColumnNaming(seatmapXML); const seats: I.JSCSeats = JSC.getSeats(seatmapXML); const legend: I.JSCLegend = JSC.generateLegend(inVenueXML, seatmapXML, "#JSCLegendInner"); - addSeatmap("#containerSeatmapInner", map, rows, seats, legend); + addSeatmap("#containerSeatmapInner", map, rowsNaming, seats, legend); JSC.setUnavailableSeats(seatmapXML, seatmap); selectSeatsInCart(); UI.convertLegendToDropdown("dropdownLegend"); dropdownLegendOnChange("#dropdownLegend"); + // jQuery("#containerSeatmapInner").append('
foobar
'); + + + processTrims(); + panzoom = UI.addPanzoom("#containerSeatmapInner", ".panzoomZoomIn", ".panzoomZoomOut", "#panzoomResetZoom"); UI.controlLoftloader("hide"); + + + new jBox("Tooltip", { + attach: jQuery(".seatCharts-seat"), + // title: "title", + // content: "foooobar", + onOpen: function (this: any) { + showSeatTooltip(this); + }, + }); + + break; } case "parent_sendCheckoutResponse": { + console.log(data.message); const inIsValidSeatSelection: boolean = data.message.isValidSeatSelection; - if (!inIsValidSeatSelection) { + if (!inIsValidSeatSelection) showJBoxNotice(`Auswahl nicht möglich: Bitte lassen Sie keinen einzelnen Platz frei.`); + else { + removeCartItems(); + generateCartItems(); + const url = generateCheckoutUrl(); + setCheckoutBtn(url); + showModalCart(); + console.log(state); } + state.cartChanged = false; + break; } @@ -97,10 +135,294 @@ function messagesHandler(inE: any) { } } +function processTrims() { + // Create sum of the height of every .seatCharts-row to get total height of seatmap + const heightTotal = calcHeightTotal(); + + // Height of a single .seatCharts-row + const heightPerRow = heightTotal / jQuery(".seatCharts-row").length; + + // Maximum width of one seatCharts-row is the width we need + // Attention: Maximum width of #containerSeatmapInner is bigger than seatCharts-row, therefore use width of seatCharts-row + const totalWidth = jQuery(".seatCharts-row")[0].getBoundingClientRect().width; + const widthPerSeat = totalWidth / jQuery(".seatCharts-row")[0].childElementCount; + + // const containerSeatmapInnerRect: DOMRect = jQuery("#containerSeatmapInner")[0].getBoundingClientRect(); + // console.log(containerSeatmapInnerRect); + + // const viewWidthQuotient = 100 / containerSeatmapInnerRect.width; + + // Width: Calculate quotient to convert pixel (px) to viewwidth (vw) later + // 1px = (100vw / [document.documentElement.clientWidth] px) + // e.g. — If your viewport was 500px wide (equals by definition to 100vw) then + // 1px = (100vw / 500px) = 0.2vw + const viewWidthQuotient = 100 / totalWidth; + + // const widthQuotient = widthTotal/1580; + // const widthQuotient = containerSeatmapInnerRect.width / 1580; + + // Width: Quotient is later used to convert PVO trim x coord to absolute x-coord in browser + // Max-width of trim coord system is 1600 with offset of 20 + // 1600 - 20 = 1580 + const widthQuotient = totalWidth / 1580; + + // Height: Quotient is later used to convert PVO trim y coord to absolute y-coord in browser + // Max-height of trim coord system is 425 with offset of 21 + // 425 - 20 = 405 + const heightQuotient = heightTotal / 405; + + console.log(`heightTotal: ${heightTotal} heightPerRow: ${heightPerRow} heightQuotient: ${heightQuotient} totalWidth: ${totalWidth} widthPerSeat: ${widthPerSeat} widthQuotient: ${widthQuotient}`); + + const trimArr: I.Trim[] = seatmapXML.seatmap[0].trims[0].trim; + trimArr.forEach(arr => { + const text: string = getTrimText(arr); + const [xTrim, yTrim] = getTrimCoord(arr); + const [leftVw, topPx] = calcTrimPos(xTrim, yTrim, heightPerRow, widthQuotient, heightQuotient, viewWidthQuotient, widthPerSeat); + + console.log(`${text} -> x: ${xTrim} y: ${yTrim} -> x: ${leftVw} y: ${topPx}px`); + + createTrim(leftVw, topPx, text); + }); +} + +function calcHeightTotal(): number { + let heightTotal = 0; + jQuery(".seatCharts-row").each(function () { + const domRect: DOMRect = this.getBoundingClientRect(); + console.log(domRect); + heightTotal += domRect.height; + }); + + return heightTotal; +} + +function createTrim(inLeft: number, inTop: number, inText: string) { + jQuery("#containerSeatmapInner").append(` +
+ ${inText} +
`); +} + +function calcTrimPos(xTrim: number, yTrim: number, heightPerRow: number, widthQuotient: number, heightQuotient: number, viewWidthQuotient: number, widthPerSeat: number) { + let leftPx = Math.round(xTrim * widthQuotient); + // const leftVw = parseInt((leftPx * viewWidthQuotient).toFixed(3)); + console.log(viewWidthQuotient); + + let topPx = Math.round(yTrim * heightQuotient); + + // if (topPx > heightPerRow) + // topPx -= heightPerRow; + console.log(heightPerRow); + + // if (leftPx > widthPerSeat) + // leftPx -= widthPerSeat; + console.log(widthPerSeat); + + + // return [leftVw, topPx]; + return [leftPx, topPx]; +} + +function getTrimText(arr: I.Trim): string { + const textArr: string[] = arr.text[0].split(";").filter(Boolean); + + return textArr.map(element => { + const charCode = element.replace(/^\&\#/, "0"); + return String.fromCharCode(parseInt(charCode, 16)); + }).join(""); +} + +function showSeatTooltip(jBox: any): void { + const seatID: string = jBox.source[0].id; + const seat = state.layoutRows[seatID][5]; + const row = state.layoutRows[seatID][4]; + const sectionID = state.layoutRows[seatID][3]; + const sectionDesc = getSectionDescBySectionID(inVenueXML, sectionID); + const tooltipContent = `${sectionDesc}
Reihe ${row} Platz ${seat}`; + + jBox.setContent(tooltipContent); +} + +function getSectionDescBySectionID(inVenueXML: I.VenueXML, sectionID: string): string | undefined { + const sectionArr = inVenueXML.master_config[0].section_config[0].section; + + const sectionDesc = sectionArr.find(arr => { + return sectionID === arr.id[0]; + })?.desc[0]; + + return sectionDesc; +} + +function setCheckoutBtn(inUrl: string | undefined) { + if (!inUrl) + return; + + const btnCheckout = jQuery("#modalCart-overlay #checkout .fl-button").get(0); + if (btnCheckout) { + btnCheckout.addEventListener("click", function () { + const message: I.Message = { + message: { + url: inUrl, + }, + from: "child", + event: "child_click_checkout", + date: Date.now() + }; + Communication.sendMessage(message, "parent"); + }); + } +} + +function showModalCart() { + jQuery("#modalCart-overlay").fadeIn(300); + Communication.sendEventToParent("child_hide_dialog_titlebar"); +} + +function getVenuePricescalePropertyByPricescaleID(property: I.Pricescale2Properties, pricescaleID: string, inVenueXML: I.VenueXML) { + const venuePricescaleArr: I.Pricescale2[] = inVenueXML.venue[0].pricescales[0].pricescale; + + return venuePricescaleArr.find(obj => { + if (obj.id[0] === pricescaleID) + return obj; + + return undefined; + })?.[property][0]; +} + +function addCartItem(inSeatObj: I.JSCSelectedSeat) { + const color = `#${getVenuePricescalePropertyByPricescaleID("color", inSeatObj.data.seatsObj.id[0], inVenueXML)}`; + const category = getVenuePricescalePropertyByPricescaleID("desc", inSeatObj.data.seatsObj.id[0], inVenueXML); + const seat = state.layoutRows[inSeatObj.id][5]; + const row = state.layoutRows[inSeatObj.id][4]; + const sectionID = state.layoutRows[inSeatObj.id][3]; + const sectionDesc = getSectionDescBySectionID(inVenueXML, sectionID); + const seatStr = `${sectionDesc}
Reihe ${row} Platz ${seat}`; + const buyerTypes: (string | undefined)[][] | undefined = getBuyerTypesByPricescaleID(inSeatObj.data.seatsObj.id[0]); + const cartId = `cartItem-${inSeatObj.id}`; + const dropdownBuyerTypesSelector = `#${cartId} .dropdownBuyerTypes`; + + // const dropdownCrossSelector = `#${cartId} .cartItemCross`; + + // jQuery("#cartItemHTML .fl-html").append(` + //
+ //
X
+ //
+ //
${category}
+ //
${seat}
+ // + //
`); + + jQuery("#cartItemHTML .fl-html").append(` +
+
+
${category}
+
${seatStr}
+ +
`); + + fillDropdownBuyerTypes(buyerTypes, dropdownBuyerTypesSelector); + + // todo: shorter version? + jQuery(dropdownBuyerTypesSelector).on('change', function (this: HTMLSelectElement) { + changedDropdownBuyerType(this, inSeatObj); + }); + + // jQuery(dropdownCrossSelector).on("click", function () { + // console.log("cross clicked"); + // removeSeatFromState(inSeatObj); + // calcOverallPrice(inVenueXML); + // setBtnCartText(); + // }); +} + +function changedDropdownBuyerType(inSelect: HTMLSelectElement, inSeatObj: I.JSCSelectedSeat) { + const index = state.selectedSeatsArr.findIndex(arr => { + return arr[0] === inSeatObj.id; + }); + + + + state.selectedSeatsArr[index][1] = inSelect.value; + + const buyerTypeCode = getBuyerTypeCodeByBuyerTypeID(inVenueXML, inSelect.value); + + if (buyerTypeCode) + state.selectedSeatsArr[index][2] = buyerTypeCode; + + calcOverallPrice(inVenueXML); + setBtnCartText(); + + const url = generateCheckoutUrl(); + console.log(url); + setCheckoutBtn(url); + + + console.log(state); +} + +// todo: generalize dropdown fill options +function fillDropdownBuyerTypes(inBuyerTypes: (string | undefined)[][] | undefined, inSelector: string) { + if (!inBuyerTypes) + return; + + const dropdownBuyerTypes = jQuery(inSelector).get(0); + console.log(dropdownBuyerTypes); + console.log(inBuyerTypes); + + inBuyerTypes.forEach(arr => { + if (!arr[0]) + return; + + let opt = document.createElement('option'); + opt.value = arr[0]; + opt.innerHTML = `${arr[2]} €${arr[1]}`; + dropdownBuyerTypes.appendChild(opt); + }); +} + +function getBuyerTypesByPricescaleID(inPricescaleID: string) { + const venuePricescaleArr = inVenueXML.price_structure[0].pricescale; + const buyerTypesArr = venuePricescaleArr.find(obj => { + return obj.id[0] === inPricescaleID; + })?.buyer_type; + + if (!buyerTypesArr) + return; + + const buyerTypes: (string | undefined)[][] = buyerTypesArr.map(arr => { + const buyerTypeDesc = inVenueXML.venue[0].buyer_types[0].buyer_type.find(obj => { + return obj.id[0] === arr.id[0] ? obj.desc[0] : undefined; + })?.desc[0]; + + return [arr.id[0], arr.price[0], buyerTypeDesc]; + }); + + return buyerTypes; +} + +function removeCartItems() { + jQuery("#cartItemHTML .cartItem").each(function () { + this.remove(); + }); +} + +function generateCartItems() { + if (!state.selectedSeatsArr.length) + return; + + for (const key in state.selectedSeatsObj) { + if (Object.prototype.hasOwnProperty.call(state.selectedSeatsObj, key)) { + const element = state.selectedSeatsObj[key]; + addCartItem(element); + console.log(element); + } + } +} + function selectSeatsInCart() { state.selectedSeatsArr.forEach(arr => { const seatID: string = arr[0]; - + if (seatmap.get(seatID)) seatmap.status(seatID, "selected"); }); @@ -108,8 +430,8 @@ function selectSeatsInCart() { function showJBoxNotice(inContent: string, inAutoClose: number | boolean | undefined = 5000) { new jBox('Notice', { - position: {x: 'center', y: 'top'}, - offset: {x: 0, y: 320}, + position: { x: 'center', y: 'top' }, + offset: { x: 0, y: 320 }, content: inContent, autoClose: inAutoClose, animation: { open: "zoomIn", close: "zoomOut" }, @@ -147,6 +469,28 @@ function generatePricescaleCSS(inVenueXML: I.VenueXML): string { return (cssArr.join("\r\n")); } +function initModalCart() { + jQuery("#modalCart-overlay").hide(); + + const btnClose = jQuery("#modalCart-overlay .uabb-close-icon").get(0); + const btnBack = jQuery("#modalCart-overlay #goBack .fl-button").get(0); + + + // const btnClose = jQuery("#modalCart-overlay .uabb-close-icon").get(0); + + if (btnClose) { + btnClose.addEventListener("click", function () { + Communication.sendEventToParent("child_show_dialog_titlebar"); + }); + } + + if (btnBack) { + btnBack.addEventListener("click", function () { + jQuery("#modalCart-overlay .uabb-close-icon").trigger("click"); + }); + } +} + window.addEventListener('load', function () { Utils.inject(config.urlJSCStaging, "js", "head"); Utils.inject(config.urlCSSJSCStaging, "css", "body"); @@ -155,11 +499,20 @@ window.addEventListener('load', function () { Communication.listenToMessages(messagesHandler); Utils.waitForSeatmap(Communication.showBookingBtnParent); - const btnCart: HTMLElement | null = document.getElementById("btnCart"); + Utils.waitForElement("#modalCart-overlay", initModalCart); + // Utils.waitForElement("#modalCart-overlay", () => jQuery("#modalCart-overlay").hide()); + + + const btnCart = jQuery("#modalCart .uabb-button").get(0); if (btnCart) { btnCart.addEventListener("click", function () { - console.log("foo"); - isValidSeatSelection(); + console.log(state.selectedSeatsArr.length); + + if (!state.selectedSeatsArr.length) { + showJBoxNotice(`Sie haben bislang keinen Platz ausgewählt.`); + return; + } + state.cartChanged ? isValidSeatSelection() : showModalCart(); }); } @@ -174,29 +527,6 @@ window.addEventListener('load', function () { } }); -// function showNotification(duration: number) { -// jQuery("#notificationColumn").css({ opacity: 0.0, visibility: "visible" }).animate({ opacity: 1 }); -// jQuery("#notificationColumn").promise().done(function () { -// console.log("done showing"); -// setTimeout(() => { -// hideNotification(); -// }, duration); -// }); -// } - -// function hideNotification() { -// jQuery("#notificationColumn").css({ opacity: 1.0, visibility: "visible" }).animate({ opacity: 0 }); -// jQuery("#notificationColumn").promise().done(function () { -// console.log("done hiding"); -// }); -// } - -// function displayNotification(inContent: string, inDuration: number = 5000) { -// jQuery("#notificationHeading .fl-heading-text")[0].innerText = inContent; -// showNotification(inDuration); -// } - - function dropdownLegendOnChange(inSelector: string) { const dropdownLegend = jQuery(inSelector).get(0); dropdownLegend.addEventListener("change", function (this: HTMLSelectElement) { @@ -241,7 +571,7 @@ function addSeatToState(inVenueXML: I.VenueXML, inSelectedSeat: I.JSCSelectedSea state.selectedSeatsObj = { ...state.selectedSeatsObj, ...seatObj }; const pricescaleID: string = state.selectedSeatsObj[seatID].data.seatsObj.id[0]; - const pricescaleObj: I.Pricescale5 | undefined = getVenuePriceStructurePricescaleObjByPricescaleID(inVenueXML, pricescaleID); + const pricescaleObj: I.Pricescale5 | undefined = getVenuePriceStructurePropertyByPricescaleID(inVenueXML, pricescaleID); console.log(pricescaleObj); if (!pricescaleObj) { @@ -251,14 +581,33 @@ function addSeatToState(inVenueXML: I.VenueXML, inSelectedSeat: I.JSCSelectedSea // get id and code of first buyer type const firstBuyerTypeID: string = pricescaleObj.buyer_type[0].id[0]; - const firstPriceStructureCode: string = inVenueXML.price_structure[0].code[0]; // todo: code of first price_structure always correct? what about multiple schablonen? + // const firstPriceStructureCode: string = inVenueXML.price_structure[0].code[0]; // todo: code of first price_structure always correct? what about multiple schablonen? + const buyerTypeCode: string | undefined = getBuyerTypeCodeByBuyerTypeID(inVenueXML, firstBuyerTypeID); - state.selectedSeatsArr.push([seatID, firstBuyerTypeID, firstPriceStructureCode]); + if (buyerTypeCode) { + state.selectedSeatsArr.push([seatID, firstBuyerTypeID, buyerTypeCode]); + state.cartChanged = true; + } +} + +function getBuyerTypeCodeByBuyerTypeID(inVenueXML: I.VenueXML, inBuyerTypeID: string): string | undefined { + const venueBuyerTypeArr = inVenueXML.venue[0].buyer_types[0].buyer_type; + return venueBuyerTypeArr.find(arr => { + return inBuyerTypeID === arr.id[0]; + })?.code[0]; } function isValidSeatSelection() { - const selectedSeatIndexes: string = generateSelectedSeatIndexes(); - const url: string = `${inputsWithValue["ticketPurchaseUrl"]}?user_context=${inputsWithValue.user_context}&pid=${inputsWithValue["pid"]}&selected_seat_indexes=${selectedSeatIndexes}&trxstate=148`; + jQuery("#modalCart-overlay").hide(); + + if (!state.selectedSeatsArr.length) + return; + + const url = generateCheckoutUrl(); + + // const selectedSeatIndexes: string = generateSelectedSeatIndexes(); + // const url: string = `${inputsWithValue["ticketPurchaseUrl"]}?user_context=${inputsWithValue.user_context}&pid=${inputsWithValue["pid"]}&selected_seat_indexes=${selectedSeatIndexes}&trxstate=148`; + const message: I.Message = { message: { url: url, @@ -270,13 +619,22 @@ function isValidSeatSelection() { Communication.sendMessage(message, "parent"); } +function generateCheckoutUrl(): string | undefined { + if (!state.selectedSeatsArr.length) + return; + else { + const selectedSeatIndexes: string = generateSelectedSeatIndexes(); + return `${inputsWithValue["ticketPurchaseUrl"]}?user_context=${inputsWithValue.user_context}&pid=${inputsWithValue["pid"]}&selected_seat_indexes=${selectedSeatIndexes}&trxstate=148`; + } +} + function generateSelectedSeatIndexes(): string { return (state.selectedSeatsArr.map(function (arr) { return arr.join(","); })).join("|"); } -function getVenuePriceStructurePricescaleObjByPricescaleID(inVenueXML: I.VenueXML, inID: string): I.Pricescale5 | undefined { +function getVenuePriceStructurePropertyByPricescaleID(inVenueXML: I.VenueXML, inID: string): I.Pricescale5 | undefined { const venuePricescaleArr: I.Pricescale5[] = inVenueXML.price_structure[0].pricescale; return venuePricescaleArr.find(obj => { return obj.id[0] === inID; @@ -290,23 +648,26 @@ function removeSeatFromState(inSelectedSeat: I.JSCSelectedSeat) { return arr[0] === inSelectedSeat.id; }); state.selectedSeatsArr.splice(index, 1); + state.cartChanged = true; + + if (!state.selectedSeatsArr.length) + jQuery("#modalCart-overlay").hide(); } -function calcOverallPrice(inVenueXML: I.VenueXML): string | undefined { - if (!state.selectedSeatsArr.length) - return; - +function calcOverallPrice(inVenueXML: I.VenueXML): string | undefined { + if (!state.selectedSeatsArr.length) { + state.priceOverall = "0"; + return "0"; + } + let overallPrice: number = 0; state.selectedSeatsArr.forEach(arr => { const seatID: string = arr[0]; const buyertypeID: string = arr[1]; - - // const selectedSeat: I.JSCSelectedSeat = seatmap.get(seatID).settings; const selectedSeat: I.JSCSelectedSeat = state.selectedSeatsObj[seatID]; - const pricescaleID: string = selectedSeat.data.seatsObj.id[0]; - const pricescaleObj: I.Pricescale5 | undefined = getVenuePriceStructurePricescaleObjByPricescaleID(inVenueXML, pricescaleID); + const pricescaleObj: I.Pricescale5 | undefined = getVenuePriceStructurePropertyByPricescaleID(inVenueXML, pricescaleID); if (!pricescaleObj) return; @@ -319,7 +680,6 @@ function calcOverallPrice(inVenueXML: I.VenueXML): string | undefined { overallPrice += seatPrice; }); - state.priceOverall = overallPrice.toFixed(2); return state.priceOverall; @@ -332,25 +692,42 @@ function getPriceByBuyertypeID(inBuyertypeID: string, inPricescaleObj: I.Pricesc if (price) return parseFloat(price); - else - return undefined; + + return undefined; } function setBtnCartText() { const numTickets = state.selectedSeatsArr.length; let text: string = ""; + let textModal: string = ""; console.log(numTickets); - if (state.priceOverall !== "") + if (state.priceOverall !== "") { numTickets === 1 ? text = `${numTickets} Ticket für €${state.priceOverall}` : text = `${numTickets} Tickets für €${state.priceOverall}`; - else + textModal = `Summe (${numTickets} Plätze) €${state.priceOverall}`; + } + else { text = "0 Tickets für €0.00"; + textModal = `Summe (0 Plätze) €0,00`; + } + + jQuery("#modalCart .uabb-button-text")[0].innerText = text; + jQuery("#modalCartSum .uabb-heading-text")[0].textContent = textModal; - jQuery("#btnCart .fl-button-text")[0].innerText = text; } -function addSeatmap(inSelector: string, inMap: string[], inRows: number[], inSeats: I.JSCSeats, inLegend: I.JSCLegend): void { +function maximumSelectedSeatsReached(inSeatObj: I.JSCSelectedSeat): boolean { + if (state.selectedSeatsArr.length >= config.maxSelectedSeats) { + showJBoxNotice(`Sie können maximal ${config.maxSelectedSeats} Plätze auswählen.`); + seatmap.status(inSeatObj.id, "available"); + return true; + } + + return false; +} + +function addSeatmap(inSelector: string, inMap: string[], inRowsNaming: string[], inSeats: I.JSCSeats, inLegend: I.JSCLegend): void { // console.log(inSeatmapInitMap); // console.log(inSeats); @@ -361,8 +738,8 @@ function addSeatmap(inSelector: string, inMap: string[], inRows: number[], inSea seatmap = containerSeatmap.seatCharts({ naming: { top: false, - left: true, - rows: inRows, + left: false, + rows: inRowsNaming, }, map: inMap, // map: [ @@ -389,30 +766,32 @@ function addSeatmap(inSelector: string, inMap: string[], inRows: number[], inSea console.log("seat selected"); console.log(selectedSeat); + if (maximumSelectedSeatsReached(selectedSeat)) + return "available"; + addSeatToState(inVenueXML, selectedSeat); calcOverallPrice(inVenueXML); setBtnCartText(); console.log(state.selectedSeatsArr); - return 'selected'; + return "selected"; } - else if (this.status() == 'selected') { + else if (this.status() === "selected") { const selectedSeat: I.JSCSelectedSeat = this.settings; console.log("seat unselected"); removeSeatFromState(selectedSeat); - calcOverallPrice(inVenueXML); setBtnCartText(); console.log(state.selectedSeatsArr); - return 'available'; + return "available"; } else if (this.status() == 'unavailable') { console.log("unavailable"); - return 'unavailable'; + return "unavailable"; } else { return this.style(); diff --git a/client/src/types/types.d.ts b/client/src/types/types.d.ts index 3d0c086..eedbc36 100644 --- a/client/src/types/types.d.ts +++ b/client/src/types/types.d.ts @@ -1,7 +1,24 @@ +export interface Trim { + coord: string[]; + enabled: string[]; + id: string[]; + image_path: string[]; + metadata: string[]; + text: string[]; + type: string[]; + target_seat_map_id: string[]; +} + export interface State { priceOverall: string; + cartChanged: boolean; selectedSeatsArr: string[][]; - selectedSeatsObj: StateJSCSelectedSeats + selectedSeatsObj: StateJSCSelectedSeats; + layoutRows: layoutRows; +} + +interface layoutRows { + [key: string]: string[] } export interface StateJSCSelectedSeats { @@ -70,6 +87,7 @@ export interface Config { urlCSSParentMaster: string; childHasVenueXML: boolean; urlCSSjQueryUI: string; + maxSelectedSeats: number; } export interface Message { @@ -128,6 +146,8 @@ export interface Event { timestamp: string[]; } +export type Pricescale2Properties = "code" | "color" | "desc" | "id" | "pricescale_group_id" | "text_color"; + export interface Pricescale2 { code: string[]; color: string[];