favicon added. Mass update buttons added. Recipe image placeholders added

This commit is contained in:
2026-02-01 17:59:05 +01:00
parent a970b72aef
commit c34dcae89d
8 changed files with 138 additions and 39 deletions

View File

@@ -2,7 +2,9 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" /> <link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!-- Fonts --> <!-- Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.googleapis.com">
@@ -29,14 +31,14 @@
¡A Mezclar! ¡A Mezclar!
</button> </button>
<!-- TODO: select all/none --> <!-- TODO: select all/none -->
<!--<div class="selection-options" id="selection-options"> <div class="selection-options" id="selection-options">
<button class="selection-button" id="select-all-button" disabled="true"> <button class="selection-button" id="select-all-button" disabled="false">
Select all ¡Todos!
</button> </button>
<button class="selection-button" id="select-none-button" disabled="true"> <button class="selection-button" id="select-none-button" disabled="true">
Reset selection Reset
</button> </button>
</div>--> </div>
</div> </div>
<div class="section"> <div class="section">

BIN
public/favicon-16x16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 701 B

BIN
public/favicon-32x32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 723 B

BIN
public/spirits/default.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

View File

@@ -3,14 +3,12 @@ import data from "./data.json";
let spiritsData = []; let spiritsData = [];
let recipesData = []; let recipesData = [];
// Cargar datos desde JSON
async function loadData() { async function loadData() {
console.log("data");
console.log(data);
try { try {
spiritsData = data.spirits; spiritsData = data.spirits;
recipesData = data.recipes; recipesData = data.recipes;
renderSpirits(); renderSpirits();
enableButtonsLogic();
} catch (error) { } catch (error) {
console.error("Error cargando datos:", error); console.error("Error cargando datos:", error);
document.getElementById("spiritsGrid").innerHTML = document.getElementById("spiritsGrid").innerHTML =
@@ -32,6 +30,24 @@ function renderSpirits() {
`, `,
) )
.join(""); .join("");
}
function toggleSpirit(index, forcedCheckValue) {
const card = document.getElementById(`spirit-${index}`);
const checkbox = document.getElementById(`check-${index}`);
checkbox.checked =
forcedCheckValue !== undefined ? forcedCheckValue : !checkbox.checked;
card.classList.toggle("selected");
// Update buttons status only on natural toggle
if (forcedCheckValue === undefined) {
updateMixButtonStatus();
updateMassSelectButtonsStatus();
}
}
function enableButtonsLogic() {
const grid = document.getElementById("spiritsGrid");
// Add toggle logic to cards // Add toggle logic to cards
spiritsData.map((spirit, index) => { spiritsData.map((spirit, index) => {
grid.querySelector(`#spirit-${index}`).addEventListener("click", (e) => { grid.querySelector(`#spirit-${index}`).addEventListener("click", (e) => {
@@ -44,28 +60,45 @@ function renderSpirits() {
section[0].querySelector(".mix-button").addEventListener("click", (e) => { section[0].querySelector(".mix-button").addEventListener("click", (e) => {
findRecipes(); findRecipes();
}); });
section[0].querySelector(".mix-button").disabled = true;
} }
} // Add logic to select all / none buttons
function toggleSpirit(index) {
const card = document.getElementById(`spirit-${index}`);
const checkbox = document.getElementById(`check-${index}`);
checkbox.checked = !checkbox.checked;
card.classList.toggle("selected");
// Check enable/disable mix button on card toggle
updateMixButtonStatus();
}
function updateMixButtonStatus() {
const section = document.getElementsByClassName("section");
const selected = getSelectedSpirits();
if (section && section.length > 0) { if (section && section.length > 0) {
section[0].querySelector(".mix-button").disabled = selected.length === 0; section[0]
.querySelector("#select-all-button")
.addEventListener("click", (e) => {
selectAll();
});
section[0]
.querySelector("#select-none-button")
.addEventListener("click", (e) => {
selectNone();
});
updateMassSelectButtonsStatus();
} }
} }
function getSelectedSpirits() { function updateMixButtonStatus(forcedDisabledValue) {
const section = document.getElementsByClassName("section");
const selected = getSelectedSpiritsTypes();
if (section && section.length > 0) {
section[0].querySelector(".mix-button").disabled =
forcedDisabledValue ?? selected.length === 0;
}
}
function updateMassSelectButtonsStatus() {
const section = document.getElementsByClassName("selection-options");
const selected = getSelectedSpiritsNames();
if (section && section.length > 0) {
section[0].querySelector("#select-all-button").disabled =
selected.length === spiritsData.length;
section[0].querySelector("#select-none-button").disabled =
selected.length === 0;
}
}
function getSelectedSpiritsTypes() {
const selectedSpirits = []; const selectedSpirits = [];
spiritsData.forEach((spirit, index) => { spiritsData.forEach((spirit, index) => {
const checkbox = document.getElementById(`check-${index}`); const checkbox = document.getElementById(`check-${index}`);
@@ -76,8 +109,49 @@ function getSelectedSpirits() {
return selectedSpirits; return selectedSpirits;
} }
function getSelectedSpiritsNames() {
const selectedSpirits = [];
spiritsData.forEach((spirit, index) => {
const checkbox = document.getElementById(`check-${index}`);
if (checkbox.checked) {
selectedSpirits.push(spirit.name);
}
});
return selectedSpirits;
}
function selectAll() {
const selectAllButton = document.getElementById("select-all-button");
if (selectAllButton) {
const selectedNames = getSelectedSpiritsNames();
spiritsData.forEach((spirit, index) => {
if (!selectedNames.includes(spirit.name)) {
toggleSpirit(index, true);
}
});
}
updateMixButtonStatus(false);
updateMassSelectButtonsStatus();
return;
}
function selectNone() {
const selectNoneButton = document.getElementById("select-none-button");
if (selectNoneButton) {
const selectedNames = getSelectedSpiritsNames();
spiritsData.forEach((spirit, index) => {
if (selectedNames.includes(spirit.name)) {
toggleSpirit(index, false);
}
});
}
updateMixButtonStatus(true);
updateMassSelectButtonsStatus();
return;
}
function findRecipes() { function findRecipes() {
const selectedSpirits = getSelectedSpirits(); const selectedSpirits = getSelectedSpiritsTypes();
if (selectedSpirits.length === 0) { if (selectedSpirits.length === 0) {
document.getElementById("recipesContainer").innerHTML = document.getElementById("recipesContainer").innerHTML =
'<p class="no-recipes">Por favor, selecciona al menos un licor para encontrar recetas.</p>'; '<p class="no-recipes">Por favor, selecciona al menos un licor para encontrar recetas.</p>';
@@ -101,15 +175,6 @@ function findRecipes() {
}); });
} }
// TODO: select all/none spirits
// function selectAll() {
// return;
// }
// function selectNone() {
// return;
// }
function renderRecipes(recipes) { function renderRecipes(recipes) {
const container = document.getElementById("recipesContainer"); const container = document.getElementById("recipesContainer");
@@ -130,6 +195,7 @@ function renderRecipes(recipes) {
return ` return `
<div class="recipe-card"> <div class="recipe-card">
<div class="recipe-content">
<div class="recipe-name">${recipe.name}</div> <div class="recipe-name">${recipe.name}</div>
<div class="non-alcohol-title">Licores:</div> <div class="non-alcohol-title">Licores:</div>
<div class="recipe-ingredients"> <div class="recipe-ingredients">
@@ -173,6 +239,14 @@ function renderRecipes(recipes) {
: "" : ""
} }
</div> </div>
<div class="recipe-img">
<img
src="/public/spirits/${recipe.name?.toLowerCase()}.png"
alt=""
onerror="this.onerror=null; this.src='/public/spirits/default.png';"
/>
</div>
</div>
`; `;
}) })
.join(""); .join("");

View File

@@ -169,10 +169,10 @@ h2 {
left: -50%; left: -50%;
width: 200%; width: 200%;
height: 200%; height: 200%;
background: radial-gradient( background: linear-gradient(
circle, to left,
rgba(255, 255, 255, 0.8) 0%, rgba(255, 255, 255, 0.8) 0%,
transparent 70% transparent 50% transparent 75%
); );
opacity: 0; opacity: 0;
transition: opacity 0.3s; transition: opacity 0.3s;
@@ -257,6 +257,9 @@ h2 {
} }
.recipe-card { .recipe-card {
display: flex;
flex-direction: row;
width: 100%;
background: linear-gradient(135deg, #fff 0%, #fff8f0 100%); background: linear-gradient(135deg, #fff 0%, #fff8f0 100%);
border-left: 5px solid var(--deep-red); border-left: 5px solid var(--deep-red);
border-radius: 10px; border-radius: 10px;
@@ -266,6 +269,10 @@ h2 {
animation: recipeAppear 0.5s ease-out backwards; animation: recipeAppear 0.5s ease-out backwards;
} }
.recipe-content {
width: 70%;
}
.recipe-card:nth-child(1) { .recipe-card:nth-child(1) {
animation-delay: 0.1s; animation-delay: 0.1s;
} }
@@ -281,6 +288,9 @@ h2 {
.recipe-card:nth-child(5) { .recipe-card:nth-child(5) {
animation-delay: 0.5s; animation-delay: 0.5s;
} }
.recipe-card:nth-child(6) {
animation-delay: 0.5s;
}
@keyframes recipeAppear { @keyframes recipeAppear {
from { from {
@@ -361,6 +371,19 @@ h2 {
font-style: italic; font-style: italic;
} }
.recipe-img {
width: 30%;
min-width: 30%;
max-width: 30%;
}
.recipe-img img {
width: 100%;
height: 100%;
object-fit: contain;
display: block;
}
.loading { .loading {
text-align: center; text-align: center;
padding: 2rem; padding: 2rem;