class HotelsCalculator extends ROICalculator { // private inputs #weeklyMisplacedKeys; #yearlyLostKeys; // private outputs constructor(misplacedKeyRate, lostKeyRate, replacementKeyCost, staffTimeMisplacedKeys, manualKeyHandOffTime, keycafeHandOffTime, avgHourlyWage, misplacedKeysStaffTimeCost, lostKeyCost, manualKeyHandoffStaffTimeCost, yearlyTotalCosts){ // inputs and outputs super(); // assumptions this.misplacedKeyRate = misplacedKeyRate; this.lostKeyRate = lostKeyRate; this.replacementKeyCost = replacementKeyCost; this.staffTimeMisplacedKeys = staffTimeMisplacedKeys; this.avgHourlyWage = avgHourlyWage; this.keycafeHandOffTime = keycafeHandOffTime; this.manualKeyHandOffTime = manualKeyHandOffTime; this.misplacedKeysStaffTimeCost = misplacedKeysStaffTimeCost; this.lostKeyCost = lostKeyCost; this.manualKeyHandoffStaffTimeCost = manualKeyHandoffStaffTimeCost; this.yearlyTotalCosts = yearlyTotalCosts; } // constructor getWeeklyMisplacedKeys(){ this.#weeklyMisplacedKeys = ((this.misplacedKeyRate * this.getWeeklyKeyAccesses()) / 100); return Number(this.#weeklyMisplacedKeys); } // getWeeklyMisplacedKeys() getYearlyLostKeys(){ this.#yearlyLostKeys = ((this.lostKeyRate * this.getWeeklyKeyAccesses() * 52) / 100); return Number(this.#yearlyLostKeys); } // getYearlyLostKeys() getStatusQuoCosts(){ const replacementKeyCost = this.replacementKeyCost.isCurrency ? this.replacementKeyCost.raw : this.replacementKeyCost, avgHourlyWage = this.avgHourlyWage.isCurrency ? this.avgHourlyWage.raw : this.avgHourlyWage; this.misplacedKeysStaffTimeCost = ( this.getWeeklyMisplacedKeys() * 52 * this.staffTimeMisplacedKeys * avgHourlyWage ) / 60; this.lostKeyCost = this.getYearlyLostKeys() * replacementKeyCost; this.manualKeyHandoffStaffTimeCost = (this.getWeeklyKeyAccesses() * 52 * (2 - this.keycafeHandOffTime) * this.manualKeyHandOffTime * avgHourlyWage) / 60; this.yearlyTotalCosts = this.misplacedKeysStaffTimeCost + this.lostKeyCost + this.manualKeyHandoffStaffTimeCost; return { misplacedKeysStaffTimeCost: this.misplacedKeysStaffTimeCost, lostKeyCost: this.lostKeyCost, manualKeyHandoffStaffTimeCost: this.manualKeyHandoffStaffTimeCost, yearlyTotalCosts: this.yearlyTotalCosts, } } // getStatusQuoCosts() getStatusQuoBreakdown(){ const statusQuo = this.getStatusQuoCosts(); let statusQuoBreakdown = []; const misplacedKeysStaffTimeCost = { type: "Misplaced keys staff time cost", calculation: `(${this.round(this.getWeeklyMisplacedKeys(), 2)} * 52 * ${this.staffTimeMisplacedKeys} * ${this.avgHourlyWage.isCurrency ? this.avgHourlyWage.amount : this.formatCurrency(this.avgHourlyWage).amount}) / 60`, amount: this.formatCurrency(statusQuo.misplacedKeysStaffTimeCost).amount, } const lostKeyCost = { type: "Lost Key Cost", calculation: `${this.round(this.getYearlyLostKeys(), 2)} * ${this.replacementKeyCost.isCurrency ? this.replacementKeyCost.amount : this.formatCurrency(this.replacementKeyCost).amount}`, amount: this.formatCurrency(statusQuo.lostKeyCost).amount, } const manualKeyHandoffStaffTimeCost = { type: "Manual key handoff staff time cost", calculation: `(${this.getWeeklyKeyAccesses()} * 52 * (2 - ${this.keycafeHandOffTime}) * ${this.manualKeyHandOffTime} * ${this.avgHourlyWage.isCurrency ? this.avgHourlyWage.amount : this.formatCurrency(this.avgHourlyWage).amount}) / 60`, amount: this.formatCurrency(statusQuo.manualKeyHandoffStaffTimeCost).amount, } const yearlyTotalCosts = { type: "Total cost per year", calculation: `${this.formatCurrency(statusQuo.misplacedKeysStaffTimeCost).amount} + ${this.formatCurrency(statusQuo.lostKeyCost).amount} + ${this.formatCurrency(statusQuo.manualKeyHandoffStaffTimeCost).amount}`, amount: this.formatCurrency(statusQuo.yearlyTotalCosts).amount, } const breakEvenTime = { type: "Time to break even", calculation: `(12 * (${this.hardwareCost.isCurrency ? this.hardwareCost.amount : this.formatCurrency(this.hardwareCost).amount} + ${this.softwareCost.isCurrency ? this.softwareCost.amount : this.formatCurrency(this.softwareCost).amount})) / ${this.formatCurrency(statusQuo.yearlyTotalCosts).amount}`, amount: this.breakEvenTime, } const Year3ROI = { type: "Year 3 ROI", calculation: `(${this.formatCurrency(statusQuo.yearlyTotalCosts).amount} * 3) / (${this.hardwareCost.isCurrency ? this.hardwareCost.amount : this.formatCurrency(this.hardwareCost).amount} + (${this.softwareCost.isCurrency ? this.softwareCost.amount : this.formatCurrency(this.softwareCost).amount} * 3)) * 100`, amount: this.year3ROI, } const Year5ROI = { type: "Year 5 ROI", calculation: `(${this.formatCurrency(statusQuo.yearlyTotalCosts).amount} * 5) / (${this.hardwareCost.isCurrency ? this.hardwareCost.amount : this.formatCurrency(this.hardwareCost).amount} + (${this.softwareCost.isCurrency ? this.softwareCost.amount : this.formatCurrency(this.softwareCost).amount} * 5)) * 100`, amount: this.year5ROI, } statusQuoBreakdown.push(misplacedKeysStaffTimeCost); statusQuoBreakdown.push(lostKeyCost); statusQuoBreakdown.push(manualKeyHandoffStaffTimeCost); statusQuoBreakdown.push(yearlyTotalCosts); statusQuoBreakdown.push(breakEvenTime); statusQuoBreakdown.push(Year3ROI); statusQuoBreakdown.push(Year5ROI); return statusQuoBreakdown; } // getStatusQuoBreakdown() #calculateBreakEvenTime(){ const hardwareCost = this.calculateHardwareCost().cost; const softwareAnnualCost = this.calculateSoftwareCost(this.softwarePlan); const breakEvenTime = 12 * (hardwareCost + softwareAnnualCost) / this.getStatusQuoCosts().yearlyTotalCosts; return breakEvenTime; } // #calculateBreakEvenTime() -- private breakEvenPeriod(){ const breakEvenTime = this.#calculateBreakEvenTime(); let breakEven; if(isFinite(breakEvenTime) && breakEvenTime >= 0){ breakEven = `${Math.round(breakEvenTime)} Months`; } else if (breakEvenTime <= 0){ breakEven = "N/A"; } else { breakEven = "< 1 Month"; } return breakEven; } // breakEvenPeriod() calculateROI(years) { const hardwareCost = this.calculateHardwareCost().cost; const softwareAnnualCost = this.calculateSoftwareCost(this.softwarePlan); const ROI = Math.round((this.getStatusQuoCosts().yearlyTotalCosts * years / (hardwareCost + (softwareAnnualCost * years))) * 100); if (!isNaN(ROI)) { return `${ROI}%` } else { return `N/A`; } } // calculateROI() } // HotelsCalculator const hotel = new HotelsCalculator(); // gets inputs from HTML const getHTMLInputs = () => { const inputsContainer = document.getElementById("roi-inputs"); const inputs = { numKeys: inputsContainer.querySelector("#numKeys"), weeklyKeyUsage: inputsContainer.querySelector("#weeklyKeyUsage"), weeklyKeyAccesses: inputsContainer.querySelector("#weeklyKeyAccesses"), softwarePlan: inputsContainer.querySelector("#plan"), lostKeyRate: inputsContainer.querySelector("#lostKeyRate"), yearlyLostKeys: inputsContainer.querySelector("#yearlyLostKeys"), misplacedKeyRate: inputsContainer.querySelector("#misplacedKeyRate"), replacementKeyCost: inputsContainer.querySelector("#replacementKeyCost"), staffTimeMisplacedKeys: inputsContainer.querySelector("#staffTimeMisplacedKeys"), keycafeHandOffTime: inputsContainer.querySelector("#keycafeHandOffTime"), avgHourlyWage: inputsContainer.querySelector("#hourlyWage"), weeklyMisplacedKeys: inputsContainer.querySelector("#weeklyMisplacedKeys"), manualKeyHandOffTime: inputsContainer.querySelector("#manualKeyHandOffTime"), } return inputs; } // getHTMLInputs // builds obj const setHotelProps = (i) => { // set inputs & assumptions hotel.numKeys = Number(i.numKeys.value); hotel.weeklyKeyUsage = Number(i.weeklyKeyUsage.value); hotel.softwarePlan = i.softwarePlan.value; hotel.misplacedKeyRate = Number(i.misplacedKeyRate.value); hotel.lostKeyRate = Number(i.lostKeyRate.value); hotel.replacementKeyCost = Number(i.replacementKeyCost.value); hotel.staffTimeMisplacedKeys = Number(i.staffTimeMisplacedKeys.value); hotel.avgHourlyWage = Number(i.avgHourlyWage.value); hotel.keycafeHandOffTime = Number(i.keycafeHandOffTime.value); hotel.manualKeyHandOffTime = Number(i.manualKeyHandOffTime.value); } // setHotelProps() const inputs = getHTMLInputs(); setHotelProps(inputs); const renderInputs = () => { inputs.weeklyKeyAccesses.value = hotel.getWeeklyKeyAccesses(); inputs.weeklyMisplacedKeys.value = hotel.getWeeklyMisplacedKeys().toFixed(1); inputs.yearlyLostKeys.value = hotel.getYearlyLostKeys().toFixed(1); inputs.numKeys.addEventListener("input", (e) => { hotel.numKeys = inputs.numKeys.value; inputs.weeklyKeyAccesses.value = hotel.getWeeklyKeyAccesses(); inputs.weeklyMisplacedKeys.value = hotel.getWeeklyMisplacedKeys().toFixed(1); inputs.yearlyLostKeys.value = hotel.getYearlyLostKeys().toFixed(1); }); inputs.weeklyKeyUsage.addEventListener("input", (e) => { hotel.weeklyKeyUsage = inputs.weeklyKeyUsage.value; inputs.weeklyKeyAccesses.value = hotel.getWeeklyKeyAccesses(); inputs.weeklyMisplacedKeys.value = hotel.getWeeklyMisplacedKeys(); inputs.yearlyLostKeys.value = hotel.getYearlyLostKeys().toFixed(1); }); inputs.misplacedKeyRate.addEventListener("input", (e) => { hotel.misplacedKeyRate = inputs.misplacedKeyRate.value; inputs.weeklyMisplacedKeys.value = hotel.getWeeklyMisplacedKeys().toFixed(1); }); inputs.lostKeyRate.addEventListener("input", (e) => { hotel.lostKeyRate = inputs.lostKeyRate.value; inputs.yearlyLostKeys.value = hotel.getYearlyLostKeys().toFixed(1); }); } // renderInputs() // calculate outputs const renderCalculation = () => { const inputs = getHTMLInputs(); setHotelProps(inputs); const calculations = { hardware: hotel.calculateHardwareCost(), software: hotel.calculateSoftwareCost(hotel.softwarePlan), } hotel.hardwareCost = hotel.formatCurrency(calculations.hardware.cost); hotel.hardwareList = calculations.hardware.breakdown; hotel.breakEvenTime = hotel.breakEvenPeriod(); hotel.year3ROI = hotel.calculateROI(3); hotel.year5ROI = hotel.calculateROI(5); hotel.softwareCost = hotel.formatCurrency(calculations.software); hotel.replacementKeyCost = hotel.formatCurrency(hotel.replacementKeyCost); hotel.avgHourlyWage = hotel.formatCurrency(hotel.avgHourlyWage); hotel.lostKeyCost = hotel.formatCurrency(hotel.lostKeyCost); hotel.misplacedKeysStaffTimeCost = hotel.formatCurrency(hotel.misplacedKeysStaffTimeCost); hotel.manualKeyHandoffStaffTimeCost = hotel.formatCurrency(hotel.manualKeyHandoffStaffTimeCost); hotel.yearlyTotalCosts = hotel.formatCurrency(hotel.yearlyTotalCosts); const outputForm = document.querySelector("form"); hotel.fillForm(hotel, outputForm); renderDetails(outputForm); // render outputs const results = document.getElementById("calculation-result"); const sendRoiResults = document.getElementById("send-roi-result"); results.classList.add("roi-visible"); sendRoiResults.classList.add("roi-visible"); // window.scrollTo({ behavior: 'smooth', top: results.offsetTop }); let breakEven = results.querySelector("#paybackPeriod"), year3ROI = results.querySelector("#year3ROI"), year5ROI = results.querySelector("#year5ROI"), hardwareCost = results.querySelector("#hardwareCost"), hardwareDetails = results.querySelector("#hardwareDetails"), softwareAnnualCost = results.querySelector("#softwareCost"), softwarePlan = results.querySelector("#softwarePlan"); breakEven.innerText = hotel.breakEvenTime; year3ROI.innerText = hotel.year3ROI; year5ROI.innerText = hotel.year5ROI; hardwareCost.innerText = hotel.hardwareCost.amount; softwarePlan.innerText = `(${hotel.softwarePlan} Plan)`; softwareAnnualCost.innerText = hotel.softwareCost.amount; hardwareDetails.innerHTML = ""; hotel.hardwareList.forEach((e) => { let span = document.createElement("span"); span.classList.add("hardware-element"); span.innerText = e; hardwareDetails.appendChild(span); }); } // renderCalculation() const renderDetails = (form) => { const calculations = document.querySelector("#status-quo-calculations-costs"); const breakdownContainers = Array.from(calculations.querySelectorAll(".status-quo-breakdown-container")); const breakdown = hotel.getStatusQuoBreakdown(); const breakdownFields = document.createElement("div"); breakdownFields.classList.add("calculations-breakdown"); const fillForm = (fields, identifier) => { const inputs = []; const fixedIdentifier = identifier.toLowerCase().split(' ').map(word => word.charAt(0).toUpperCase() + word.substring(1)).join(' '); for (const field in fields){ const input = document.createElement("input"); input.setAttribute("type", "hidden"); input.setAttribute("name", `${fixedIdentifier.replace(/\s/g, "")}-${field}`); input.setAttribute("id", `${fixedIdentifier.replace(/\s/g, "")}-${field}`); input.setAttribute("value", fields[field].innerText); inputs.push(input); } return inputs; } for (el of breakdown){ const outputContainer = breakdownContainers.find((container) => container.getAttribute("data-type") === el.type); if (outputContainer) { const breakdownOutput = { label : outputContainer.querySelector("h5"), description: outputContainer.querySelector(".status-quo-cost-label"), calculation : outputContainer.querySelector(".status-quo-breakdown"), } const identifier = outputContainer.getAttribute("data-type"); breakdownOutput.calculation.innerHTML = ""; breakdownOutput.calculation.innerText = `${el.calculation} = ${el.amount}`; fillForm(breakdownOutput, identifier).forEach((i => { breakdownFields.appendChild(i); })); }; } if (form) { const submit = form.querySelector('input[type=submit'); form.insertBefore(breakdownFields, submit); } return breakdown; } // renderDetails() // displays assumptions const displayAssumptions = () => { const assumptions = document.getElementById("assumptions"); assumptions.classList.add("roi-visible"); } // triggers const calculateROIbtn = document.getElementById("calculateBtn"); const reRunCalculations = document.getElementById("recalculateROI"); calculateROIbtn.addEventListener("click", () => { renderCalculation(); }); reRunCalculations.addEventListener("click", () => { renderCalculation(); }); const customizeAssumptions = document.getElementById("viewAssumptionsLink"); customizeAssumptions.addEventListener("click", () => { displayAssumptions(); }); const showCalculations = document.getElementById("show-calculations"); showCalculations.addEventListener("click", () => { const showDetails = document.getElementById("calculations-container"); showDetails.classList.add("details-visible"); }) // functions calls renderInputs();