Last active
July 1, 2019 11:22
-
-
Save JirkaChadima/262c06fbd0e0f00235c5a695c567a64b to your computer and use it in GitHub Desktop.
Simple sample booking page for Winding Tree
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html lang="en"> | |
<head> | |
<meta charset="utf-8"> | |
<title>Winding Tree simple sample booking page</title> | |
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css" integrity="sha384-GJzZqFGwb1QTTN6wy59ffF1BuGJpLSa9DkKMp0DgiMDm4iYMj70gZWKYbI706tWS" crossorigin="anonymous"> | |
</head> | |
<body class="container"> | |
<div id="error" class="alert"></div> | |
<div id="hotel-description"> | |
<h1 id="hotel-name"></h1> | |
<div id="hotel-address"></div> | |
</div> | |
<hr /> | |
<div id="stay-details"> | |
<p> | |
<label for="room-types-select">Select a room</label> | |
<select id="room-types-select" name="room-types-select" class="form-control"></select> | |
</p> | |
<p> | |
<label for="arrival-date">Date of arrival</label> | |
<input type="date" id="arrival-date" name="arrival-date" class="form-control" /> | |
</p> | |
<p> | |
<label for="arrival-date">Date of departure</label> | |
<input type="date" id="departure-date" name="departure-date" class="form-control" /> | |
</p> | |
</div> | |
<hr /> | |
<div id="offer"> | |
<div id="offer-price"></div> | |
<div id="offer-quantity"></div> | |
</div> | |
<hr /> | |
<button type="button" disabled="disabled" id="book-button" class="btn btn-primary">Book a room!</button> | |
<script type="text/javascript" src="https://unpkg.com/@windingtree/wt-pricing-algorithms@0.6.2/dist/umd/wt-pricing-algorithms.js"></script> | |
<script type="text/javascript" src="https://unpkg.com/@windingtree/wt-js-libs@0.13.2/dist/umd/wt-js-libs.js"></script> | |
<script type="text/javascript" src="https://unpkg.com/dayjs@1.8.2/dayjs.min.js"></script> | |
<script type="text/javascript" src="https://unpkg.com/web3-utils@1.0.0-beta.55/dist/web3-utils.umd.js"></script> | |
<script type="text/javascript"> | |
(() =>{ | |
let hotelDataFromApi; | |
let resultingPrice; | |
const errorDiv = document.getElementById('error'); | |
const bookButton = document.getElementById('book-button'); | |
const wtReadApi = 'https://lisbon-api.windingtree.com'; | |
const hotelId = '0xcca04822Ad9c178bdf9da9091218e241f4C28042'; | |
const fields = [ | |
'name', | |
'address', | |
'currency', | |
'cancellationPolicies', | |
'defaultCancellationAmount', | |
'bookingUri', | |
'roomTypes.id', | |
'roomTypes.name', | |
'roomTypes.occupancy', | |
'availability', | |
'ratePlans', | |
]; | |
const displayHotelData = (hotelData) => { | |
const address = ` | |
<span>${hotelData.address.road} ${hotelData.address.houseNumber},</span> | |
<span>${hotelData.address.city} ${hotelData.address.postcode}</span> | |
<span>${hotelData.address.countryCode}</span> | |
`; | |
document.getElementById('hotel-name').innerHTML = hotelData.name; | |
document.getElementById('hotel-address').innerHTML = address; | |
const roomTypesSelect = document.getElementById('room-types-select'); | |
hotelData.roomTypes.map((rt) => { | |
roomTypesSelect.insertAdjacentHTML('beforeend', ` | |
<option id="rt-${rt.id}" value="${rt.id}">${rt.name}</option> | |
`); | |
}); | |
}; | |
fetch(`${wtReadApi}/hotels/${hotelId}?fields=${fields.join(',')}`) | |
.then((response) => { | |
if (response.status > 299) { | |
throw new Error('Bad server response.'); | |
} | |
return response.json(); | |
}) | |
.then((data) => { | |
hotelDataFromApi = data; | |
displayHotelData(data); | |
}) | |
.catch((err) => { | |
errorDiv.innerHTML = err.toString(); | |
}); | |
const guests = [ | |
{"name": "Elizabeth", "surname": "Crown", "age": 25}, | |
{"name": "Philip", "surname": "Crown", "age": 30}, | |
]; | |
const roomTypesSelect = document.getElementById('room-types-select'); | |
const arrivalDateInput = document.getElementById('arrival-date'); | |
const departureDateInput = document.getElementById('departure-date'); | |
const recomputePriceAndAvailability = () => { | |
const roomType = roomTypesSelect.value; | |
const arrival = dayjs(arrivalDateInput.value); | |
const departure = dayjs(departureDateInput.value); | |
// Basic validation | |
if (!roomType || | |
!arrival.isValid() || | |
!departure.isValid() || | |
departure.isBefore(arrival) || | |
arrival.isBefore(dayjs()) | |
) { | |
bookButton.disabled = 'disabled'; | |
return; | |
} | |
// Price | |
const pc = new window.wtPricingAlgorithms.prices.PriceComputer( | |
hotelDataFromApi.roomTypes, | |
hotelDataFromApi.ratePlans, | |
hotelDataFromApi.currency | |
); | |
resultingPrice = pc.getBestPrice( | |
new Date(), // Booking date | |
arrival, | |
departure, | |
guests, | |
hotelDataFromApi.currency, | |
roomType | |
) | |
// We might not get a price - room is meant for a different number of people or pricing data is not available | |
.filter((p) => p.id === roomType && p.prices.length); | |
if (resultingPrice.length) { | |
// Different price resolution strategies might return more than one price | |
const actualPrice = resultingPrice[0].prices[0]; | |
document.getElementById('offer-price').innerHTML = `Price ${actualPrice.total.format()} ${actualPrice.currency}`; | |
} else { | |
document.getElementById('offer-price').innerHTML = 'Price unavailable'; | |
} | |
// Availability | |
const indexedAvailability = window.wtPricingAlgorithms.availability.indexAvailability(hotelDataFromApi.availability.roomTypes); | |
const roomAvailability = window.wtPricingAlgorithms.availability.computeAvailability( | |
arrival, | |
departure, | |
guests.length, | |
hotelDataFromApi.roomTypes, | |
indexedAvailability | |
).filter((ra) => ra.roomTypeId === roomType && ra.quantity); | |
if (roomAvailability.length) { | |
const actualQuantity = roomAvailability[0].quantity; | |
document.getElementById('offer-quantity').innerHTML = `Available: ${actualQuantity}`; | |
} else { | |
// Room might be sold out, unavailable for arrival or departure on given dates etc. | |
document.getElementById('offer-quantity').innerHTML = 'Unavailable'; | |
} | |
if (resultingPrice.length && roomAvailability.length) { | |
bookButton.removeAttribute('disabled'); | |
} else { | |
bookButton.disabled = true; | |
} | |
} | |
roomTypesSelect.addEventListener('change', recomputePriceAndAvailability); | |
arrivalDateInput.addEventListener('change', recomputePriceAndAvailability); | |
departureDateInput.addEventListener('change', recomputePriceAndAvailability); | |
bookButton.addEventListener('click', async () => { | |
const customer = { | |
name: 'Elizabeth', | |
surname: 'Crown', | |
email: 'elizabeth@example.com', | |
}; | |
const booking = { | |
arrival: dayjs(arrivalDateInput.value).format('YYYY-MM-DD'), | |
departure: dayjs(departureDateInput.value).format('YYYY-MM-DD'), | |
guestInfo: guests.map((g, i) => Object.assign({id: `guest-${i}`}, g)), | |
rooms: [ | |
{ | |
id: roomTypesSelect.value, | |
guestInfoIds: ['guest-0', 'guest-1'] | |
} | |
] | |
}; | |
const actualPrice = resultingPrice[0].prices[0]; | |
const pricing = { | |
currency: actualPrice.currency, | |
total: actualPrice.total.value, | |
cancellationFees: window.wtPricingAlgorithms.cancellationFees.computeCancellationFees( | |
new Date(), | |
dayjs(arrivalDateInput.value), | |
hotelDataFromApi.cancellationPolicies, | |
hotelDataFromApi.defaultCancellationAmount | |
) | |
}; | |
/* | |
const wallet = window.wtJsLibs.createWallet({...}); | |
wallet.unlock(walletPassword); | |
const serializedRequest = JSON.stringify({ | |
originAddress: wallet.address, | |
customer: customer, | |
hotelId: hotelId, | |
booking: booking, | |
pricing: pricing, | |
}); | |
const signature = await wallet.signData(window.Web3Utils.soliditySha3(serializedRequest)); | |
*/ | |
fetch(`${hotelDataFromApi.bookingUri}/booking`, { | |
method: 'POST', | |
body: serializedRequest, | |
headers: { | |
'Content-Type': 'application/json', | |
// 'x-wt-signature': signature, // optional | |
}, | |
}) | |
.then((response) => { | |
if (response.status > 299) { | |
throw new Error('Cannot save booking!'); | |
} | |
return response.json(); | |
}) | |
.then((data) => { | |
errorDiv.innerHTML = `Your booking was accepted with the state ${data.status} under ID ${data.id}`; | |
}) | |
.catch((err) => { | |
errorDiv.innerHTML = err.toString(); | |
}); | |
}); | |
})(); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment