favicon added. Mass update buttons added. Recipe image placeholders added
This commit is contained in:
14
index.html
14
index.html
@@ -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
BIN
public/favicon-16x16.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 701 B |
BIN
public/favicon-32x32.png
Normal file
BIN
public/favicon-32x32.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.8 KiB |
BIN
public/favicon.ico
Normal file
BIN
public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 723 B |
BIN
public/spirits/default.png
Normal file
BIN
public/spirits/default.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 MiB |
BIN
public/spirits/gin tonic.png
Normal file
BIN
public/spirits/gin tonic.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 MiB |
134
src/main.js
134
src/main.js
@@ -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("");
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user