35604-vm/assets/js/main.js
Flatlogic Bot 24d0513664 2
2025-11-09 21:38:45 +00:00

320 lines
13 KiB
JavaScript

document.addEventListener('DOMContentLoaded', function () {
// --- Snowflakes Effect ---
function createSnowflakes() {
const snowContainer = document.getElementById('snow-container');
if (!snowContainer) return;
snowContainer.innerHTML = '';
const numberOfSnowflakes = 50;
for (let i = 0; i < numberOfSnowflakes; i++) {
const snowflake = document.createElement('div');
snowflake.className = 'snowflake';
const size = Math.random() * 4 + 2; // size from 2px to 6px
snowflake.style.width = `${size}px`;
snowflake.style.height = `${size}px`;
snowflake.style.left = Math.random() * 100 + 'vw';
const animationDuration = Math.random() * 5 + 5; // 5 to 10 seconds
snowflake.style.animationDuration = `${animationDuration}s`;
const animationDelay = Math.random() * 5; // 0 to 5 seconds
snowflake.style.animationDelay = `${animationDelay}s`;
snowflake.style.opacity = Math.random() * 0.7 + 0.3; // 0.3 to 1.0
snowContainer.appendChild(snowflake);
}
}
createSnowflakes();
// --- DOM Elements ---
const recipeNameInput = document.getElementById('recipeName');
const guestCountInput = document.getElementById('guestCount');
const ingredientsContainer = document.getElementById('ingredients-container');
const addIngredientBtn = document.getElementById('add-ingredient');
const calculateBtn = document.getElementById('calculate-btn');
const newRecipeBtn = document.getElementById('new-recipe-btn');
const shoppingListContainer = document.getElementById('shopping-list-container');
const recipeCardsContainer = document.getElementById('recipe-cards-container');
// --- Core Functions ---
async function loadRecipes() {
try {
const response = await fetch('api/get_recipes.php');
const data = await response.json();
if (data.success) {
renderRecipeCards(data.recipes);
} else {
console.error('Failed to load recipes:', data.error);
recipeCardsContainer.innerHTML = '<div class="col-12"><p class="text-center text-danger">Error loading recipes.</p></div>';
}
} catch (error) {
console.error('Error:', error);
recipeCardsContainer.innerHTML = '<div class="col-12"><p class="text-center text-danger">Could not connect to the server.</p></div>';
}
}
function addIngredientRow(ingredient = { name: '', quantity: '', unit: '' }) {
const row = document.createElement('div');
row.className = 'ingredient-row mb-2';
row.innerHTML = `
<input type="text" class="form-control" placeholder="Ingredient Name" aria-label="Ingredient Name" value="${ingredient.name}">
<input type="number" class="form-control" placeholder="Qty" aria-label="Quantity" min="0" step="any" value="${ingredient.quantity}">
<input type="text" class="form-control" placeholder="Unit (e.g., grams, ml)" aria-label="Unit" value="${ingredient.unit}">
<button type="button" class="btn btn-danger btn-sm remove-ingredient">&times;</button>
`;
ingredientsContainer.appendChild(row);
}
function clearForm() {
recipeNameInput.value = '';
guestCountInput.value = '1';
ingredientsContainer.innerHTML = '';
addIngredientRow();
shoppingListContainer.innerHTML = `
<div class="text-center text-muted p-5">
<h3 class="h4">Your Shopping List</h3>
<p>Your calculated list will appear here.</p>
</div>
`;
}
function renderRecipeCards(recipes) {
recipeCardsContainer.innerHTML = '';
if (!recipes || recipes.length === 0) {
recipeCardsContainer.innerHTML = '<div class="col-12"><p class="text-center text-muted">Здесь будут появляться ваши сохраненные рецепты.</p></div>';
return;
}
recipes.forEach(recipe => {
const cardCol = document.createElement('div');
cardCol.className = 'col-lg-4 col-md-6 mb-4';
cardCol.setAttribute('data-id', recipe.id);
const card = document.createElement('div');
card.className = 'card h-100';
const cardBody = document.createElement('div');
cardBody.className = 'card-body d-flex flex-column';
const title = document.createElement('h5');
title.className = 'card-title';
title.textContent = recipe.name;
const subtitle = document.createElement('h6');
subtitle.className = 'card-subtitle mb-2 text-muted';
subtitle.textContent = `${recipe.guests} guest(s)`;
const text = document.createElement('p');
text.className = 'card-text';
text.textContent = `${recipe.ingredients.length} ingredients`;
const buttonGroup = document.createElement('div');
buttonGroup.className = 'mt-auto';
buttonGroup.innerHTML = `
<button class="btn btn-sm btn-outline-primary edit-recipe-btn">Edit</button>
<button class="btn btn-sm btn-outline-danger delete-recipe-btn">Delete</button>
`;
cardBody.appendChild(title);
cardBody.appendChild(subtitle);
cardBody.appendChild(text);
cardBody.appendChild(buttonGroup);
card.appendChild(cardBody);
cardCol.appendChild(card);
recipeCardsContainer.appendChild(cardCol);
});
}
function renderShoppingList(list) {
let html = '<h3>Общий список покупок</h3><hr>';
if (list.length === 0) {
html += '<p>Нет ингредиентов для расчета.</p>';
} else {
html += '<ul class="list-group list-group-flush">';
list.forEach(item => {
const quantityStr = Number.isInteger(item.qty) ? item.qty : parseFloat(item.qty.toFixed(2));
html += `<li class="list-group-item d-flex justify-content-between align-items-center">
<span>${item.name}</span>
<span class="badge bg-primary rounded-pill">${quantityStr} ${item.unit}</span>
</li>`;
});
html += '</ul>';
}
shoppingListContainer.innerHTML = html;
}
function getRecipeDataFromForm() {
const recipeName = recipeNameInput.value.trim();
const guests = parseInt(guestCountInput.value, 10) || 0;
const ingredients = [];
const rows = ingredientsContainer.querySelectorAll('.ingredient-row');
rows.forEach(row => {
const name = row.querySelector('input[placeholder="Ingredient Name"]').value.trim();
const qty = parseFloat(row.querySelector('input[placeholder="Qty"]').value);
const unit = row.querySelector('input[placeholder="Unit (e.g., grams, ml)"]').value.trim();
if (name && !isNaN(qty) && qty > 0) {
ingredients.push({ name, quantity: qty, unit });
}
});
if (recipeName && guests > 0 && ingredients.length > 0) {
return { name: recipeName, guests, ingredients };
}
return null;
}
// --- Event Listeners ---
addIngredientBtn.addEventListener('click', () => addIngredientRow());
ingredientsContainer.addEventListener('click', function(e) {
if (e.target.classList.contains('remove-ingredient')) {
e.target.closest('.ingredient-row').remove();
}
});
recipeCardsContainer.addEventListener('click', async function(e) {
const target = e.target;
const card = target.closest('.col-lg-4');
if (!card) return;
const recipeId = card.getAttribute('data-id');
if (target.classList.contains('delete-recipe-btn')) {
if (confirm('Are you sure you want to delete this recipe?')) {
try {
const response = await fetch('api/delete_recipe.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ id: recipeId })
});
const data = await response.json();
if (data.success) {
card.remove();
} else {
alert('Failed to delete recipe: ' + data.error);
}
} catch (error) {
alert('Error: ' + error.message);
}
}
}
if (target.classList.contains('edit-recipe-btn')) {
// Find the recipe data from the currently rendered cards
const response = await fetch('api/get_recipes.php');
const data = await response.json();
if(!data.success) return;
const recipeToEdit = data.recipes.find(r => r.id == recipeId);
if (recipeToEdit) {
// Populate form
recipeNameInput.value = recipeToEdit.name;
guestCountInput.value = recipeToEdit.guests;
ingredientsContainer.innerHTML = '';
recipeToEdit.ingredients.forEach(ing => addIngredientRow(ing));
// Delete the old recipe from DB
try {
await fetch('api/delete_recipe.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ id: recipeId })
});
card.remove(); // Remove from UI immediately
} catch (error) {
alert('Error preparing for edit: ' + error.message);
}
}
}
});
newRecipeBtn.addEventListener('click', async function() {
const recipeData = getRecipeDataFromForm();
if (recipeData) {
try {
const response = await fetch('api/save_recipe.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(recipeData)
});
const data = await response.json();
if (data.success) {
await loadRecipes(); // Reload all recipes to show the new one
clearForm();
} else {
alert('Failed to save recipe: ' + data.error);
}
} catch (error) {
alert('Error: ' + error.message);
}
} else {
alert('Please fill out the recipe name, guests, and at least one ingredient before saving.');
}
});
calculateBtn.addEventListener('click', async function() {
try {
const response = await fetch('api/get_recipes.php');
const data = await response.json();
if (!data.success) {
alert('Could not get recipes for calculation.');
return;
}
const allRecipesToCalculate = data.recipes;
const currentRecipe = getRecipeDataFromForm();
if (currentRecipe) {
// Give it a temporary ID to avoid collisions
currentRecipe.id = 'current';
allRecipesToCalculate.push(currentRecipe);
}
if (allRecipesToCalculate.length === 0) {
alert('There are no recipes to calculate. Please fill out the form or save a recipe.');
return;
}
const combinedIngredients = new Map();
allRecipesToCalculate.forEach(recipe => {
const multiplier = recipe.guests;
recipe.ingredients.forEach(ing => {
const ingName = ing.name || '';
const ingUnit = ing.unit || '';
const key = `${ingName.trim().toLowerCase()}|${ingUnit.trim().toLowerCase()}`;
const existing = combinedIngredients.get(key);
if (existing) {
existing.qty += (ing.quantity || 0) * multiplier;
} else {
combinedIngredients.set(key, {
name: ing.name,
qty: (ing.quantity || 0) * multiplier,
unit: ing.unit
});
}
});
});
renderShoppingList(Array.from(combinedIngredients.values()));
} catch(error) {
alert('Calculation Error: ' + error.message);
}
});
// --- Initial State ---
addIngredientRow();
loadRecipes();
});