This commit is contained in:
Philippe Torrel
2026-07-13 14:42:35 +02:00
parent e0bd3930d9
commit 960451ae4a
63 changed files with 2974 additions and 0 deletions

View File

@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

View File

@@ -0,0 +1,17 @@
## License and terms of use
© Web Professional Institute Inc. All rights reserved.
This material is provided solely for students enrolled in courses offered by Web Professional Institute Inc. By accessing or using this code, you acknowledge that it is strictly for educational use within the context of Web Professional Institute Inc. programs.
**Usage Restrictions:**
- Redistribution, sharing, or copying of this material outside the course environment is strictly prohibited.
- The content is designed to support your learning objectives and is not authorized for commercial projects, public repositories, or applications beyond the course scope.
- Unauthorized commercial use or open-source distribution is strictly prohibited and may result in expulsion from the program and legal action.
**Agreement & Rights:**
As a student, you have agreed to these terms as part of your enrollment agreement, which includes additional details on permitted uses, restrictions, and policies. The author and Web Professional Institute Inc. retain all rights to this material, including the code base and instructional content.
For any questions regarding these terms, please contact Web Professional Institute Inc. for clarification.

View File

@@ -0,0 +1,62 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Recipe Explorer Dashboard</title>
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<h1>Recipe Explorer Dashboard</h1>
<div id="search-container">
<input
type="text"
id="search-input"
placeholder="Search for recipes by name..."
/>
<button id="search-button">Search</button>
</div>
<div id="loading">Loading...</div>
<div id="error-message"></div>
<table id="recipes-table">
<thead>
<tr>
<th>Recipe Name</th>
<th>Cuisine</th>
<th>Preparation Time (mins)</th>
</tr>
</thead>
<tbody>
<!-- Recipe data will be populated here -->
</tbody>
</table>
<!-- Modal for Recipe Details -->
<div id="recipe-modal" class="modal">
<div class="modal-content">
<span class="close">&times;</span>
<h2 id="modal-title">Recipe Name</h2>
<img
id="modal-image"
src=""
alt="Recipe Image"
style="border-radius: 10px; max-height: 200px"
/>
<p><strong>Cuisine:</strong> <span id="modal-category"></span></p>
<p>
<strong>Preparation Time:</strong> <span id="modal-time"></span> mins
</p>
<p><strong>Ingredients:</strong></p>
<ul id="modal-ingredients">
<!-- Ingredients will be populated here -->
</ul>
<p><strong>Instructions:</strong></p>
<p id="modal-instructions"></p>
</div>
</div>
<script src="scripts.js"></script>
</body>
</html>

View File

@@ -0,0 +1,122 @@
(() => {
// ===== DOM & VARIABLES =====
const RECIPES_URL = 'https://dummyjson.com/recipes';
const searchInput = document.querySelector('#search-input');
const searchButton = document.querySelector('#search-button');
const recipesTableBody = document.querySelector('#recipes-table tbody');
const loadingIndicator = document.querySelector('#loading');
const errorMessage = document.querySelector('#error-message');
const modal = document.querySelector('#recipe-modal');
const modalTitle = document.querySelector('#modal-title');
const modalImage = document.querySelector('#modal-image');
const modalCategory = document.querySelector('#modal-category');
const modalTime = document.querySelector('#modal-time');
const modalIngredients = document.querySelector('#modal-ingredients');
const modalInstructions = document.querySelector('#modal-instructions');
const closeModal = document.querySelector('.close');
let allRecipes = [];
// ===== INIT =====
const init = () => {
// TODO: Fetch and display all recipes on initial load
// Event Listeners
closeModal.addEventListener('click', closeModalEvent);
window.addEventListener('click', windowClickEvent);
};
// ===== EVENT LISTENERS =====
const searchButtonEvent = () => {
const query = searchInput.value.trim().toLowerCase();
if (query !== '') {
// TODO: Implement search functionality
} else {
// TODO: Display all recipes if search input is empty
}
};
// Close Modal Event
const closeModalEvent = () => {
modal.style.display = 'none';
};
// Close Modal when clicking outside the modal content
const windowClickEvent = (event) => {
if (event.target == modal) {
modal.style.display = 'none';
}
};
// ===== FUNCTIONS =====
// Fetches all recipes from the API and displays them.
const fetchAndDisplayRecipes = async () => {
showLoading(true);
showError(false, '');
try {
// TODO: Fetch data from API
} catch (error) {
console.error(error);
showError(true, 'An error occurred while fetching recipes.');
} finally {
showLoading(false);
}
};
// Searches for recipes by name and displays the results.
const searchRecipes = async (query) => {
showLoading(true);
showError(false, '');
try {
// TODO: Search for recipes by name
} catch (error) {
console.error(error);
showError(true, 'An error occurred while searching for recipes.');
} finally {
showLoading(false);
}
};
// Displays a list of recipes in the table.
const displayRecipes = (recipes) => {
clearTable();
// TODO: Iterate over the recipes and display them in the table
};
// Displays detailed information about a selected recipe in a modal.
const showRecipeDetails = (recipe) => {
// TODO: Calculate total preparation time
// TODO: Set image source
// TODO: Populate ingredients
// TODO: Show modal
};
// Shows or hides the loading indicator.
const showLoading = (show) => {
loadingIndicator.style.display = show ? 'block' : 'none';
};
// Displays or hides an error message.
const showError = (show, message) => {
if (show) {
errorMessage.textContent = message;
errorMessage.style.display = 'block';
} else {
errorMessage.textContent = '';
errorMessage.style.display = 'none';
}
};
// Clears the recipes table.
const clearTable = () => {
recipesTableBody.innerHTML = '';
};
// ===== CALL INIT =====
init();
})();

