Case Solution- Part Detail (From Design to Database with Firestore)
This supports a software dev. class for businesspeople that you can find here: https://www.alexandercowan.com/making-html-manageable And a case you can find here: https://www.alexandercowan.com/making-html-manageable
by Alex Cowan
HTML
<script src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/3.6.0/jasmine.min.js"></script>
<!-- The core Firebase JS SDK is always required and must be listed first -->
<script src="https://www.gstatic.com/firebasejs/8.2.5/firebase-app.js"></script>
<!-- TODO: Add SDKs for Firebase products that you want to use
https://firebase.google.com/docs/web/setup#available-libraries -->
<script src="https://www.gstatic.com/firebasejs/8.2.1/firebase-auth.js"></script>
<script src="https://www.gstatic.com/firebasejs/8.2.5/firebase-firestore.js"></script>
<script src="https://www.gstatic.com/firebasejs/8.2.5/firebase-analytics.js"></script>
<script>
// Your web app's Firebase configuration
// For Firebase JS SDK v7.20.0 and later, measurementId is optional
var firebaseConfig = {
apiKey: "AIzaSyCOPgDQ4PAwNtOxZQtkkRpy0LV37VVEzJg",
authDomain: "hinh-78995.firebaseapp.com",
projectId: "hinh-78995",
storageBucket: "hinh-78995.appspot.com",
messagingSenderId: "389014950808",
appId: "1:389014950808:web:5599635662f42697b091ed",
measurementId: "G-XL6BSRZLQE",
}
// Initialize Firebase
firebase.initializeApp(firebaseConfig)
firebase.analytics()
</script>
<link
rel="stylesheet"
href="//netdna.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css"
/>
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"
/>
<div class="top-bar">
<ul class="nav-list">
<li><a href="#">Home</a></li>
<li><a href="#">Parts</a></li>
<li><a href="#">Search</a></li>
<li><a href="#">Help</a></li>
</ul>
<div id="search-box">
<input
type="text"
name="parts-search"
placeholder="Mfg., Model, or Part Number"
size="30"
/>
</div>
</div>
<div id="part-info">
<img id="part-img" alt="PCB board" style="width: 300px" />
<h2 id="part-title"></h2>
<button type="button" id="order-btn">Add to Cart</button
><button type="button"...
CSS
.top-bar,
#search-box,
.nav-list li,
.nav-list a {
background-color: #002d45;
font-family: "Proxima Nova", "helvetica neue", helvetica, sans-serif;
font-weight: bold;
font-size: 110%;
color: #ffffff;
}
.top-bar {
height: 40px;
margin: 0;
padding: 10px;
display: flex;
align-items: center;
}
.nav-list {
list-style-type: none;
padding: 0;
display: inline;
}
.nav-list li {
float: left;
}
.nav-list a {
display: inline;
padding: 8px;
text-decoration: none;
}
#search-box {
margin-left: auto;
margin-right: 0;
padding: 10px;
}
p {
font-family: "Lucida Sans Unicode", "Lucida Grande", sans-serif;
overflow: scroll;
margin: 0;
}
#part-info {
margin: 15px;
padding: 0 5px;
width: 100%;
}
#part-info img {
float: left;
padding: 5px;
}
#part-info h2 {
display: inline;
}
button {
display: inline;
font-size: 20px;
margin: 0 5px 5px 0;
}
#review-signifier {
direction: rtl;
unicode-bidi: bidi-override;
float: left;
}
#part-info h3 {
margin: 0;
}
input.star {
display: none;
}
label.star {
padding: 0;
font-size: 15px;
color: #444;
}
input.star:checked ~ label.star:before {
content: "\f005";
color: #000;
transition: all 0.25s;
}
label.star:before {
content: "\f006";
font-family: FontAwesome;
}
.fa-star {
opacity: 0.5;
}
.checked {
color: black;
opacity: 1;
}
#user-reviews {
clear: both;
}
#mod-link {
display: none;
}
JavaScript
// Creates an instance of the DB using the Firebase SDK
var db = firebase.firestore();
// Get just the part number that the user clickied on in the parts search page. Splits on '#' and takes the second element.
var partnum = localStorage.getItem("part-index").split("#", 2)[1];
console.log("incoming with part number " + partnum);
// Define the file path for where the part data lives
var part_file_path = "parts/" + partnum;
// Go get the part data from the applicable document in Firestore
db.doc(part_file_path)
.get()
.then(function (docSnapshot) {
var part = docSnapshot.data();
var id = docSnapshot.id;
// Render the specific part information on the page
document.getElementById("partnum").textContent = "Part #: " + id;
document.getElementById("availability").textContent =
"Availability: " + part.Availability;
document.getElementById("orders").textContent =
"Orders (90 day): " + part.Orders;
document.getElementById("price").textContent =
"Price: $" + parseFloat(part.Price).toFixed(2);
document.getElementById("part-title").textContent =
part.Manufacturer + " " + part.Model + " " + part.Type;
document
.getElementById("part-img")
.setAttribute("src", "https://" + part.imageURL);
// Initialize the stars and number of ratings
var review_file_path = "parts/" + partnum + "/reviews";
postReviews(partnum, "reviewsTargetID");
});
function postReviews(partNum, containerId = null) {
let totalRating = 0;
let numReviews = 0;
const reviewFilePath = "parts/" + partNum + "/reviews";
return db
.collection(reviewFilePath)
.get()
.then((querySnapshot) => {
querySnapshot.forEach((doc) => {
totalRating += doc.data().rating;
numReviews++;
});
return { totalRating, numReviews };
})
.then(({ totalRating, numReviews }) => {
const avgRating = totalRating / numReviews;
const roundedRating = Math.floor(avgRating);
...