class TicketShop extends HTMLElement {
constructor() {
super();
// Create a shadow root and put the content inside it
const shadowRoot = this.attachShadow({mode: 'open'});
// Define the homepage
const isHomepage = () => {
return !this.getAttribute('name') ? false : true;
}
// Create styles
this.styles = document.createElement('style');
this.styles.textContent = `
.lds-dual-ring {
width: 80px;
height: 80px;
margin: 100px auto;
}
.lds-dual-ring:after {
content: " ";
display: block;
width: 64px;
height: 64px;
margin: 8px;
border-radius: 50%;
border: 6px solid #000;
border-color: #000 transparent #000 transparent;
animation: lds-dual-ring 1.2s linear infinite;
}
@keyframes lds-dual-ring {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
:host section {
font-family: 'Montserrat';
position: relative;
text-align: center;
}
:host ul.tickets-list {
width: fit-content;
list-style: none;
margin: 24px auto;
display: flex;
gap: 18px;
padding: 0px;
}
:host ul.tickets-list li {
text-align: center;
}
:host ul.tickets-list li.current-time {
display: flex;
gap: 20px;
}
:host ul.tickets-list li .ticket-list__day {
font-weight: 600;
font-size: 14px;
line-height: 27px;
text-align: center;
color: #000000;
text-transform: uppercase;
position: relative;
}
:host ul.tickets-list li a, :host ul.tickets-list li span.disabled {
margin-top: 10px;
background: rgba(0, 0, 0, 0.1);
border-radius: 8px;
padding: 4px 20px;
font-weight: 600;
font-size: 14px;
line-height: 27px;
color: #26303E;
text-decoration: none;
display: block;
position: relative;
}
:host ul.tickets-list li span.disabled {
opacity: 0.5;
font-size: 12px;
text-align: left;
padding: 4px 11px;
cursor: default;
}
:host ul.tickets-list li a:hover, :host ul.tickets-list li a.selected {
background: rgba(72, 181, 227, 0.6);
}
:host ul.tickets-list li a.type::before {
display: block;
position: absolute;
left: 9px;
margin: auto 0px;
top: 0px;
bottom: 0px;
width: 8px;
height: 8px;
border-radius: 100%;
}
:host ul.tickets-list li a.type-Geburtstage::before, .types span.type-Geburtstage::before {
content: '';
background: #D11BEF;
}
:host ul.tickets-list li a.type-Disco::before, .types span.type-Disco::before {
content: '';
background: #3430FF
;
}
:host ul.tickets-list li a.type-OldieDisco::before, .types span.type-OldieDisco::before {
content: '';
background: #48D118;
}
:host .types {
display: flex;
justify-content: center;
gap: 8px;
font-weight: 700;
font-size: 14px;
line-height: 27px;
text-align: center;
color: rgba(0, 0, 0, 0.5);
}
:host .types span::before {
display: inline-block;
width: 8px;
height: 8px;
border-radius: 100%;
margin-right: 5px;
}
:host .tickets-list__currentBooking {
text-align: center;
padding: 12px 0px;
font-size: 14px ;
line-height: 27px ;
text-align: center ;
text-transform : uppercase ;
color: rgba ( 0, 0, 0, 0.5 ) ;
}
:host .tickets-list__currentBooking span {
color: #48B5E3;
}
:host .tickets-list__available {
width: 100%;
margin: 12px auto;
max-width: 571px;
height: 27px;
background: #e5e5e5;
text-align: right;
font-weight : 600 ;
font-size : 14px;
line-height : 27px;
color: black;
padding-right: 5px;
position: relative;
}
.tickets-list__current {
background: black;
position: absolute;
left: 0px;
top: 0px;
display: flex;
justify-content: space-between;
height: 27px;
color: white;
padding: 0px 5px;
width: 1px;
transition: all 1s ease-out;
color: #48B5E3;
}
.tickets-list__current span {
color: white;
}
:host .warning {
background: lightyellow;
padding: 5px 10px;
opacity: 0.7;
}
.gg-arrow-right {
box-sizing: border-box;
position: relative;
display: block;
transform: scale(var(--ggs,1));
width: 22px;
height: 22px
}
.gg-arrow-right::after,
.gg-arrow-right::before {
content: "";
display: block;
box-sizing: border-box;
position: absolute;
right: 9px
}
.gg-arrow-right::after {
width: 8px;
height: 8px;
border-top: 2px solid;
border-right: 2px solid;
transform: rotate(45deg);
bottom: 13px
}
.gg-arrow-right::before {
width: 16px;
height: 2px;
bottom: 16px;
background: currentColor
}
:host .gg-arrow-right {
position : absolute;
width : 35px;
height : 35px;
right : 0px;
top : 0px;
background : #EEF2F3;
border-radius : 8px;
border: 0px;
display: none;
}
:host .gg-arrow-left {
transform: rotate(180deg)!important;
}
:host .gg-arrow-right.gg-arrow-left {
left: 0px;
right: none;
}
:host .btn-order {
width: fit-content;
margin: 0px auto;
padding : 24px 48px;
font-style : normal;
font-weight : 700;
font-size : 18px;
line-height : 22px;
text-transform: uppercase;
border: 1px solid black;
border-radius: 50px;
background: white;
cursor: pointer;
margin: 12px auto 24px auto;
}
:host .btn-success {
background: yellowgreen!important;
border-color: yellowgreen!important;
color: white!important;
}
:host .btn-order:hover {
background: black;
color: white;
}
@media screen and (max-width: 768px) {
:host ul.tickets-list {
overflow-x: scroll;
width: 100%;
scroll-snap-type: x mandatory;
}
:host ul.tickets-list {
-ms-overflow-style: none; /* Internet Explorer 10+ */
scrollbar-width: none; /* Firefox */
}
:host ul.tickets-list li .ticket-list__day {
line-height: 35px;
}
:host .gg-arrow-right {
display: block;
z-index: 10;
}
:host ul.tickets-list::-webkit-scrollbar {
display: none; /* Safari and Chrome */
}
:host ul.tickets-list li {
width: 100%;
flex-shrink: 0;
}
:host .tickets-list__currentBooking {
line-height: 22px;
}
:host .tickets-list__currentBooking span {
display: block;
text-align: center;
}
:host .btn-order {
padding: 16px;
font-size : 14px;
line-height : 17px;
}
}
`;
// Add style to shadowRoot
shadowRoot.appendChild(this.styles);
// Api urls
this.getURL = 'https://www.eissporthalle-duesseldorf.de/capacity.php';
this.postURL = 'https://eissporthallebenrath.de:9999/Json/General/FindAccessZones/';
this.section = document.createElement('section');
shadowRoot.appendChild(this.section);
// Create loader icon
const loaderIcon = (isLoading) => {
if(isLoading === true) {
this.loader = document.createElement('div');
this.loader.classList.add('lds-dual-ring');
this.section.appendChild(this.loader);
} else {
this.loader ? this.loader.remove() : '';
}
}
// Init functions
const init = () => {
sliderButtons();
timeTable();
typeList();
currentTicketInfo();
bookingBtn();
buttonOrderHandler();
}
// Create slider button
const sliderButtons = () => {
this.arrowRight = document.createElement('button');
this.arrowRight.classList = 'gg-arrow-right';
this.section.appendChild(this.arrowRight);
this.arrowLeft = document.createElement('button');
this.arrowLeft.classList = 'gg-arrow-right gg-arrow-left';
this.section.appendChild(this.arrowLeft);
}
// Create ul list and paste it inside a shadow root
const timeTable = () => {
this.list = document.createElement('ul');
this.list.className = 'tickets-list';
this.section.appendChild(this.list);
}
// Create list of types
const typeList = () => {
this.types =
`
Disco
Kinderdisco
Oldie Disco
`
;
this.list.insertAdjacentHTML('afterend', this.types);
}
// Create current available time with progress bar
const currentTicketInfo = () => {
this.currentBooking = document.createElement('div');
this.currentBooking.className = 'tickets-list__currentBooking'
this.section.appendChild(this.currentBooking);
// Create a progress bar
this.progressBar = document.createElement('div');
this.section.appendChild(this.progressBar);
this.current = document.createElement('div');
this.current.className = 'tickets-list__current';
}
// Change format of the day value
const formatDay = (day) => {
const dayValue = new Date(day);
return dayValue.toLocaleDateString('de-DE', {weekday: 'long'});
}
// Change format of the time value
const formatTime = (time) => {
const dateTime = new Date(time);
return dateTime.toLocaleTimeString([], { hour: '2-digit'});
}
// Change format of the date value
const formatDate = (date) => {
const currentDate = new Date(date);
const day = ("0" + currentDate.getDate()).slice(-2);
const month = ("0" + (currentDate.getMonth() + 1)).slice(-2);
return `${day}.${month}`;
}
// Define current time in mileseconds
this.currentTime = Date.now();
// Define current date in mileseconds
this.date = new Date();
this.currentDate = this.date.getFullYear() + '-' + (this.date.getMonth() + 1) + '-' + this.date.getDate();
// Create Order button
const bookingBtn = () => {
this.orderButton = document.createElement('button');
this.orderButton.className = 'btn-order';
this.orderButton.textContent = 'DIESE LAUFZEIT BUCHEN';
this.section.appendChild(this.orderButton);
}
// Add event for order button
const buttonOrderHandler = () => {
this.orderButton.addEventListener('click', () => {
PostData(this.from, this.until, this.orderButton);
});
}
// Fetch data from api
this.fetchData = async () => {
loaderIcon(true);
this.response = await fetch(this.getURL);
// Check is response is valid
if(!this.response.ok) {
throw new Error('Response is invalid');
}
this.responseData = await this.response.json();
// console.log(this.responseData)
// Check is Array is valid
if (!this.responseData || Object.keys(this.responseData).length === 0) {
this.isDataLoading = false;
throw new Error('Data or Response is invalid');
}
// Loading content
init();
// Make an array from object
this.loadedData = Object.entries(this.responseData);
this.loadedData.forEach((data) => {
// Show current day on the homepage
if(isHomepage()) {
this.today = data[0] === this.currentDate;
if(!this.today) return;
}
// Create item of list and paste it inside the list
this.item = document.createElement('li');
isHomepage() ? this.item.classList.add('current-time') : '';
this.list.appendChild(this.item);
// Insert days into the timetable
if(!isHomepage()) {
this.day = document.createElement('div');
this.day.className = 'ticket-list__day'
this.day.textContent = formatDay(data[0]);
this.item.appendChild(this.day);
}
// Insert time range into the timetable
data[1].forEach((item) => {
if(item.remaining > 0) {
// Create link of item
this.link = document.createElement('a');
this.link.className = `type type-${item.type}`
this.link.href = '#';
this.link.textContent = `${formatTime(item.from).replace('Uhr', '')} - ${formatTime(item.until).replace('Uhr', '')} Uhr`;
this.item.appendChild(this.link);
} else {
this.disabled = document.createElement('span');
this.disabled.className = 'disabled';
this.disabled.textContent = 'keine Laufzeit';
this.item.appendChild(this.disabled);
}
// Add info about current time according progress bar
const currentInfo = (current, maximum, from, until, data) => {
// Current information about time range
this.currentBooking.innerHTML = '';
this.currentBooking.insertAdjacentHTML('afterbegin', `AUSLASTUNG FÜR
${formatDay(data)} ${formatDate(data)} / ${formatTime(from).replace('Uhr', '')} - ${formatTime(until).replace('Uhr', '')} Uhr`);
// Current information about available tickets
this.progressBar.className = 'tickets-list__available';
this.progressBar.textContent = maximum;
// Define width of the progress bar acording available and remaning tickets
this.percent = current / maximum * 100;
this.current.innerHTML = '';
this.current.insertAdjacentHTML('afterbegin', `
0 ${current}`);
this.progressBarWidth = window.document.body.offsetWidth > 768 ? 571 : 240;
this.width = this.progressBarWidth / 100 * this.percent;
this.current.style.width = `${this.width this.timeFrom && this.currentTime {
e.preventDefault();
if(item.remaining > 0) {
// Remove class selected
[...this.list.querySelectorAll('a.selected')].forEach((item) => {
item.classList.remove("selected");
})
// Add selected class
this.link.classList.remove('selected');
e.target.classList.add('selected');
// Show info about selected time range
currentInfo(item.current, item.maximum, item.from, item.until, data[0]);
// Define data for submit event
this.from = item.from;
this.until = item.until;
}
});
});
// Add event for slider buttons on mobile
let buttonHandler = (side, arg) => {
side.addEventListener('click', (e) => {
e.preventDefault;
this.list.scrollBy(
{
left: arg === true
? this.item.offsetWidth + 18
: -(this.item.offsetWidth + 18),
behavior: 'smooth',
}
)
})
}
buttonHandler(this.arrowLeft, false);
buttonHandler(this.arrowRight, true);
});
}
// Message if fetch response is invalid
this.warning = () => {
this.warMessage = document.createElement('p');
this.warMessage.classList = 'warning';
this.warMessage.textContent = 'Leider ist die aktuelle Auslastung derzeit nicht verfügbar. Sie können diese direkt im Shop einsehen.'
this.section.appendChild(this.warMessage);
}
// Call fetch from api
this.fetchData()
.catch((error) => {
console.log(error.message);
this.warning();
})
.finally(() => loaderIcon(false));
// Send order to server
const PostData = (from, until, btn) => {
const redirectURL = 'https://eissporthallebenrath.de/Exhibitions/Register';
this.request = {
"Criteria": {
"Id": null,
"Includes": {
"EntranceReaders": false,
"ExitReaders": false,
"Occupancy": true,
"InactiveZoneControl": false
},
"OccupancyFrom": from,
"OccupancyUntil": until
},
"Context": {
"Language": "en",
"ShopId": "a38fb7dc-248c-4dc7-b282-4f61d76b08cf",
"SessionId": "00000000-0000-0000-0000-000000000000",
"Password": "zGNM436kTVWNnw"
}
};
fetch(this.postURL, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(this.request)
})
.then(function(response) {
if (response.ok) {
btn.textContent = 'Bestellung wurde abgeschickt';
btn.classList.add('btn-success');
setTimeout(() => {
btn.classList.remove('btn-success');
btn.textContent = 'DIESE LAUFZEIT BUCHEN';
}, 3000);
return response.json();
}
throw new Error("Network response was not ok.");
})
.then(function(data) {
// Handle the response data here
console.log(data)
const redirectUrl = (id) => {
window.open(`${redirectURL}?ID=${id}`, '_blank')
}
const loadedData = Object.entries(data.accessZones);
loadedData.forEach((el) => {
redirectUrl(el[1].id);
});
})
.catch(function(error) {
// Handle errors here
console.error(error);
});
}
}
}
customElements.define('ticket-shop', TicketShop);