JS Final Project
This commit is contained in:
BIN
INFO-1251 (Web Server)/Labs/Lab 5/McLean_Lab5.pdf
Normal file
BIN
INFO-1251 (Web Server)/Labs/Lab 5/McLean_Lab5.pdf
Normal file
Binary file not shown.
BIN
INFO-1272 (JS 1)/Projects/Final Project/Final Project.pdf
Normal file
BIN
INFO-1272 (JS 1)/Projects/Final Project/Final Project.pdf
Normal file
Binary file not shown.
116
INFO-1272 (JS 1)/Projects/Final Project/index.html
Normal file
116
INFO-1272 (JS 1)/Projects/Final Project/index.html
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Flowery Flower Shop</title>
|
||||||
|
<meta charset="utf-8" lang="en">
|
||||||
|
<link rel="stylesheet" href="style.css">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<h1>Flowery Flower Shop</h1>
|
||||||
|
<small>Browse our flowers below...</small>
|
||||||
|
|
||||||
|
<h2>Available flowers</h2>
|
||||||
|
<section id="catalog">
|
||||||
|
<article id="rose">
|
||||||
|
<h3>Rose</h3>
|
||||||
|
<ul>
|
||||||
|
<li>Price:</li>
|
||||||
|
<li>Category:</li>
|
||||||
|
<li>Stock:</li>
|
||||||
|
<li>Description:</li>
|
||||||
|
</ul>
|
||||||
|
<form class="add-to-cart">
|
||||||
|
<label>Quantity: <input type="number" name="quantity" min="1" value="1" required></label>
|
||||||
|
<button type="submit">Add to Cart</button>
|
||||||
|
</form>
|
||||||
|
</article>
|
||||||
|
<article id="tulip">
|
||||||
|
<h3>Tulip</h3>
|
||||||
|
<ul>
|
||||||
|
<li>Price:</li>
|
||||||
|
<li>Category:</li>
|
||||||
|
<li>Stock:</li>
|
||||||
|
<li>Description:</li>
|
||||||
|
</ul>
|
||||||
|
<form class="add-to-cart">
|
||||||
|
<label>Quantity: <input type="number" name="quantity" min="1" value="1" required></label>
|
||||||
|
<button type="submit">Add to Cart</button>
|
||||||
|
</form>
|
||||||
|
</article>
|
||||||
|
<article id="sunflower">
|
||||||
|
<h3>Sunflower</h3>
|
||||||
|
<ul>
|
||||||
|
<li>Price:</li>
|
||||||
|
<li>Category:</li>
|
||||||
|
<li>Stock:</li>
|
||||||
|
<li>Description:</li>
|
||||||
|
</ul>
|
||||||
|
<form class="add-to-cart">
|
||||||
|
<label>Quantity: <input type="number" name="quantity" min="1" value="1" required></label>
|
||||||
|
<button type="submit">Add to Cart</button>
|
||||||
|
</form>
|
||||||
|
</article>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<h2>Shopping Cart</h2>
|
||||||
|
<section id="cart">
|
||||||
|
<table id="cart-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Flower</th>
|
||||||
|
<th>Price</th>
|
||||||
|
<th>Quantity</th>
|
||||||
|
<th>Subtotal</th>
|
||||||
|
<th>Remove</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div id="cart-total">
|
||||||
|
<p>Subtotal: $<span id="subtotal">0.00</span></p>
|
||||||
|
<p>Tax: $<span id="tax">0.00</span></p>
|
||||||
|
<strong>Total: $<span id="total">0.00</span></strong>
|
||||||
|
</div>
|
||||||
|
<button id="checkout-button">Checkout</button>
|
||||||
|
<div id="checkout-message"></div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<h2>Testimonials and Feedback</h2>
|
||||||
|
<section id="feedback">
|
||||||
|
<form id="feedback-form">
|
||||||
|
<label>
|
||||||
|
Flower:
|
||||||
|
<select name="flower" required>
|
||||||
|
<option value="">Select a flower to rate</option>
|
||||||
|
<option value="Rose">Red Rose</option>
|
||||||
|
<option value="Tulip">Tulip</option>
|
||||||
|
<option value="Sunflower">Sunflower</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
Rating:
|
||||||
|
<select name="rating" required>
|
||||||
|
<option value="">Rate</option>
|
||||||
|
<option value="5">5 - Excellent</option>
|
||||||
|
<option value="4">4 - Good</option>
|
||||||
|
<option value="3">3 - Average</option>
|
||||||
|
<option value="2">2 - Poor</option>
|
||||||
|
<option value="1">1 - Terrible</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
Feedback:
|
||||||
|
<input type="text" name="content" maxlength="120" placeholder="Your feedback" required>
|
||||||
|
</label>
|
||||||
|
<button type="submit">Submit Feedback</button>
|
||||||
|
</form>
|
||||||
|
<div id="feedback-list">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<script src="index.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
184
INFO-1272 (JS 1)/Projects/Final Project/index.js
Normal file
184
INFO-1272 (JS 1)/Projects/Final Project/index.js
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
// Object called flowers that contains 3 flower objects with their information
|
||||||
|
const flowers = {
|
||||||
|
"rose": {
|
||||||
|
name: "Rose",
|
||||||
|
price: 2.5,
|
||||||
|
category: "Romance",
|
||||||
|
stock: 20,
|
||||||
|
description: "The classic red rose for that someone special."
|
||||||
|
},
|
||||||
|
"tulip": {
|
||||||
|
name: "Tulip",
|
||||||
|
price: 3.0,
|
||||||
|
category: "Springtime",
|
||||||
|
stock: 15,
|
||||||
|
description: "A perfect spring time flower for a bouquet."
|
||||||
|
},
|
||||||
|
"sunflower": {
|
||||||
|
name: "Sunflower",
|
||||||
|
price: 5.0,
|
||||||
|
category: "Exclusives",
|
||||||
|
stock: 10,
|
||||||
|
description: "Large iconic flower perfect for an Autumn garden."
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let feedback = [
|
||||||
|
{flower: "Rose", rating: 5, content: "A beautiful bouqet delivered on time with care!"},
|
||||||
|
{flower: "Tulip", rating: 4, content: "Reminds me of Spring any time of year!"},
|
||||||
|
{flower: "Sunflower", rating: 5, content: "Sunflower was tall and healthy, so elegant!"}
|
||||||
|
];
|
||||||
|
|
||||||
|
// Function to add flower information (price, stock, description, category) to HTML catalog
|
||||||
|
function populateCatalog() {
|
||||||
|
Object.values(flowers).forEach(flower => {
|
||||||
|
const article = document.getElementById(`${flower.name.toLowerCase()}`)
|
||||||
|
if (article) {
|
||||||
|
const listItems = article.querySelectorAll("ul li");
|
||||||
|
listItems[0].textContent = `Price: $${flower.price.toFixed(2)}`;
|
||||||
|
listItems[1].textContent = `Category: ${flower.category}`;
|
||||||
|
listItems[2].textContent = `Stock: ${flower.stock}`;
|
||||||
|
listItems[3].textContent = `Description: ${flower.description}`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to update items inside cart, used by addToCart() and at initialization
|
||||||
|
function updateCartDisplay() {
|
||||||
|
const tbody = document.querySelector("#cart-table tbody");
|
||||||
|
tbody.innerHTML = "";
|
||||||
|
let subtotal = 0;
|
||||||
|
cart.forEach((item, idx) => {
|
||||||
|
const row = document.createElement("tr");
|
||||||
|
row.innerHTML = `
|
||||||
|
<td>${flowers[item.name].name}</td>
|
||||||
|
<td>$${item.price.toFixed(2)}</td>
|
||||||
|
<td>${item.quantity}</td>
|
||||||
|
<td>${(item.price * item.quantity).toFixed(2)}</td>
|
||||||
|
<td><button data-idx="${idx}" class="remove-button">Remove</button></td>
|
||||||
|
`
|
||||||
|
tbody.appendChild(row);
|
||||||
|
subtotal += item.price * item.quantity;
|
||||||
|
});
|
||||||
|
|
||||||
|
const tax = subtotal * 0.13;
|
||||||
|
const total = subtotal + tax;
|
||||||
|
|
||||||
|
document.getElementById("subtotal").textContent = subtotal.toFixed(2);
|
||||||
|
document.getElementById("tax").textContent = tax.toFixed(2);
|
||||||
|
document.getElementById("total").textContent = total.toFixed(2);
|
||||||
|
|
||||||
|
document.querySelectorAll(".remove-button").forEach(btn => {
|
||||||
|
btn.addEventListener("click", function() {
|
||||||
|
const idx = Number(btn.getAttribute("data-idx"));
|
||||||
|
const item = cart[idx];
|
||||||
|
flowers[item.name].stock += item.quantity;
|
||||||
|
cart.splice(idx,1);
|
||||||
|
updateCartDisplay();
|
||||||
|
populateCatalog();
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to handle getting the correct flower name and quantity to add to cart
|
||||||
|
function setupCartForms() {
|
||||||
|
document.querySelectorAll(".add-to-cart").forEach(form => {
|
||||||
|
form.addEventListener("submit", function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
const article = form.closest("article");
|
||||||
|
const flowerName = article.querySelector("h3").textContent;
|
||||||
|
const quantity = form.querySelector("input[name='quantity']").value;
|
||||||
|
addToCart(flowerName, quantity);
|
||||||
|
form.reset();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start with empty cart
|
||||||
|
let cart = [];
|
||||||
|
|
||||||
|
// Funcion to add items to cart
|
||||||
|
function addToCart(flowerName, quantity) {
|
||||||
|
quantity = Number(quantity);
|
||||||
|
|
||||||
|
const key = flowerName.toLowerCase();
|
||||||
|
const flower = flowers[key];
|
||||||
|
|
||||||
|
if (!flower || quantity < 1 || quantity > flower.stock) {
|
||||||
|
alert("Too many or too few flowers added to cart, rejecting.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const existing = cart.find(item => item.name === flowerName);
|
||||||
|
if (existing) {
|
||||||
|
if (quantity > flower.stock) {
|
||||||
|
alert("Added quantity goes over stock limit, rejecting.");
|
||||||
|
return
|
||||||
|
}
|
||||||
|
existing.quantity += quantity;
|
||||||
|
} else {
|
||||||
|
cart.push({
|
||||||
|
name: key,
|
||||||
|
price: flower.price,
|
||||||
|
quantity: quantity
|
||||||
|
});
|
||||||
|
}
|
||||||
|
flower.stock -= quantity;
|
||||||
|
updateCartDisplay();
|
||||||
|
populateCatalog();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to create feedback HTML elements from list
|
||||||
|
function renderFeedback() {
|
||||||
|
const feedbackList = document.getElementById("feedback-list");
|
||||||
|
feedbackList.innerHTML = "";
|
||||||
|
if (feedback.length === 0) {
|
||||||
|
feedbackList.innerHTML = "<p>No feedback yet. Be our first review?</p>";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
feedback.forEach(fb => {
|
||||||
|
const div = document.createElement("div");
|
||||||
|
div.className = "testimonial";
|
||||||
|
div.innerHTML = `
|
||||||
|
<strong>${fb.flower}</strong>
|
||||||
|
<span>(${fb.rating}★)</span>
|
||||||
|
<p>${fb.content}</p>
|
||||||
|
<hr>
|
||||||
|
`
|
||||||
|
feedbackList.appendChild(div);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Event listener for checkout button to show checkout message and reset cart
|
||||||
|
document.getElementById("checkout-button").addEventListener("click", function() {
|
||||||
|
if (cart.length === 0) {
|
||||||
|
document.getElementById("checkout-message").textContent = "Your cart is empty!";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const confirmed = confirm("Are you sure you want to finalize cart and check out?");
|
||||||
|
if (confirmed) {
|
||||||
|
document.getElementById("checkout-message").textContent = "Thank you for your order!";
|
||||||
|
cart = [];
|
||||||
|
updateCartDisplay();
|
||||||
|
populateCatalog();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById("feedback-form").addEventListener("submit", function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
const form = e.target;
|
||||||
|
const flower = form.flower.value;
|
||||||
|
const rating = Number(form.rating.value);
|
||||||
|
const content = form.content.value.trim()
|
||||||
|
|
||||||
|
if (!flower || !rating || !content) return;
|
||||||
|
|
||||||
|
feedback.unshift({ flower, rating, content });
|
||||||
|
renderFeedback();
|
||||||
|
form.reset();
|
||||||
|
})
|
||||||
|
|
||||||
|
populateCatalog();
|
||||||
|
setupCartForms();
|
||||||
|
updateCartDisplay();
|
||||||
|
renderFeedback();
|
||||||
82
INFO-1272 (JS 1)/Projects/Final Project/style.css
Normal file
82
INFO-1272 (JS 1)/Projects/Final Project/style.css
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
body {
|
||||||
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
|
background: #fafafa;
|
||||||
|
color: #222;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
text-align: center;
|
||||||
|
margin: 24px 0 12px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
small {
|
||||||
|
display: block;
|
||||||
|
text-align: center;
|
||||||
|
margin: 0 0 18px 0;
|
||||||
|
color: #555;
|
||||||
|
font-size: 0.98em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
margin-top: 32px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
section {
|
||||||
|
margin: 0 auto 24px auto;
|
||||||
|
max-width: 700px;
|
||||||
|
}
|
||||||
|
|
||||||
|
article {
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 12px;
|
||||||
|
margin: 12px 0;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
th, td {
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
padding: 6px 8px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
form label {
|
||||||
|
display: block;
|
||||||
|
margin: 6px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="number"], input[type="text"], select {
|
||||||
|
padding: 2px 4px;
|
||||||
|
margin-left: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
margin-top: 6px;
|
||||||
|
padding: 4px 10px;
|
||||||
|
border: 1px solid #bbb;
|
||||||
|
border-radius: 3px;
|
||||||
|
background: #f0f0f0;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
background: #e0e0e0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#feedback-list .testimonial {
|
||||||
|
background: #f9f9f9;
|
||||||
|
border: 1px solid #eee;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin: 8px 0;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user