-
Notifications
You must be signed in to change notification settings - Fork 32
Supriya dasari fetch security debugging assignments #523
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,120 @@ | ||
| * { | ||
| box-sizing: border-box; | ||
| margin: 0; | ||
| padding: 0; | ||
| } | ||
|
|
||
| body { | ||
| font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif; | ||
| background: #f7f7f7; | ||
| color: #333; | ||
| line-height: 1.6; | ||
| } | ||
|
|
||
| header { | ||
| background: #4a90e2; | ||
| color: white; | ||
| padding: 15px 20px; | ||
| display: flex; | ||
| justify-content: space-between; | ||
| align-items: center; | ||
| } | ||
|
|
||
| header h1 { | ||
| font-size: 24px; | ||
| } | ||
|
|
||
| .search-bar { | ||
| display: flex; | ||
| gap: 10px; | ||
| } | ||
|
|
||
| .search-bar input { | ||
| padding: 8px; | ||
| border-radius: 4px; | ||
| border: none; | ||
| width: 200px; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| } | ||
|
|
||
| .search-bar button { | ||
| padding: 8px 12px; | ||
| border: none; | ||
| border-radius: 4px; | ||
| background: white; | ||
| color: #4a90e2; | ||
| cursor: pointer; | ||
| font-weight: bold; | ||
| transition: background 0.3s; | ||
| } | ||
|
|
||
| .search-bar button:hover { | ||
| background: #e2e2e2; | ||
| } | ||
|
|
||
| main { | ||
| padding: 20px; | ||
| } | ||
|
|
||
| .grid { | ||
| display: grid; | ||
| grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); | ||
| gap: 20px; | ||
| } | ||
|
|
||
| .product-card { | ||
| background: white; | ||
| border-radius: 8px; | ||
| padding: 15px; | ||
| text-align: center; | ||
| box-shadow: 0 2px 5px rgba(0,0,0,0.1); | ||
| cursor: pointer; | ||
| transition: transform 0.2s; | ||
| } | ||
|
|
||
| .product-card:hover { | ||
| transform: translateY(-5px); | ||
| } | ||
|
|
||
| .product-card img { | ||
| max-width: 100%; | ||
| height: 150px; | ||
| object-fit: contain; | ||
| margin-bottom: 10px; | ||
| } | ||
|
|
||
| .product-card h3 { | ||
| font-size: 18px; | ||
| margin-bottom: 8px; | ||
| } | ||
|
|
||
| .product-card p { | ||
| font-size: 16px; | ||
| color: #4a90e2; | ||
| font-weight: bold; | ||
| } | ||
|
|
||
| #product-details { | ||
| background: white; | ||
| border-radius: 8px; | ||
| padding: 20px; | ||
| box-shadow: 0 2px 5px rgba(0,0,0,0.1); | ||
| margin-top: 20px; | ||
| } | ||
|
|
||
| #product-details img { | ||
| max-width: 300px; | ||
| display: block; | ||
| margin-bottom: 15px; | ||
| } | ||
|
|
||
| .hidden { | ||
| display: none; | ||
| } | ||
|
|
||
| footer { | ||
| margin-top: 40px; | ||
| padding: 15px; | ||
| background: #4a90e2; | ||
| color: white; | ||
| text-align: center; | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,88 @@ | ||||||
| "use strict"; | ||||||
|
|
||||||
| const productList = document.getElementById("product-list"); | ||||||
| const productDetails = document.getElementById("product-details"); | ||||||
| const searchInput = document.getElementById("search-input"); | ||||||
| const searchBtn = document.getElementById("search-btn"); | ||||||
| const allBtn = document.getElementById("all-btn"); | ||||||
|
|
||||||
| async function fetchProducts() { | ||||||
| try { | ||||||
| const res = await fetch("https://dummyjson.com/products?limit=30"); | ||||||
| if (!res.ok) throw new Error("Unable to load products"); | ||||||
| const data = await res.json(); | ||||||
| return data.products; | ||||||
| } catch (err) { | ||||||
| productList.innerHTML = `<p class="error">${err.message}</p>`; | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using
Suggested change
|
||||||
| } | ||||||
| } | ||||||
|
|
||||||
| async function searchProducts(query) { | ||||||
| try { | ||||||
| const res = await fetch(`https://dummyjson.com/products/search?q=${query}`); | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The search query is being inserted directly into the fetch URL. User input should always be properly encoded when used in a URL to prevent issues like malformed URLs or potential injection attacks if the endpoint is vulnerable. Use
Suggested change
|
||||||
| if (!res.ok) throw new Error("Search failed"); | ||||||
| const data = await res.json(); | ||||||
| return data.products; | ||||||
| } catch (err) { | ||||||
| productList.innerHTML = `<p class="error">${err.message}</p>`; | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| async function getProduct(id) { | ||||||
| try { | ||||||
| const res = await fetch(`https://dummyjson.com/products/${id}`); | ||||||
| if (!res.ok) throw new Error("Unable to fetch product details"); | ||||||
| return await res.json(); | ||||||
| } catch (err) { | ||||||
| productDetails.innerHTML = `<p class="error">${err.message}</p>`; | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| function renderProducts(products) { | ||||||
| productDetails.classList.add("hidden"); | ||||||
| productList.innerHTML = ""; | ||||||
| products.forEach(p => { | ||||||
| const card = document.createElement("div"); | ||||||
| card.className = "product-card"; | ||||||
| card.innerHTML = ` | ||||||
| <img src="${p.thumbnail}" alt="${p.title}"> | ||||||
| <h3>${p.title}</h3> | ||||||
| <p>$${p.price}</p> | ||||||
| `; | ||||||
| card.addEventListener("click", () => showProduct(p.id)); | ||||||
| productList.appendChild(card); | ||||||
|
Comment on lines
+45
to
+53
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using const card = document.createElement("div");
card.className = "product-card";
const img = document.createElement("img");
img.src = p.thumbnail;
img.alt = p.title;
const title = document.createElement("h3");
title.textContent = p.title;
const price = document.createElement("p");
price.textContent = `$${p.price}`;
card.append(img, title, price);
card.addEventListener("click", () => showProduct(p.id));
productList.appendChild(card); |
||||||
| }); | ||||||
| } | ||||||
|
|
||||||
| async function showProduct(id) { | ||||||
| const product = await getProduct(id); | ||||||
| if (product) { | ||||||
| productDetails.classList.remove("hidden"); | ||||||
| productDetails.innerHTML = ` | ||||||
| <h2>${product.title}</h2> | ||||||
| <img src="${product.thumbnail}" alt="${product.title}"> | ||||||
| <p>${product.description}</p> | ||||||
| <p><strong>Price:</strong> $${product.price}</p> | ||||||
| <p><strong>Brand:</strong> ${product.brand}</p> | ||||||
| <p><strong>Category:</strong> ${product.category}</p> | ||||||
| `; | ||||||
|
Comment on lines
+61
to
+68
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similar to the productDetails.innerHTML = '';
const titleEl = document.createElement('h2');
titleEl.textContent = product.title;
const imgEl = document.createElement('img');
imgEl.src = product.thumbnail;
imgEl.alt = product.title;
const descriptionEl = document.createElement('p');
descriptionEl.textContent = product.description;
const priceEl = document.createElement('p');
priceEl.innerHTML = `<strong>Price:</strong> `;
priceEl.append(`$${product.price}`);
const brandEl = document.createElement('p');
brandEl.innerHTML = `<strong>Brand:</strong> `;
brandEl.append(product.brand);
const categoryEl = document.createElement('p');
categoryEl.innerHTML = `<strong>Category:</strong> `;
categoryEl.append(product.category);
productDetails.append(titleEl, imgEl, descriptionEl, priceEl, brandEl, categoryEl); |
||||||
| window.scrollTo({ top: productDetails.offsetTop - 20, behavior: "smooth" }); | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| async function loadAllProducts() { | ||||||
| const products = await fetchProducts(); | ||||||
| if (products) renderProducts(products); | ||||||
| } | ||||||
|
|
||||||
| async function handleSearch() { | ||||||
| const query = searchInput.value.trim(); | ||||||
| if (!query) return; | ||||||
| const products = await searchProducts(query); | ||||||
| if (products) renderProducts(products); | ||||||
| } | ||||||
|
|
||||||
| searchBtn.addEventListener("click", handleSearch); | ||||||
| allBtn.addEventListener("click", loadAllProducts); | ||||||
|
|
||||||
| window.addEventListener("load", loadAllProducts); | ||||||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,32 @@ | ||||||
| <!DOCTYPE html> | ||||||
| <html lang="en"> | ||||||
| <head> | ||||||
| <meta charset="UTF-8"> | ||||||
| <title>ShopEase - Simple E-Commerce</title> | ||||||
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||||||
| <link rel="stylesheet" href="./css/shop.css"> | ||||||
| </head> | ||||||
| <body> | ||||||
| <header> | ||||||
| <h1>ShopEase</h1> | ||||||
| <div class="search-bar"> | ||||||
| <input type="text" id="search-input" placeholder="Search products..."> | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This input field is missing a
Suggested change
|
||||||
| <button id="search-btn">Search</button> | ||||||
| <button id="all-btn">All Products</button> | ||||||
| </div> | ||||||
| </header> | ||||||
|
|
||||||
| <main> | ||||||
| <section id="products-section"> | ||||||
| <div id="product-list" class="grid"></div> | ||||||
| </section> | ||||||
| <section id="product-details" class="hidden"></section> | ||||||
| </main> | ||||||
|
|
||||||
| <footer> | ||||||
| <p>© 2025 ShopEase. All rights reserved.</p> | ||||||
| </footer> | ||||||
|
|
||||||
| <script src="./scripts/shop.js" defer></script> | ||||||
| </body> | ||||||
| </html> | ||||||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,15 @@ | ||||||
| <!DOCTYPE html> | ||||||
| <html lang="en"> | ||||||
| <head> | ||||||
| <meta charset="UTF-8" /> | ||||||
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||||||
|
|
||||||
| <title>Javascript Assignments | Supriya Dasari</title> | ||||||
| </head> | ||||||
| <body> | ||||||
| <details> | ||||||
| <summary>Assignment 1</summary> | ||||||
| <iframe src="./assignment-1/shop.html" frameborder="0" width="100%"></iframe> | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The
Suggested change
|
||||||
| </details> | ||||||
| </body> | ||||||
| </html> | ||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To improve maintainability, it's best to declare colors as CSS variables. This makes it easy to manage your color palette from one place. I've started the conversion here, which you should continue for other hardcoded colors in this file (e.g., on lines 43, 51, 92, 117).