skipper-organisms
Version:
A vanilla JavaScript library for skipperhospitality management
354 lines (331 loc) • 12 kB
JavaScript
import "./styles/accommodation-search-form.css";
const extend = function (to, from, overwrite) {
let prop, hasProp;
for (prop in from) {
hasProp = to[prop] !== undefined;
if (
hasProp &&
typeof from[prop] === "object" &&
from[prop] !== null &&
from[prop].nodeName === undefined
) {
if (isDate(from[prop])) {
if (overwrite) {
to[prop] = new Date(from[prop].getTime());
}
} else if (Array.isArray(from[prop])) {
if (overwrite) {
to[prop] = from[prop].slice(0);
}
} else {
to[prop] = extend({}, from[prop], overwrite);
}
} else if (overwrite || !hasProp) {
to[prop] = from[prop];
}
}
return to;
};
const isDate = function (obj) {
return (
/Date/.test(Object.prototype.toString.call(obj)) &&
!isNaN(obj.getTime())
);
};
const defaults = {
blockId: "",
blockClass: "",
accommodations: [],
checkIn: null,
checkOut: null,
rooms: 1,
promoCode: null,
maxRoom: 10,
maxGuest: 10,
showAccommodations: true,
showDatesLabel: true,
showPromocode: true,
monthsShort: [
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec",
],
hotelErrorMsg: "Please select a hotel",
};
const renderHotels = (hotels) => {
let optEle = "";
hotels.forEach((hotel) => {
optEle += `<div class="option">
<input type="radio" class="radio" id="${hotel}" value="${hotel}" name="hotel"/>
<label for="${hotel}">${hotel}</label>
</div>`;
});
return `
<div class="md:pr-4" id="hotel-selector">
<label class="form_label sm:hidden" for="hotel-selector" > Select Hotel </label>
<div class="select-box hotel">
<div class="options-container">
${optEle}
</div>
<div class="selected">Select Hotel</div>
</div>
<div class="sm:px-4 error-msg" id="hotel-error"></div>
</div>`;
};
const renderDate = (type, options) => {
let dateString = "",
dateValue = "";
if (type === "check-in") {
dateValue =
options.checkIn.getDate() +
` ${options.monthsShort[options.checkIn.getMonth()]}`;
dateString =
options.checkIn.getDate() +
` <small>${
options.monthsShort[options.checkIn.getMonth()]
}</small>`;
} else if (type === "check-out") {
dateValue =
options.checkOut.getDate() +
` ${options.monthsShort[options.checkOut.getMonth()]}`;
dateString =
options.checkOut.getDate() +
` <small>${
options.monthsShort[options.checkOut.getMonth()]
}</small>`;
}
let valEle = "";
if (options.showDatesLabel) {
valEle = `<input type="text" class="hidden" name="${type}" id="datepicker-${type}" value="${dateValue}"/>
<label class="datepicker text-center" for="datepicker-${type}" id="datepicker-${type}-label">${dateString}</label>`;
} else {
valEle = `<input type="text" class="" name="${type}" id="datepicker-${type}" value="${dateValue}"/>`;
}
return `
<div class="px-2 md:px-4 sm:py-2 sm:flex sm:justify-between sm:items-center sm:border-t${
type === "check-in" ? "-r" : ""
}" >
<label class="form_label text-center">${
type === "check-in"
? "Check In"
: type === "check-out"
? "Check Out"
: ""
}</label>
${valEle}
</div>`;
};
const renderRooms = (opts) => {
let roomsEle = "";
for (let i = 1; i <= opts.maxRoom; i++) {
roomsEle += `
<div class="option">
<input type="radio" class="radio" id="rooms_${i}" value="${i}" name="rooms" checked />
<label for="rooms_${i}">${i}</label>
</div>`;
}
return `
<div class="sm:grid px-2 md:px-4 sm:border-t-r" id="rooms-selector" >
<label class="form_label sm:text-center" for="rooms-selector" > Rooms </label>
<div class="select-box">
<div class="options-container">
${roomsEle}
</div>
<div class="selected sm:w-10 sm:mx-auto">1</div>
</div>
</div>`;
};
const renderGuest = (opts) => {
let guestEle = "";
for (let i = 1; i <= opts.maxGuest; i++) {
guestEle += `
<div class="option">
<input type="radio" class="radio" id="guests_${i}" value="${i}" name="guests" checked />
<label for="guests_${i}">${i}</label>
</div>`;
}
return `
<div class="sm:grid px-2 md:px-4 sm:border-t" id="guests-selector" >
<label class="form_label sm:text-center" for="guests-selector" > Guests </label>
<div class="select-box">
<div class="options-container sm:w-10">
${guestEle}
</div>
<div class="selected sm:w-10 sm:mx-auto">1</div>
</div>
</div>
`;
};
const renderPromoCode = () => {
return `
<div class="px-4 sm:py-5 sm:flex sm:border-t">
<label class="form_label" for="promo-code" >Promo Code</label >
<input type="text" class="promo-code" name="promo-code" id="promo-code"/>
</div>`;
};
const renderButtn = () => {
return `
<button type="submit" class="submit_button">
Book Now
</button>`;
};
export let AccomodationForm = function (options) {
let self = this;
self.config(options);
if (self._o.el) {
self.render();
//add close button event
const closeBtn = document.querySelector("#btn-close");
closeBtn.addEventListener("click", () => {
self.close();
});
// add select events
const selected = document.querySelectorAll(".selected");
selected.forEach((item) => {
const optionsContainer =
item.parentElement.querySelector(".options-container");
const optionList = optionsContainer.querySelectorAll(".option");
item.addEventListener("click", () => {
optionsContainer.classList.toggle("active");
//Should close if click on outside
document.addEventListener("mousedown", function (e) {
if (!e.composedPath().includes(optionsContainer)) {
optionsContainer.classList.remove("active");
}
});
});
optionList.forEach((o) => {
o.addEventListener("click", () => {
item.innerHTML = o.querySelector("label").innerHTML;
optionsContainer.classList.remove("active");
});
});
});
// Form validation
function logSubmit(event) {
event.preventDefault();
let errorMessage = "";
let hotel = ""; //form.elements["hotel-selector"].querySelector('input[type="radio"]:checked').value;
const hotelSelector = form
.querySelector("#hotel-selector")
.querySelector('input[type="radio"]:checked');
if (!hotelSelector) {
errorMessage += "Please select a hotel.\n";
self._o.hotelErrorMsg = "Please select a hotel to continue";
} else {
hotel = hotelSelector.value;
self._o.hotelErrorMsg = "";
}
const checkIn = form.elements["check-in"].value;
const checkOut = form.elements["check-out"].value;
const rooms = form
.querySelector("#rooms-selector")
.querySelector('input[type="radio"]:checked').value;
const guests = form
.querySelector("#guests-selector")
.querySelector('input[type="radio"]:checked').value; //form.elements["guests-selector"].value;
const promoCode = form.elements["promo-code"].value;
const formData = {
hotel,
checkIn,
checkOut,
rooms,
guests,
promoCode,
};
var event = new CustomEvent("accommodation-search-form-submitted", {
detail: formData,
});
self.showError();
if (!errorMessage.length > 0) {
document.dispatchEvent(event);
}
}
const form = document.getElementById("accommodations");
form.addEventListener("submit", logSubmit);
}
};
AccomodationForm.prototype = {
config: function (options) {
if (!this._o) {
this._o = extend({}, defaults, true);
}
let opts = extend(this._o, options, true);
if (opts.blockId) {
opts.el = document.getElementById(this._o.blockId);
} else if (opts.blockClass) {
opts.el = document.getElementsByClassName(this._o.blockClass);
}
//Set default check in and check out dates
opts.checkIn = new Date();
opts.checkIn.setDate(opts.checkIn.getDate() + 1);
opts.checkOut = new Date();
opts.checkOut.setDate(opts.checkOut.getDate() + 2);
return opts;
},
render: function () {
this._o.el.classList.add("form_container");
let hotelsEle = "";
if (this._o.accommodations.length > 0 && this._o.showAccommodations) {
hotelsEle = renderHotels(this._o.accommodations);
}
let checkInEle = renderDate("check-in", this._o);
let checkOutEle = renderDate("check-out", this._o);
this._o.el.innerHTML = `
<div class="md:hidden sm:flex sm:flex-row-reverse">
<button id="btn-close" class="btn-close bg-white border-0">
<svg fill="none" stroke="#000" width="1.5rem" height="1.5rem" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="3" d="M6 18L18 6M6 6l12 12"></path></svg>
</button>
</div>
<form class="booking_form sm:border" id="accommodations">
${hotelsEle}
<div class="grid grid-cols-2">
${checkInEle}
${checkOutEle}
<div class="date-ranger" id="calendar-component" style="grid-column: span 2 / span 2;">
</div>
</div>
<div class="grid grid-cols-2">
${renderRooms(this._o)}
${renderGuest(this._o)}
</div>
${this._o.showPromocode ? renderPromoCode() : ""}
${renderButtn()}
</form>
`;
this._o.el.classList.add("sm:hidden");
let btn = document.createElement("button");
btn.innerHTML = "Book Now";
btn.type = "button";
btn.id = "btn-mobile-book-now";
btn.className = "mobile_submit_button";
btn.addEventListener("click", () => {
btn.classList.add("!z-1");
this._o.el.classList.remove("sm:hidden");
});
if (this._o.el.nextSibling) {
this._o.el.parentNode.insertBefore(btn, this._o.el.nextSibling);
} else {
this._o.el.parentNode.appendChild(btn);
}
},
showError: function () {
const error = document.querySelector(`#hotel-error`);
error.innerHTML = this._o.hotelErrorMsg;
},
close: function () {
this._o.el.classList.add("sm:hidden");
this._o.el.parentNode
.querySelector("#btn-mobile-book-now")
.classList.remove("!z-1");
},
};