From 7700bffd38750562f7e356659ff69fa2c30f74a7 Mon Sep 17 00:00:00 2001 From: Samm Du Date: Tue, 7 Dec 2021 22:19:24 -0500 Subject: [PATCH 1/8] fix duplicate code --- js/discover.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/js/discover.js b/js/discover.js index 3e5a1b2..af01b63 100644 --- a/js/discover.js +++ b/js/discover.js @@ -128,17 +128,15 @@ function toggleSortOrder() { const sortIcon = document.getElementById('sortIcon'); if (fetchQueryParamByKey('sort_asc') == 'false') { // for ascending order, flip icon vertically - setPairInQuery('sort_asc', 'true'); sortIcon.style.transform = 'scaleY(-1)'; // configure the search params to set ascending search order to true - setPairInQuery('sort_asc', true); + setPairInQuery('sort_asc', 'true'); } else { // for ascending order, do not flip icon - setPairInQuery('sort_asc', 'false'); sortIcon.style.transform = 'none'; // configure the search params to set ascending search order to false - setPairInQuery('sort_asc', false); + setPairInQuery('sort_asc', 'false'); } } From a073aaeb8b48c273d79a22a0b8b9d462a73f13a2 Mon Sep 17 00:00:00 2001 From: Samm Du Date: Wed, 8 Dec 2021 03:31:36 -0500 Subject: [PATCH 2/8] basic layout for details page --- css/details.css | 74 +++++++++++++++++++++++++++++++++++++---- css/index.css | 1 + css/main.css | 2 ++ details.html | 81 +++++++++++++++++++++++++++++++++++++-------- index.html | 2 +- js/details.js | 88 +++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 227 insertions(+), 21 deletions(-) create mode 100644 js/details.js diff --git a/css/details.css b/css/details.css index 5cd5c7b..37ae795 100644 --- a/css/details.css +++ b/css/details.css @@ -84,6 +84,11 @@ header img { main > nav { flex-grow: 0; padding: 1rem 2rem; + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: space-between; + align-items: flex-start; } main > div { @@ -94,24 +99,79 @@ main > div { overflow: hidden; } +/* claimed loan offers sidebar styles */ + main > div aside { flex-grow: 1; max-width: 20rem; - max-height: 60rem; + max-height: 40rem; overflow-y: auto; + overscroll-behavior-y: contain; } +/* loan offer detail display styling */ + main > div section { flex-grow: 2; border-radius: 0 0 1.1rem 0; - padding: 1rem; + padding: 2rem; max-width: 75%; min-height: 30rem; - /* overflow-y: auto; */ + display: flex; + flex-direction: column; + justify-content: flex-start; + align-items: flex-start; + row-gap: 2rem; } -main > div section div { - word-wrap: anywhere; - max-height: 100%; - overflow-y: auto; +main > div section div.car-hero { + width: 100%; + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + height: 15rem; +} + +main > div section div.car-hero img { + height: 100%; + width: auto; + border-radius: 1rem; +} + +main > div section div.loan-offer-details { + display: flex; + flex-direction: column; + row-gap: 1rem; +} + +main > div section div.loan-offer-details h4 { + font-weight: 500; + color: #faa500; +} + +div.loan-offer-details ul { + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: flex-start; + align-items: flex-start; + column-gap: 1rem; + row-gap: 1rem; +} + +div.loan-offer-details ul li span { + font-weight: 500; + font-size: 1.2rem; +} + +main > div section div.loan-offer-details input { + border-color: #ffffff; + margin: 0.5rem 0.5rem 0.5rem 0; +} + +main > div section div.loan-offer-details button { + height: 2.4rem; + padding: 0.25em 1.5em; + border-radius: 1.2rem; } diff --git a/css/index.css b/css/index.css index e366aaf..47b9b7f 100644 --- a/css/index.css +++ b/css/index.css @@ -147,6 +147,7 @@ section.sec-claimed-offers header img { section.sec-claimed-offers ul.loan-offers { max-height: 18rem; overflow-y: auto; + overscroll-behavior-y: contain; } section.sec-claimed-offers a.round-empty-pill { diff --git a/css/main.css b/css/main.css index 779e8a1..4d7fa28 100644 --- a/css/main.css +++ b/css/main.css @@ -72,6 +72,8 @@ button { border: none; display: inline-flex; justify-content: center; + align-items: center; + transition: all 250ms ease; } button:hover { diff --git a/details.html b/details.html index 3dba2f9..eedfec2 100644 --- a/details.html +++ b/details.html @@ -29,8 +29,9 @@ - + +
@@ -49,21 +50,62 @@

Your loan offer details

-
+
+ Car Hero Image +
+
+
    +
  • +

    Make:

    + {{make}} +
  • +
  • +

    Model:

    + {{model}} +
  • +
  • +

    Year:

    + {{year}} +
  • +
  • +

    Usage:

    + {{kms}} km +
  • +
  • +

    Retail price:

    + ${{price}} +
  • +
+
+ + + +
+
    +
  • +

    Interest rate:

    + {{apr}}% +
  • +
  • +

    You pay:

    + ${{payment_mo}}/mo +
  • +
  • +

    Loan term:

    + {{loan_term}} years +
  • +
  • +

    Total cost:

    + ${{total_sum}} +
  • +
+
@@ -108,5 +150,18 @@

Powered by:

+ + + diff --git a/index.html b/index.html index 574a4cd..42c9036 100644 --- a/index.html +++ b/index.html @@ -77,7 +77,7 @@

You need to:

Your loan offers

- - See all offer details + See all offer details
diff --git a/js/api.js b/js/api.js index b832035..380218c 100644 --- a/js/api.js +++ b/js/api.js @@ -44,6 +44,22 @@ var api = { // retrieve the returned cars/offers const results = await response.json(); + if (response.status == 200) { + return results; + } + else { + console.log(response); + throw "HTTP status: " + response.status; + } + }, + + getClaimedOffers: async function(userID) { + // send the search query to the backend API + const response = await fetch(this.API_URL + '/getClaimedOffers?user_id=' + userID); + + // retrieve the returned cars/offers + const results = await response.json(); + if (response.status == 200) { return results; } diff --git a/js/details.js b/js/details.js index c0d67a8..526a5cd 100644 --- a/js/details.js +++ b/js/details.js @@ -20,22 +20,55 @@ limitations under the License. */ async function onPageLoad() { // DEMO - for (let i = 0; i != 20; i++) { - addOfferToContainer( - "offer123", "Honda", "Civic", 2018, 250, 320, 3.2 - ); - } + // for (let i = 0; i != 20; i++) { + // addOfferToContainer( + // "offer123", "Honda", "Civic", 2018, 250, 320, 3.2 + // ); + // } + fetchClaimedOffers(); } /* Claimed offers list operations */ +/* + Get the list of offers for a specific user +*/ +async function fetchClaimedOffers() { + // fetch the user_id from the URL + let userID = fetchQueryParamByKey('user_id'); + + try { + // call the API to get the user's claimed offers + const userClaimedOffers = await api.getClaimedOffers(userID); + + // populate the sidebar with user's claimed offers + removeAllLoanOffers(); + for (const offer of userClaimedOffers) { + console.log(offer); + // DEMO + offer['brand'] = 'Honda'; + offer['model'] = 'Civic'; + offer['year'] = 2018; + + addOfferToContainer( + offer['offerId'], offer['brand'], offer['model'], offer['year'], + offer['interestRate'], offer['termMo'], offer['totalSum'] + ); + } + } + catch (e) { + console.log(e); + console.log(window.location.search); + } +} + /* Add a loan offer to the loan offers container */ function addOfferToContainer( - offer_id, make, model, year, payment_mo, loan_term, apr + offer_id, make, model, year, apr, loan_term, total_sum ) { // get render target let offersContainer = document.getElementById('loanOffersContainer'); @@ -49,9 +82,9 @@ function addOfferToContainer( make: make, model: model, year: year, - payment_mo: payment_mo, + payment_mo: Math.round((total_sum / loan_term) * 100) / 100, loan_term: loan_term, - apr: apr + apr: Math.round(apr * 100) / 100 }; const loanOfferRendered = Mustache.render(tmpl_LoanOffer, loanOfferData); @@ -69,7 +102,7 @@ function removeAllLoanOffers() { /* - .. + Offer details operations */ /* diff --git a/js/discover.js b/js/discover.js index 41e2d23..6a2aa90 100644 --- a/js/discover.js +++ b/js/discover.js @@ -60,6 +60,10 @@ async function userLogin() { setPairInQuery('user_id', userID); } + // set the link of the offers details page with userID + const offersDetailsLink = document.getElementById('offersDetailsLink'); + offersDetailsLink.href = './details.html?user_id=' + userID; + // try to log in the user and fetch their financial information try { // call the API to log in the user From 02078f2152ffcc05c7bad1713cfc8b9cdc275832 Mon Sep 17 00:00:00 2001 From: Samm Du Date: Thu, 9 Dec 2021 00:59:14 -0500 Subject: [PATCH 6/8] able to highlight selected offer --- css/details.css | 8 ++++++ details.html | 4 +-- index.html | 20 +++++++------ js/details.js | 75 ++++++++++++++++++++++--------------------------- js/discover.js | 5 ++++ js/main.js | 42 +++++++++++++++++++++++++++ 6 files changed, 102 insertions(+), 52 deletions(-) diff --git a/css/details.css b/css/details.css index 20e295e..557b465 100644 --- a/css/details.css +++ b/css/details.css @@ -149,6 +149,14 @@ main > div aside { } } +.offer-selected, .offer-selected > * { + background-color: #1d2345 !important; + color: #ffffff; +} +.offer-selected .offer-loan-info { + color: #3ee3ac !important; +} + /* loan offer detail display styling */ main > div section { diff --git a/details.html b/details.html index 65f2e44..a10bad1 100644 --- a/details.html +++ b/details.html @@ -153,8 +153,8 @@

Powered by:

+ diff --git a/js/details.js b/js/details.js index 526a5cd..667e8ef 100644 --- a/js/details.js +++ b/js/details.js @@ -44,19 +44,10 @@ async function fetchClaimedOffers() { const userClaimedOffers = await api.getClaimedOffers(userID); // populate the sidebar with user's claimed offers - removeAllLoanOffers(); - for (const offer of userClaimedOffers) { - console.log(offer); - // DEMO - offer['brand'] = 'Honda'; - offer['model'] = 'Civic'; - offer['year'] = 2018; - - addOfferToContainer( - offer['offerId'], offer['brand'], offer['model'], offer['year'], - offer['interestRate'], offer['termMo'], offer['totalSum'] - ); - } + displayClaimedOffers(userClaimedOffers, userID); + + // highlight the specified offer + highlightOffer(fetchQueryParamByKey('offerSelected')); } catch (e) { console.log(e); @@ -65,39 +56,41 @@ async function fetchClaimedOffers() { } /* - Add a loan offer to the loan offers container + Display the given list of claimed offers +*/ +function displayClaimedOffers(offers, userID) { + removeAllLoanOffers(); + for (const offer of offers) { + // DEMO + // console.log(offer); + offer['brand'] = 'Honda'; + offer['model'] = 'Civic'; + offer['year'] = 2018; + + addOfferToContainer( + userID, offer['offerId'], offer['brand'], offer['model'], offer['year'], + offer['interestRate'], offer['termMo'], offer['totalSum'] + ); + } +} + +/* + Highlight an offer (purely cosmetically) */ -function addOfferToContainer( - offer_id, make, model, year, apr, loan_term, total_sum -) { - // get render target - let offersContainer = document.getElementById('loanOffersContainer'); - - // get mustache template - const tmpl_LoanOffer = document.getElementById('tmpl_LoanOffer').innerHTML; - - // render loan offer info - const loanOfferData = { - offer_id: offer_id, - make: make, - model: model, - year: year, - payment_mo: Math.round((total_sum / loan_term) * 100) / 100, - loan_term: loan_term, - apr: Math.round(apr * 100) / 100 - }; - const loanOfferRendered = Mustache.render(tmpl_LoanOffer, loanOfferData); - - // append loan offer element to offers container - offersContainer.innerHTML += loanOfferRendered; +function highlightOffer(offer_id) { + // give the offer a "selected" style + const offerEntry = document.querySelector('li[name="' + offer_id +'"]'); + offerEntry.className += " offer-selected"; + // scroll the offer entry into view (if not already in-view) + offerEntry.scrollIntoView(false); } + /* - Remove all loan offers in the claimed offers container. + Highlight an offer and display its details */ -function removeAllLoanOffers() { - let carsContainer = document.getElementById('loanOffersContainer'); - carsContainer.innerHTML = ''; +async function showOfferDetails() { + } diff --git a/js/discover.js b/js/discover.js index 6a2aa90..c245d83 100644 --- a/js/discover.js +++ b/js/discover.js @@ -46,6 +46,11 @@ async function onPageLoad() { else { userLogin(); } + + // DEMO + addOfferToContainer( + "u1639018896514634", "offer123", "Honda", "Civic", 2018, 250, 320, 3.2 + ); } diff --git a/js/main.js b/js/main.js index 3e796fa..d3b2bf5 100644 --- a/js/main.js +++ b/js/main.js @@ -37,3 +37,45 @@ function setPairInQuery(key, value) { var new_params = '?' + url_params.toString(); window.history.replaceState(null, null, new_params); } + + +/* + Claimed offers list operations +*/ + +/* + Add a loan offer to the loan offers container +*/ +function addOfferToContainer( + user_id, offer_id, make, model, year, apr, loan_term, total_sum +) { + // get render target + let offersContainer = document.getElementById('loanOffersContainer'); + + // get mustache template + const tmpl_LoanOffer = document.getElementById('tmpl_LoanOffer').innerHTML; + + // render loan offer info + const loanOfferData = { + user_id: user_id, + offer_id: offer_id, + make: make, + model: model, + year: year, + payment_mo: Math.round((total_sum / loan_term) * 100) / 100, + loan_term: loan_term, + apr: Math.round(apr * 100) / 100 + }; + const loanOfferRendered = Mustache.render(tmpl_LoanOffer, loanOfferData); + + // append loan offer element to offers container + offersContainer.innerHTML += loanOfferRendered; +} + +/* + Remove all loan offers in the claimed offers container. +*/ +function removeAllLoanOffers() { + let carsContainer = document.getElementById('loanOffersContainer'); + carsContainer.innerHTML = ''; +} From 2fb52ba4e678aed6ff20287490b50da333e45a95 Mon Sep 17 00:00:00 2001 From: Samm Du Date: Thu, 9 Dec 2021 01:38:14 -0500 Subject: [PATCH 7/8] ability to fetch offer details added; details page basic functionality complete --- css/details.css | 2 +- details.html | 109 +++++++++++++++++++++++++----------------------- js/api.js | 18 ++++++++ js/details.js | 90 +++++++++++++++++++++++++++------------ 4 files changed, 139 insertions(+), 80 deletions(-) diff --git a/css/details.css b/css/details.css index 557b465..4af7b39 100644 --- a/css/details.css +++ b/css/details.css @@ -212,7 +212,7 @@ div.loan-offer-details ul { flex-wrap: wrap; justify-content: flex-start; align-items: flex-start; - column-gap: 1rem; + column-gap: 2rem; row-gap: 1rem; } diff --git a/details.html b/details.html index a10bad1..dfc8317 100644 --- a/details.html +++ b/details.html @@ -53,59 +53,7 @@

Your loan offer details

-
-
-
- Car Hero Image -
-
    -
  • -

    Make:

    - {{make}} -
  • -
  • -

    Model:

    - {{model}} -
  • -
  • -

    Year:

    - {{year}} -
  • -
  • -

    Usage:

    - {{kms}} km -
  • -
  • -

    Retail price:

    - ${{price}} -
  • -
-
- - - -
-
    -
  • -

    Interest rate:

    - {{apr}}% -
  • -
  • -

    You pay:

    - ${{payment_mo}}/mo -
  • -
  • -

    Loan term:

    - {{loan_term}} years -
  • -
  • -

    Total cost:

    - ${{total_sum}} -
  • -
-
+
@@ -163,5 +111,60 @@

Powered by:

+ + diff --git a/js/api.js b/js/api.js index 380218c..e793648 100644 --- a/js/api.js +++ b/js/api.js @@ -60,6 +60,24 @@ var api = { // retrieve the returned cars/offers const results = await response.json(); + if (response.status == 200) { + return results; + } + else { + console.log(response); + throw "HTTP status: " + response.status; + } + }, + + getOfferDetails: async function(userID, OfferID) { + // send the search query to the backend API + const response = await fetch( + this.API_URL + '/getOfferDetails?user_id=' + userID + '&offer_id=' + OfferID + ); + + // retrieve the returned cars/offers + const results = await response.json(); + if (response.status == 200) { return results; } diff --git a/js/details.js b/js/details.js index 667e8ef..d3fa409 100644 --- a/js/details.js +++ b/js/details.js @@ -19,13 +19,15 @@ limitations under the License. Always called when the details page is loaded. */ async function onPageLoad() { - // DEMO - // for (let i = 0; i != 20; i++) { - // addOfferToContainer( - // "offer123", "Honda", "Civic", 2018, 250, 320, 3.2 - // ); - // } - fetchClaimedOffers(); + // fetch the user_id from the URL + let userID = fetchQueryParamByKey('user_id'); + // fetch all claimed offers for this user + await fetchClaimedOffers(userID); + + // find the selected offer for this user + let offerSelected = fetchQueryParamByKey('offerSelected'); + // show details for the specified offer + await getOfferDetails(userID, offerSelected); } /* @@ -35,19 +37,13 @@ async function onPageLoad() { /* Get the list of offers for a specific user */ -async function fetchClaimedOffers() { - // fetch the user_id from the URL - let userID = fetchQueryParamByKey('user_id'); - +async function fetchClaimedOffers(userID) { try { // call the API to get the user's claimed offers const userClaimedOffers = await api.getClaimedOffers(userID); // populate the sidebar with user's claimed offers displayClaimedOffers(userClaimedOffers, userID); - - // highlight the specified offer - highlightOffer(fetchQueryParamByKey('offerSelected')); } catch (e) { console.log(e); @@ -58,17 +54,11 @@ async function fetchClaimedOffers() { /* Display the given list of claimed offers */ -function displayClaimedOffers(offers, userID) { +function displayClaimedOffers(offers, user_id) { removeAllLoanOffers(); for (const offer of offers) { - // DEMO - // console.log(offer); - offer['brand'] = 'Honda'; - offer['model'] = 'Civic'; - offer['year'] = 2018; - addOfferToContainer( - userID, offer['offerId'], offer['brand'], offer['model'], offer['year'], + user_id, offer['offerId'], offer['brand'], offer['model'], offer['year'], offer['interestRate'], offer['termMo'], offer['totalSum'] ); } @@ -87,16 +77,64 @@ function highlightOffer(offer_id) { /* - Highlight an offer and display its details + Offer details operations */ -async function showOfferDetails() { +/* + Fetch and display an offer's details based on the given offer ID +*/ +async function getOfferDetails(user_id, offer_id) { + // attempt to fetch the specified offer's details from the backend API + try { + // make the API call + let details = await api.getOfferDetails(user_id, offer_id); + + // highlight the chosen offer + highlightOffer(offer_id); + + // display the offer details + renderOfferDetails( + details['brand'], details['model'], details['year'], details['kms'], + details['price'], details['loanAmount'], details['interestRate'], details['termMo'], + details['totalSum'] + ) + } + catch (e) { + console.log(e); + console.log(window.location.search); + } } - /* - Offer details operations + Present a given offer's details */ +function renderOfferDetails( + make, model, year, kms, price, principal, apr, loan_term, total_sum +) { + // get render target + let offerDetailsContainer = document.getElementById('offerDetailsContainer'); + + // get mustache template + const tmpl_offerDetails = document.getElementById('tmpl_LoanDetails').innerHTML; + + // render loan offer info + const offerDetailsData = { + make: make, + model: model, + year: year, + kms: Math.round(kms), + price: Math.round(price * 100) / 100, + principal: principal, + apr: Math.round(apr * 100) / 100, + payment_mo: Math.round((total_sum / loan_term) * 100) / 100, + loan_term: loan_term, + total_sum: total_sum + }; + const offerDetailsRendered = Mustache.render(tmpl_offerDetails, offerDetailsData); + + // append loan offer element to offers container + offerDetailsContainer.innerHTML = offerDetailsRendered; +} /* Update the loan principal given user input From 7fdc37a406a0c741936e8acfc217f7f27a0f262d Mon Sep 17 00:00:00 2001 From: Samm Du Date: Thu, 9 Dec 2021 01:44:46 -0500 Subject: [PATCH 8/8] improve syntax --- js/details.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/js/details.js b/js/details.js index d3fa409..32c746b 100644 --- a/js/details.js +++ b/js/details.js @@ -54,11 +54,11 @@ async function fetchClaimedOffers(userID) { /* Display the given list of claimed offers */ -function displayClaimedOffers(offers, user_id) { +function displayClaimedOffers(offers, userID) { removeAllLoanOffers(); for (const offer of offers) { addOfferToContainer( - user_id, offer['offerId'], offer['brand'], offer['model'], offer['year'], + userID, offer['offerId'], offer['brand'], offer['model'], offer['year'], offer['interestRate'], offer['termMo'], offer['totalSum'] ); } @@ -67,9 +67,9 @@ function displayClaimedOffers(offers, user_id) { /* Highlight an offer (purely cosmetically) */ -function highlightOffer(offer_id) { +function highlightOffer(offerID) { // give the offer a "selected" style - const offerEntry = document.querySelector('li[name="' + offer_id +'"]'); + const offerEntry = document.querySelector('li[name="' + offerID +'"]'); offerEntry.className += " offer-selected"; // scroll the offer entry into view (if not already in-view) offerEntry.scrollIntoView(false); @@ -83,14 +83,14 @@ function highlightOffer(offer_id) { /* Fetch and display an offer's details based on the given offer ID */ -async function getOfferDetails(user_id, offer_id) { +async function getOfferDetails(userID, offerID) { // attempt to fetch the specified offer's details from the backend API try { // make the API call - let details = await api.getOfferDetails(user_id, offer_id); + let details = await api.getOfferDetails(userID, offerID); // highlight the chosen offer - highlightOffer(offer_id); + highlightOffer(offerID); // display the offer details renderOfferDetails(