View File

@@ -0,0 +1,124 @@
body {
font-family: Arial, sans-serif;
margin: 20px;
}
h1 {
text-align: center;
color: #4d7c0f;
}
#search-container {
text-align: center;
margin-bottom: 20px;
}
#search-input {
width: 300px;
padding: 10px;
font-size: 16px;
border: 2px solid #65a30d;
border-radius: 5px;
}
#search-button {
padding: 10px 20px;
font-size: 16px;
cursor: pointer;
background-color: #4d7c0f;
color: white;
border: none;
border-radius: 5px;
margin-left: 10px;
}
#search-button:hover {
background-color: #65a30d;
transition: 0.1s;
}
#recipes-table {
width: 100%;
border-collapse: collapse;
margin-bottom: 20px;
max-width: 1000px;
margin-inline: auto;
}
#recipes-table th,
#recipes-table td {
border: 1px solid #ddd;
padding: 12px;
text-align: left;
}
#recipes-table th {
background-color: #4d7c0f;
color: white;
}
#recipes-table tr:nth-child(even) {
background-color: #f9f9f9;
}
#recipes-table tr:hover {
background-color: #ecfccb;
cursor: pointer;
}
/* Modal styling */
.modal {
display: none;
position: fixed;
z-index: 1;
padding-top: 100px;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgba(0, 0, 0, 0.4);
}
.modal-content {
background-color: #fff;
margin: auto;
padding: 20px;
border: 1px solid #888;
width: 60%;
border-radius: 10px;
position: relative;
}
.close {
color: #aaa;
position: absolute;
top: 15px;
right: 25px;
font-size: 30px;
font-weight: bold;
cursor: pointer;
}
.close:hover,
.close:focus {
color: black;
text-decoration: none;
}
/* Loading Indicator */
#loading {
display: none;
text-align: center;
font-size: 18px;
color: #4d7c0f;
}
/* Error Message */
#error-message {
display: none;
text-align: center;
color: red;
font-size: 18px;
margin-bottom: 20px;
}

View File

@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

View File

@@ -0,0 +1,17 @@
## License and terms of use
© Web Professional Institute Inc. All rights reserved.
This material is provided solely for students enrolled in courses offered by Web Professional Institute Inc. By accessing or using this code, you acknowledge that it is strictly for educational use within the context of Web Professional Institute Inc. programs.
**Usage Restrictions:**
- Redistribution, sharing, or copying of this material outside the course environment is strictly prohibited.
- The content is designed to support your learning objectives and is not authorized for commercial projects, public repositories, or applications beyond the course scope.
- Unauthorized commercial use or open-source distribution is strictly prohibited and may result in expulsion from the program and legal action.
**Agreement & Rights:**
As a student, you have agreed to these terms as part of your enrollment agreement, which includes additional details on permitted uses, restrictions, and policies. The author and Web Professional Institute Inc. retain all rights to this material, including the code base and instructional content.
For any questions regarding these terms, please contact Web Professional Institute Inc. for clarification.

View File

@@ -0,0 +1,76 @@
'use strict';
const menu = [
{
category: 'Beverages',
items: [
{ name: 'Coffee', price: 3.5 },
{ name: 'Tea', price: 2.5 },
{ name: 'Juice', price: 4 },
],
},
{
category: 'Pastries',
items: [
{ name: 'Croissant', price: 2.75 },
{ name: 'Muffin', price: 2.5 },
{ name: 'Bagel', price: 2.25 },
],
},
{
category: 'Sandwiches',
items: [
{ name: 'Ham Sandwich', price: 5.5 },
{ name: 'Veggie Sandwich', price: 5 },
{ name: 'Turkey Sandwich', price: 6 },
],
},
];
// ADD YOUR CODE BELOW
const listMenuItems = (menu) => {};
const calculateAveragePrice = (menu) => {};
const findItemsByCategory = (menu, categoryName) => {};
// ===== TEST CASES (DO NOT MODIFY) =====
// 1. List All Menu Items
const menuItems = listMenuItems(menu);
console.log('All Menu Items:\n', menuItems);
// 2. Calculate Average Price
const averagePrice = calculateAveragePrice(menu);
console.log('Average Price of All Items:', averagePrice);
// 3. Find Items by Category
const searchCategory = 'Pastries';
const pastries = findItemsByCategory(menu, searchCategory);
console.log(`Items in "${searchCategory}" \nItems:`, pastries);
/*
===== EXPECTED OUTPUT =====
All Menu Items:
[
'Coffee',
'Tea',
'Juice',
'Croissant',
'Muffin',
'Bagel',
'Ham Sandwich',
'Veggie Sandwich',
'Turkey Sandwich'
]
Average Price of All Items: 3.78
Items in "Pastries"
Items: [
{ name: 'Croissant', price: 2.75 },
{ name: 'Muffin', price: 2.5 },
{ name: 'Bagel', price: 2.25 }
]
*/