Files
thymecrunch/src/main/resources/templates/explore.html
T
Madeleine Stamp 267d5c7bdf Update 17 files
- /src/main/resources/application.properties
- /src/main/resources/templates/view-recipe.html
- /src/main/resources/templates/create-recipe.html
- /src/main/resources/templates/home.html
- /src/main/resources/templates/explore.html
- /src/main/resources/templates/public-profile.html
- /src/main/resources/templates/my-profile.html
- /src/main/resources/templates/update-recipe.html
- /src/main/resources/static/css/view-recipe.css
- /src/main/java/com/example/demo/controller/SiteController.java
- /src/main/java/com/example/demo/controller/RecipeUploadController.java
- /src/main/java/com/example/demo/controller/RecipeUploadForm.java
- /src/main/java/com/example/demo/config/WebConfig
- /src/main/java/com/example/demo/config/SecurityConfig.java
- /src/main/java/com/example/demo/config/WebConfig.java
- /src/main/java/com/example/demo/service/Impl/RecipeServiceImpl.java
- /src/main/java/com/example/demo/service/RecipeService.java
2026-04-21 13:37:02 -06:00

242 lines
8.0 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="_csrf_header" th:content="${_csrf.headerName}"/>
<meta name="_csrf" th:content="${_csrf.token}"/>
<title>Thyme Crunch Home</title>
<link rel="stylesheet" th:href="@{css/explore.css}">
<link href="https://fonts.googleapis.com/css2?family=Delius+Swash+Caps&family=Mali:ital,wght@0,200;0,300;0,400;0,500;0,600;0,700;1,200;1,300;1,400;1,500;1,600;1,700" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css">
</head>
<body>
<header class="top-header">
<img th:src="@{images/header_left.png}" alt="Violin f-hole shape to the left of header." class="swirl">
<h1 class="site-name">Thyme Crunch</h1>
<img th:src="@{images/header_right.png}" alt="Violin f-hole shape to the right of header." class="swirl">
</header>
<div class="body">
<!--Navigation Bar -->
<div class="body-left">
<nav class="sidebar-left">
<ul>
<li><a href="/">Home</a></li>
<li><a th:href="@{/explore}">Explore</a></li>
<li><a th:href="@{/my-profile}">Profile</a></li>
<li>
<form th:action="@{/logout}" method="post">
<input type="image" th:src="@{images/logout_icon.png}" alt="Logout button" class="nav_icon"/>
</form>
</li>
</ul>
</nav>
<a th:href="@{/create}" target="_blank" class="create_icon">
<img th:src="@{images/create_icon.png}" alt="Create New Recipe Icon (Red mixing bowl with a spoon and yellow addition symbol.">
</a>
</div>
<!-- Main Content -->
<main class="main-content">
<div class="search-bar">
<form action="/explore" method="get" id="search-form">
<label for="site-search">Search:</label>
<div class="search-btn">
<div id="tag-input-wrapper">
<!-- <div id="chips-container"></div> -->
<input type="text" id="site-search" name="q" th:value="${q}" placeholder="Search for recipes...">
</div>
<button type="submit"><i class="fa fa-search"></i></button>
</div>
</form>
</div>
<p th:if="${#lists.isEmpty(recipes)}">There are no recipes that fit this description.</p>
<div class="recipe-card" th:unless="${#lists.isEmpty(recipes)}">
<a th:href="@{/recipes/{id}(id=${recipe.id})}" class="card" th:each="recipe : ${recipes}">
<div class="card-text">
<h2 th:text="${recipe.title}">Recipe Title</h2>
<p th:text="${recipe.description}">Description</p>
</div>
<div class="card-right">
<div th:if="${recipe.images != null and !#lists.isEmpty(recipe.images)}">
<img th:src="${recipe.images[0].imageUrl}" alt="Recipe Image">
</div>
<p th:text="${recipe.cost}">Cost</p>
</div>
</a>
</div>
</main>
<!--Filter -->
<div class="body-right">
<div class="sidebar-right">
<h1> FILTER </h1>
<form id="filter-form">
<h3>Prep Time</h3>
<label>
<input type="checkbox" name="prepTime" value="15"><15 min
</label>
<label>
<input type="checkbox" name="prepTime" value="30">15~30 min
</label>
<label>
<input type="checkbox" name="prepTime" value="60">30~60 min
</label>
<label>
<input type="checkbox" name="prepTime" value="240">1~4 hours
</label>
<label>
<input type="checkbox" name="prepTime" value="241">4+ hours
</label>
<h3>Cook Time</h3>
<label>
<input type="checkbox" name="cookTime" value="15"><15 min
</label>
<label>
<input type="checkbox" name="cookTime" value="30">15~30 min
</label>
<label>
<input type="checkbox" name="cookTime" value="60">30~60 min
</label>
<label>
<input type="checkbox" name="cookTime" value="120">1~2 hours
</label>
<label>
<input type="checkbox" name="cookTime" value="121">2+ hours
</label>
<h3>Price</h3>
<label>
<input type="checkbox" name="price" value="1"> $
</label>
<label>
<input type="checkbox" name="price" value="2"> $$
</label>
<label>
<input type="checkbox" name="price" value="3"> $$$
</label>
<label>
<input type="checkbox" name="price" value="4"> $$$$
</label>
</form>
</div>
</div>
</div>
<script>
(function () {
const input = document.getElementById('site-search');
const tags = [];
// Restore price checkboxes from URL
const params = new URLSearchParams(window.location.search);
params.getAll('prices').forEach(p => {
const cb = document.querySelector(`input[name="price"][value="${p}"]`);
if (cb) cb.checked = true;
});
// Restore cookTime checkboxes from URL
params.getAll('cookTime').forEach(p => {
const cb = document.querySelector(`input[name="cookTime"][value="${p}"]`);
if (cb) cb.checked = true;
});
// Restore prepTime checkboxes from URL
params.getAll('prepTime').forEach(p => {
const cb = document.querySelector(`input[name="prepTime"][value="${p}"]`);
if (cb) cb.checked = true;
});
// Restore tag chips from URL
params.getAll('tags').forEach(addChip);
input.addEventListener('keydown', (e) => {
if ((e.key === ' ' || e.key === 'Enter') && input.value.includes('#')) {
const val = input.value + ' ';
const match = val.match(/(^|\s)(#\w+)(\s)/); // NASTY regex ):
if (match) {
e.preventDefault();
addChip(match[2].substring(1));
input.value = val.replace(match[0], '');
input.value += ' ';
}
}
});
document.querySelectorAll('input[name="price"]').forEach(cb => {
cb.addEventListener('change', submitSearch);
});
document.querySelectorAll('input[name="cookTime"]').forEach(cb => {
cb.addEventListener('change', submitSearch);
});
document.querySelectorAll('input[name="prepTime"]').forEach(cb => {
cb.addEventListener('change', submitSearch);
});
document.getElementById('search-form').addEventListener('submit', (e) => {
e.preventDefault();
submitSearch();
});
function addChip(tag) {
if (tags.includes(tag)) return;
tags.push(tag);
const chip = document.createElement('span');
chip.className = 'tag-chip';
chip.innerHTML = `#${tag} <button type="button" aria-label="Remove ${tag}">×</button>`;
chip.querySelector('button').addEventListener('click', () => {
chip.remove();
tags.splice(tags.indexOf(tag), 1);
});
const lastChip = input.parentElement.querySelector('.tag-chip:last-of-type');
if (lastChip) {
lastChip.insertAdjacentElement('afterend', chip);
} else {
input.insertAdjacentElement('afterend', chip);
}
}
function submitSearch() {
// Only use chips for tags, not strings!
const cleanedQuery = input.value.replace(/#\w+/g, '').trim();
const out = new URLSearchParams();
if (cleanedQuery) out.set('q', cleanedQuery);
tags.forEach(t => out.append('tags', t));
document.querySelectorAll('input[name="price"]:checked')
.forEach(cb => out.append('prices', cb.value));
document.querySelectorAll('input[name="cookTime"]:checked')
.forEach(cb => out.append('cookTime', cb.value));
document.querySelectorAll('input[name="prepTime"]:checked')
.forEach(cb => out.append('prepTime', cb.value));
window.location.href = '/explore?' + out.toString();
}
})();
</script>
</body>
</html>