From c4b922e6070e52fb858a8dc2cee1ef2acef9e411 Mon Sep 17 00:00:00 2001 From: Tyler Sharp Date: Wed, 16 Nov 2022 23:25:15 -0500 Subject: [PATCH 1/4] DOM structure and rest of project structure in place --- septa-fare-calculator/favicon.ico | Bin 0 -> 3230 bytes septa-fare-calculator/index.html | 80 ++++++++++++++++-- septa-fare-calculator/src/css/index.css | 20 +++++ .../{ => src/data}/fares.json | 0 septa-fare-calculator/src/html/component.html | 0 .../src/js/FareCalculator.js | 15 ++++ septa-fare-calculator/src/js/index.js | 31 +++++++ 7 files changed, 138 insertions(+), 8 deletions(-) create mode 100644 septa-fare-calculator/favicon.ico create mode 100644 septa-fare-calculator/src/css/index.css rename septa-fare-calculator/{ => src/data}/fares.json (100%) create mode 100644 septa-fare-calculator/src/html/component.html create mode 100644 septa-fare-calculator/src/js/FareCalculator.js create mode 100644 septa-fare-calculator/src/js/index.js diff --git a/septa-fare-calculator/favicon.ico b/septa-fare-calculator/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..3954048efe273da645cacf96f0fdb8f927bb7d64 GIT binary patch literal 3230 zcmcJSTWl0%6vxMRpN&z$CX|A~7?h%j;th(4O2I&hi1Z>QBpA7s77QdlCrV9 z`fnRw&3V2vrlGfA#UBIN>i4P$M>WeFp__wf`SJs}D(0iU^qFj}dm4?qR$};aD>N-- z`K{=~fG>r$Rbf20O2)#~RxMg1WAi6bcyFZVQIm1B9oULPZ$5&<#nWw7x9lR>JT7%fb_V=;~2XR35_2q9A6!J|VW}#dl>gY%ut}|ADW^f?~#YG*mB7=Vr=2H_u;C@XFe>4fo4GR#Kq*%zm47bY(t%tXu%k z&)-3bM@1pf$C|qoyqvLt<3Bb3DK9AJ++SDYR!=)_^}4gw=SKIht>|m%L^2}V@^6o7 zShFpRnY1RR#!u&DN9|6>HFvrth4s7RRCBC##carkU(?qduY-Guzx$hNBtqlnsVvtG z=aX3VVHm$tZ0(NGfQGGmqdNCq`0j%{h#jz-7LBb*lx%ipj}d$OXA|tH^4q`Kj<{^k zKHIM7?B-5;zy0GHs`gpgx%t!4eqbB1{kDGp=2ozp{65#=zz%At{ydv)&!_rJdET~v z3*x~sTQ=`$n?8;>KJNgrcYZ-^Q_W?rAyNJvdh1b~p*=1ZxMRzf!x~ODC2{mr0! - - - SEPTA Regional Rail Fare Calculator - - - - - \ No newline at end of file + + + + + SEPTA Regional Rail Fare Calculator + + + + +
+
Regional Rail Fares
+
+ + +
+
+ + +
+
+ Where will you purchase the fare? +
+ + + + +
+
+
+ + +
+
+
+
+ +
+ + diff --git a/septa-fare-calculator/src/css/index.css b/septa-fare-calculator/src/css/index.css new file mode 100644 index 000000000..07eba4f6e --- /dev/null +++ b/septa-fare-calculator/src/css/index.css @@ -0,0 +1,20 @@ +.widget-body { + border: 2px solid lightgrey; + width: 50%; + margin: auto; +} + +.widget-body, +.widget-section, +.widget-section div { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} + +.widget-section { + border-bottom: 1px solid lightgrey; + width: 100%; + margin: 2em; +} diff --git a/septa-fare-calculator/fares.json b/septa-fare-calculator/src/data/fares.json similarity index 100% rename from septa-fare-calculator/fares.json rename to septa-fare-calculator/src/data/fares.json diff --git a/septa-fare-calculator/src/html/component.html b/septa-fare-calculator/src/html/component.html new file mode 100644 index 000000000..e69de29bb diff --git a/septa-fare-calculator/src/js/FareCalculator.js b/septa-fare-calculator/src/js/FareCalculator.js new file mode 100644 index 000000000..e91e262aa --- /dev/null +++ b/septa-fare-calculator/src/js/FareCalculator.js @@ -0,0 +1,15 @@ +class FareCalculator { + constructor( + whereGoing, + whenRiding, + purchasedAtOnboard, + numberOfRides, + fareZoneInfo + ) { + this.whereGoing = whereGoing || "Zone 4"; + this.whenRiding = whenRiding || "Weekdays"; + this.purchaseAtOnboard = purchasedAtOnboard || false; + this.numberOfRides = numberOfRides || 1; + this.fareZoneInfo = fareZoneInfo || {}; + } +} diff --git a/septa-fare-calculator/src/js/index.js b/septa-fare-calculator/src/js/index.js new file mode 100644 index 000000000..280ba7d38 --- /dev/null +++ b/septa-fare-calculator/src/js/index.js @@ -0,0 +1,31 @@ +let fareInfo = {}; +let fareZones = {}; + +//SETUP +const getFareData = async () => { + const response = await fetch("./src/data/fares.json"); + + if (response.ok) { + const data = response.json(); + return data; + } else { + throw new Error(`Error (Code: ${response.status}) ${response.statusText}`); + } +}; + +const setFareData = (fareData) => { + fareInfo = fareData.info; + fareZones = fareData.zones; +}; + +const onLoad = async () => { + const fareData = await getFareData().catch((error) => error); + + setFareData(fareData); + + console.log(fareInfo, fareZones); +}; + +//EVENTS + +//HELPERS From 3588160c4c5d7fb92454c978e68884384fec4704 Mon Sep 17 00:00:00 2001 From: Tyler Sharp Date: Thu, 17 Nov 2022 02:31:05 -0500 Subject: [PATCH 2/4] Mostly working, just need CSS --- septa-fare-calculator/index.html | 41 +++++-- .../src/js/FareCalculator.js | 63 ++++++++++- septa-fare-calculator/src/js/index.js | 106 +++++++++++++++++- 3 files changed, 190 insertions(+), 20 deletions(-) diff --git a/septa-fare-calculator/index.html b/septa-fare-calculator/index.html index 14927e6a6..4474693d3 100644 --- a/septa-fare-calculator/index.html +++ b/septa-fare-calculator/index.html @@ -12,35 +12,38 @@ -
-
Regional Rail Fares
+ +
+
Regional Rail Fares
- - - +
- - + +
- Where will you purchase the fare? + Where will you purchase the fare?
@@ -62,12 +67,26 @@ id="num-rides-input" name="number of rides" type="number" - value="1" + value="4" + min="1" + max="20" + autocomplete="off" />
-
+
+ Your fare will cost + $0.00 +
+
diff --git a/septa-fare-calculator/src/js/FareCalculator.js b/septa-fare-calculator/src/js/FareCalculator.js index e91e262aa..65c6ddf7b 100644 --- a/septa-fare-calculator/src/js/FareCalculator.js +++ b/septa-fare-calculator/src/js/FareCalculator.js @@ -6,10 +6,63 @@ class FareCalculator { numberOfRides, fareZoneInfo ) { - this.whereGoing = whereGoing || "Zone 4"; - this.whenRiding = whenRiding || "Weekdays"; - this.purchaseAtOnboard = purchasedAtOnboard || false; - this.numberOfRides = numberOfRides || 1; - this.fareZoneInfo = fareZoneInfo || {}; + this.whereGoing = parseInt(whereGoing.split(" ")[1]) || 4; + this.whenRiding = + !whenRiding === "anytime" || !whenRiding === "weekday" + ? "evening_weekend" + : whenRiding; + this.purchasedAtOnboard = purchasedAtOnboard + ? "onboard_purchase" + : "advance_purchase"; + this.numberOfRides = numberOfRides || 4; + this.fareZoneInfo = fareZoneInfo || []; + } + + setWhereGoing(str) { + this.whereGoing = parseInt(str.split(" ")[1]); + } + + setWhenRiding(str) { + const whenRiding = + str === "anytime" || str === "weekday" ? str : "evening_weekend"; + console.log(str); + this.whenRiding = whenRiding; + } + + setPurchasedAtOnboard(boolean) { + const purchasedAtOnboard = boolean + ? "onboard_purchase" + : "advance_purchase"; + this.purchasedAtOnboard = purchasedAtOnboard; + } + + setNumberOfRides(num) { + this.numberOfRides = num; + } + + calculateFare() { + let price; + const fareZone = this.fareZoneInfo.find((z) => z.zone === this.whereGoing); + + //because everything except anytime and weekday share same pricing, this parses the user's preference to coincide with the json. + //however, we kept the values as user-friendly labels in the html for screen readers + const fareType = fareZone.fares.filter((fare) => { + return fare.type === this.whenRiding; + }); + + //we can end the function early if "anytime" is selected, because that's all the info we need to get the user's price - only one anytime price exists per zone + if (this.whenRiding === "anytime") { + price = fareType[0].price; + + return price; + } + + const farePurchase = fareType.find( + (fare) => fare.purchase === this.purchasedAtOnboard + ); + + price = farePurchase.price; + + return price; } } diff --git a/septa-fare-calculator/src/js/index.js b/septa-fare-calculator/src/js/index.js index 280ba7d38..dbbffbc52 100644 --- a/septa-fare-calculator/src/js/index.js +++ b/septa-fare-calculator/src/js/index.js @@ -1,5 +1,14 @@ +//Could have broken things up into more classes, but for the scope of this assignment didn't seem necessary. + let fareInfo = {}; -let fareZones = {}; +let fareZones = []; + +let fareDefaults = { + whereGoing: "Zone 4", + whenRiding: "weekday", + purchasedAtOnboard: false, + numberOfRides: 4, +}; //SETUP const getFareData = async () => { @@ -18,14 +27,103 @@ const setFareData = (fareData) => { fareZones = fareData.zones; }; +const { whereGoing, whenRiding, purchasedAtOnboard, numberOfRides } = + fareDefaults; + +let WidgetFareCalculator = new FareCalculator( + whereGoing, + whenRiding, + purchasedAtOnboard, + numberOfRides +); + const onLoad = async () => { const fareData = await getFareData().catch((error) => error); - setFareData(fareData); + await setFareData(fareData); + + WidgetFareCalculator.fareZoneInfo = fareZones; +}; + +//EVENT HANDLERS +const handleWhereGoingSelectChange = (e) => { + e.preventDefault(); + + const whereGoingValue = e.target.value; + WidgetFareCalculator.setWhereGoing(whereGoingValue); + displayPrice(); +}; + +const handleWhenRidingSelectChange = (e) => { + //anytime tickets require at least ten rides and adv purchase, so if anytime is selected, it will force it to be on 10 and kiosk if it isn't so. + e.preventDefault(); + + const whenRidingValue = e.target.value; + if (numRidesInput.value < 10 && whenRidingValue == "anytime") { + numRidesInput.value = 10; + } + WidgetFareCalculator.setWhenRiding(whenRidingValue); + displayPrice(); +}; + +const handleKioskOrOnboardButtonsClick = (e) => { + const purchasedAtOnboardBoolean = e.target.value === "kiosk" ? false : true; + WidgetFareCalculator.setPurchasedAtOnboard(purchasedAtOnboardBoolean); + displayPrice(); +}; + +const handleNumRidesInputChange = (e) => { + const numRidesInputValue = e.target.value; + + //because anytime tickets start at 10 rides and adv purchase, if it is above 10, it will default to anytime and kiosk for customer convenience + //also disables the input for onboarding purchase after 10, since those must be purchased in advance + if (numRidesInputValue >= 10) { + whenRidingSelect.value = "anytime"; + kioskOrOnboardButtons[0].checked = true; + kioskOrOnboardButtons[1].disabled = true; + } + + if (numRidesInputValue < 10) { + kioskOrOnboardButtons[1].disabled = false; + } - console.log(fareInfo, fareZones); + WidgetFareCalculator.setNumberOfRides(numRidesInputValue); + displayPrice(); }; -//EVENTS +//EVENT LISTENERS +const whereGoingSelect = document.getElementById("where-going-select"); +const whenRidingSelect = document.getElementById("when-riding-select"); +const kioskOrOnboardButtons = document.getElementsByClassName( + "kioskOrOnboardRadio" +); +const numRidesInput = document.getElementById("num-rides-input"); + +whereGoingSelect.addEventListener("change", (e) => { + handleWhereGoingSelectChange(e); +}); + +whenRidingSelect.addEventListener("change", (e) => { + handleWhenRidingSelectChange(e); +}); + +for (let i = 0; i < kioskOrOnboardButtons.length; i++) { + kioskOrOnboardButtons[i].addEventListener("click", (e) => { + handleKioskOrOnboardButtonsClick(e); + }); +} + +numRidesInput.addEventListener("change", (e) => { + handleNumRidesInputChange(e); +}); //HELPERS +const displayPrice = () => { + const price = WidgetFareCalculator.calculateFare(); + + const displayPriceSpan = document.getElementsByClassName( + "price-calc-output-number" + )[0]; + + displayPriceSpan.innerHTML = `$${price.toFixed(2)}`; +}; From 7e08c81b884c602c03b4951ee55f9d36fc1f2014 Mon Sep 17 00:00:00 2001 From: Tyler Sharp Date: Thu, 17 Nov 2022 03:08:27 -0500 Subject: [PATCH 3/4] added helper text and css skeleton. --- septa-fare-calculator/index.html | 13 ++++++++++- septa-fare-calculator/src/css/index.css | 7 +++++- .../src/js/FareCalculator.js | 1 - septa-fare-calculator/src/js/index.js | 22 ++++++++++++++++++- 4 files changed, 39 insertions(+), 4 deletions(-) diff --git a/septa-fare-calculator/index.html b/septa-fare-calculator/index.html index 4474693d3..30e5c32de 100644 --- a/septa-fare-calculator/index.html +++ b/septa-fare-calculator/index.html @@ -14,7 +14,12 @@
-
Regional Rail Fares
+
+ The logo of SEPTA Regional Rail, two opposite arrows dividing a red and blue backgroundRegional Rail Fares +
+
Where will you purchase the fare? diff --git a/septa-fare-calculator/src/css/index.css b/septa-fare-calculator/src/css/index.css index 07eba4f6e..513b14912 100644 --- a/septa-fare-calculator/src/css/index.css +++ b/septa-fare-calculator/src/css/index.css @@ -1,6 +1,11 @@ +body { + height: 100vh; + display: flex; +} + .widget-body { border: 2px solid lightgrey; - width: 50%; + width: 20%; margin: auto; } diff --git a/septa-fare-calculator/src/js/FareCalculator.js b/septa-fare-calculator/src/js/FareCalculator.js index 65c6ddf7b..2ed6613a0 100644 --- a/septa-fare-calculator/src/js/FareCalculator.js +++ b/septa-fare-calculator/src/js/FareCalculator.js @@ -25,7 +25,6 @@ class FareCalculator { setWhenRiding(str) { const whenRiding = str === "anytime" || str === "weekday" ? str : "evening_weekend"; - console.log(str); this.whenRiding = whenRiding; } diff --git a/septa-fare-calculator/src/js/index.js b/septa-fare-calculator/src/js/index.js index dbbffbc52..522172cdb 100644 --- a/septa-fare-calculator/src/js/index.js +++ b/septa-fare-calculator/src/js/index.js @@ -64,12 +64,14 @@ const handleWhenRidingSelectChange = (e) => { } WidgetFareCalculator.setWhenRiding(whenRidingValue); displayPrice(); + displayHelperText(false); }; const handleKioskOrOnboardButtonsClick = (e) => { const purchasedAtOnboardBoolean = e.target.value === "kiosk" ? false : true; WidgetFareCalculator.setPurchasedAtOnboard(purchasedAtOnboardBoolean); displayPrice(); + displayHelperText(true); }; const handleNumRidesInputChange = (e) => { @@ -117,7 +119,7 @@ numRidesInput.addEventListener("change", (e) => { handleNumRidesInputChange(e); }); -//HELPERS +//UTILITY const displayPrice = () => { const price = WidgetFareCalculator.calculateFare(); @@ -127,3 +129,21 @@ const displayPrice = () => { displayPriceSpan.innerHTML = `$${price.toFixed(2)}`; }; + +const displayHelperText = (purchasedOrWhenRidingBoolean) => { + const helperTextSpan = document.getElementById("when-riding-helper-text"); + + console.log(helperTextSpan); + + const isAskingWherePurchased = purchasedOrWhenRidingBoolean; + + if (isAskingWherePurchased) { + const helperText = fareInfo[WidgetFareCalculator.purchasedAtOnboard]; + + helperTextSpan.innerHTML = helperText; + } else { + const helperText = fareInfo[WidgetFareCalculator.whenRiding]; + + helperTextSpan.innerHTML = helperText; + } +}; From a44b63bb07f3b01a9c6a11c37f14cccd6f248225 Mon Sep 17 00:00:00 2001 From: Tyler Sharp Date: Thu, 17 Nov 2022 21:58:46 -0500 Subject: [PATCH 4/4] Complete. Minor bugs, but no time. --- septa-fare-calculator/index.html | 33 +++--- septa-fare-calculator/src/css/index.css | 106 +++++++++++++++++- .../src/js/FareCalculator.js | 4 +- septa-fare-calculator/src/js/index.js | 14 ++- 4 files changed, 136 insertions(+), 21 deletions(-) diff --git a/septa-fare-calculator/index.html b/septa-fare-calculator/index.html index 30e5c32de..40b4d0e3a 100644 --- a/septa-fare-calculator/index.html +++ b/septa-fare-calculator/index.html @@ -3,10 +3,7 @@ - + SEPTA Regional Rail Fare Calculator @@ -14,7 +11,7 @@
-
+
The logo of SEPTA Regional Rail, two opposite arrows dividing a red and blue background + >Valid Monday through Friday, 4:00 a.m. - 7:00 p.m. On trains arriving + or departing 30th Street Station, Suburban and Jefferson Station
Where will you purchase the fare? -
- +
+ - +
@@ -80,7 +85,7 @@ type="number" value="4" min="1" - max="20" + max="10" autocomplete="off" /> @@ -93,7 +98,7 @@ class="price-calc-output-number" name="price of fare" aria-labelledby="fare-cost" - >$0.00$28.00 diff --git a/septa-fare-calculator/src/css/index.css b/septa-fare-calculator/src/css/index.css index 513b14912..385b5decc 100644 --- a/septa-fare-calculator/src/css/index.css +++ b/septa-fare-calculator/src/css/index.css @@ -5,7 +5,7 @@ body { .widget-body { border: 2px solid lightgrey; - width: 20%; + width: 100%; margin: auto; } @@ -14,12 +14,112 @@ body { .widget-section div { display: flex; flex-direction: column; - justify-content: center; + justify-content: space-around; align-items: center; } .widget-section { border-bottom: 1px solid lightgrey; width: 100%; - margin: 2em; + height: 20vh; +} + +.widget-section input[type="number"], +.widget-section select { + font-size: 1.25em; +} + +.kioskOrOnboardContainer-label { + font-size: 1.5em; + margin: 5px; + color: grey; +} + +.widget-section select, +.widget-section input[type="number"] { + background: none; + text-align: left; + border: 1px solid lightgrey; + border-radius: 10px; + width: 75%; + padding: 0.5em; + color: grey; +} + +.widget-section input[type="number"] { + text-align: center; + color: black; +} + +.widget-section > label, +.widget-section > span:first-child { + margin: 0.5rem; + font-size: 1.25em; + font-family: Verdana, Geneva, Tahoma, sans-serif; + color: grey; +} + +.widget-header { + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + width: 100%; + font-size: 1.5em; + background-color: grey; + color: white; + height: 2em; + font-family: Verdana, Geneva, Tahoma, sans-serif; + font-weight: bold; +} + +.widget-header img { + margin-right: 20px; +} + +.when-riding-helper { + color: grey; + font-size: 0.8rem; + width: 80%; + text-align: center; +} + +.kioskOrOnboardContainer input { + display: none; +} + +.kioskOrOnboardContainer-label { + padding: 5px; + border: 1px solid lightgrey; + border-radius: 10px; + width: 100%; + text-align: center; +} + +.checked-label { + background: grey; + color: white; + border: 2px solid grey; +} + +input:hover, +label:hover { + cursor: pointer; +} + +#price-calc { + font-size: 1.25em; + background: gray; + color: white; +} + +.price-calc-output-number { + font-size: 3em; +} + +@media screen and (min-width: 600px) { + body { + width: 30%; + margin: auto; + } } diff --git a/septa-fare-calculator/src/js/FareCalculator.js b/septa-fare-calculator/src/js/FareCalculator.js index 2ed6613a0..32b3c91b5 100644 --- a/septa-fare-calculator/src/js/FareCalculator.js +++ b/septa-fare-calculator/src/js/FareCalculator.js @@ -61,7 +61,7 @@ class FareCalculator { ); price = farePurchase.price; - - return price; + if (this.numberOfRides > 9) return price; + return price * this.numberOfRides; } } diff --git a/septa-fare-calculator/src/js/index.js b/septa-fare-calculator/src/js/index.js index 522172cdb..7d1521d4a 100644 --- a/septa-fare-calculator/src/js/index.js +++ b/septa-fare-calculator/src/js/index.js @@ -70,10 +70,21 @@ const handleWhenRidingSelectChange = (e) => { const handleKioskOrOnboardButtonsClick = (e) => { const purchasedAtOnboardBoolean = e.target.value === "kiosk" ? false : true; WidgetFareCalculator.setPurchasedAtOnboard(purchasedAtOnboardBoolean); + displayPrice(); displayHelperText(true); }; +const handleKioskOrOnboardButtonsLabelChange = (e) => { + const currentBtn = e.target.labels[0]; + const prevBtn = document.querySelector(".checked-label"); + + console.log(currentBtn); + + prevBtn.classList.remove("checked-label"); + currentBtn.classList.add("checked-label"); +}; + const handleNumRidesInputChange = (e) => { const numRidesInputValue = e.target.value; @@ -112,6 +123,7 @@ whenRidingSelect.addEventListener("change", (e) => { for (let i = 0; i < kioskOrOnboardButtons.length; i++) { kioskOrOnboardButtons[i].addEventListener("click", (e) => { handleKioskOrOnboardButtonsClick(e); + handleKioskOrOnboardButtonsLabelChange(e); }); } @@ -133,8 +145,6 @@ const displayPrice = () => { const displayHelperText = (purchasedOrWhenRidingBoolean) => { const helperTextSpan = document.getElementById("when-riding-helper-text"); - console.log(helperTextSpan); - const isAskingWherePurchased = purchasedOrWhenRidingBoolean; if (isAskingWherePurchased) {