Good registration page
This commit is contained in:
parent
7643a96d2f
commit
afdfe09b22
@ -6,19 +6,19 @@ $data = json_decode(file_get_contents('php://input'), true);
|
|||||||
$email = $data['email'] ?? '';
|
$email = $data['email'] ?? '';
|
||||||
$password = $data['password'] ?? '';
|
$password = $data['password'] ?? '';
|
||||||
|
|
||||||
if (empty($email) || empty($password)) {
|
if ($email === '' || $password === '') {
|
||||||
echo json_encode(['success' => false, 'error' => 'Email and password are required.']);
|
echo json_encode(['success' => false, 'error' => 'Email and password are required.']);
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$pdo = db();
|
$stmt = db()->prepare("SELECT id, email, password FROM users WHERE email = ?");
|
||||||
$stmt = $pdo->prepare("SELECT id, password_hash FROM users WHERE email = ?");
|
|
||||||
$stmt->execute([$email]);
|
$stmt->execute([$email]);
|
||||||
$user = $stmt->fetch();
|
$user = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
if ($user && password_verify($password, $user['password_hash'])) {
|
if ($user && password_verify($password, $user['password'])) {
|
||||||
login_user($user['id']);
|
$_SESSION['user_id'] = $user['id'];
|
||||||
|
$_SESSION['email'] = $user['email'];
|
||||||
echo json_encode(['success' => true]);
|
echo json_encode(['success' => true]);
|
||||||
} else {
|
} else {
|
||||||
echo json_encode(['success' => false, 'error' => 'Invalid email or password.']);
|
echo json_encode(['success' => false, 'error' => 'Invalid email or password.']);
|
||||||
|
|||||||
@ -6,7 +6,7 @@ $data = json_decode(file_get_contents('php://input'), true);
|
|||||||
$email = $data['email'] ?? '';
|
$email = $data['email'] ?? '';
|
||||||
$password = $data['password'] ?? '';
|
$password = $data['password'] ?? '';
|
||||||
|
|
||||||
if (empty($email) || empty($password)) {
|
if ($email === '' || $password === '') {
|
||||||
echo json_encode(['success' => false, 'error' => 'Email and password are required.']);
|
echo json_encode(['success' => false, 'error' => 'Email and password are required.']);
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
@ -16,25 +16,19 @@ if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
|||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$hashedPassword = password_hash($password, PASSWORD_DEFAULT);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$pdo = db();
|
$stmt = db()->prepare("INSERT INTO users (email, password) VALUES (?, ?)");
|
||||||
|
if ($stmt->execute([$email, $hashedPassword])) {
|
||||||
// Check if user already exists
|
$_SESSION['user_id'] = db()->lastInsertId();
|
||||||
$stmt = $pdo->prepare("SELECT id FROM users WHERE email = ?");
|
$_SESSION['email'] = $email;
|
||||||
$stmt->execute([$email]);
|
|
||||||
if ($stmt->fetch()) {
|
|
||||||
echo json_encode(['success' => false, 'error' => 'Email already registered.']);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
$password_hash = password_hash($password, PASSWORD_DEFAULT);
|
|
||||||
$stmt = $pdo->prepare("INSERT INTO users (email, password_hash) VALUES (?, ?)");
|
|
||||||
$stmt->execute([$email, $password_hash]);
|
|
||||||
|
|
||||||
$user_id = $pdo->lastInsertId();
|
|
||||||
login_user($user_id);
|
|
||||||
|
|
||||||
echo json_encode(['success' => true]);
|
echo json_encode(['success' => true]);
|
||||||
|
}
|
||||||
} catch (PDOException $e) {
|
} catch (PDOException $e) {
|
||||||
|
if ($e->getCode() == 23000) {
|
||||||
|
echo json_encode(['success' => false, 'error' => 'Email is already registered.']);
|
||||||
|
} else {
|
||||||
echo json_encode(['success' => false, 'error' => 'Database error: ' . $e->getMessage()]);
|
echo json_encode(['success' => false, 'error' => 'Database error: ' . $e->getMessage()]);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -20,20 +20,20 @@ $imageType = $_FILES['image']['type'];
|
|||||||
file_put_contents(__DIR__ . '/scan_debug.log', date('Y-m-d H:i:s') . " Processing image: type=$imageType, size=$imageSize bytes" . PHP_EOL, FILE_APPEND);
|
file_put_contents(__DIR__ . '/scan_debug.log', date('Y-m-d H:i:s') . " Processing image: type=$imageType, size=$imageSize bytes" . PHP_EOL, FILE_APPEND);
|
||||||
file_put_contents(__DIR__ . '/scan_debug.log', date('Y-m-d H:i:s') . " Data URL prefix: " . substr("data:$imageType;base64,$imageData", 0, 50) . "..." . PHP_EOL, FILE_APPEND);
|
file_put_contents(__DIR__ . '/scan_debug.log', date('Y-m-d H:i:s') . " Data URL prefix: " . substr("data:$imageType;base64,$imageData", 0, 50) . "..." . PHP_EOL, FILE_APPEND);
|
||||||
|
|
||||||
$prompt = "Analyze this image. If it's a photo of a dish, identify what it is and generate a possible recipe for it. If it's a photo of a written recipe, extract the information.
|
$prompt = "Analyze this image. If it's a photo of a dish, identify what it is and create a possible recipe for it. If it's a photo of a written recipe, extract the information from it.
|
||||||
Extract or generate the following information in JSON format:
|
Provide the information in JSON format IN ENGLISH:
|
||||||
- name: The name of the dish/recipe
|
- name: Dish/recipe name
|
||||||
- category: One of 'Drinks', 'Breakfast', 'Dinner', 'Appetizers'
|
- category: One of the following values: 'Drinks', 'Breakfast', 'Dinner', 'Appetizers'
|
||||||
- ingredients: An array of objects, each with:
|
- ingredients: Array of objects, each containing:
|
||||||
- name: Name of the ingredient
|
- name: Ingredient name
|
||||||
- quantity: Numeric quantity (float)
|
- quantity: Numeric quantity (float)
|
||||||
- unit: Unit of measurement (e.g., 'g', 'kg', 'ml', 'l', 'piece', 'pack')
|
- unit: Unit of measurement (e.g., 'g', 'kg', 'ml', 'l', 'pcs', 'pack')
|
||||||
- guests: The default number of guests/servings the recipe is for (integer)
|
- guests: Default number of guests/portions (integer)
|
||||||
|
|
||||||
Important:
|
Important:
|
||||||
1. The quantities should be for 1 person if possible, or for the number of guests specified. If you're generating a recipe for a dish, default to 1 or 2 guests.
|
1. Quantities should be specified per 1 person if possible. If you are creating a recipe for a dish, default to 1 or 2 guests.
|
||||||
2. Return ONLY the JSON object.
|
2. Return ONLY the JSON object.
|
||||||
3. If you can't determine something, provide a best guess or null.";
|
3. If something cannot be determined, make the most accurate assumption or specify null.";
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$response = LocalAIApi::createResponse([
|
$response = LocalAIApi::createResponse([
|
||||||
|
|||||||
@ -117,7 +117,7 @@ const app = {
|
|||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error:', error);
|
console.error('Error:', error);
|
||||||
app.dom.recipeCardsContainer.innerHTML = '<div class="col-12"><p class="text-center text-danger">Could not connect to the server.</p></div>';
|
app.dom.recipeCardsContainer.innerHTML = '<div class="col-12"><p class="text-center text-danger">Failed to connect to the server.</p></div>';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async saveRecipe(formData) {
|
async saveRecipe(formData) {
|
||||||
@ -191,7 +191,10 @@ const app = {
|
|||||||
if (recipe.category) {
|
if (recipe.category) {
|
||||||
const categoryLabel = document.createElement('div');
|
const categoryLabel = document.createElement('div');
|
||||||
categoryLabel.className = 'recipe-category-label';
|
categoryLabel.className = 'recipe-category-label';
|
||||||
categoryLabel.textContent = recipe.category;
|
categoryLabel.textContent = recipe.category === 'Drinks' ? 'Drinks' :
|
||||||
|
recipe.category === 'Breakfast' ? 'Breakfast' :
|
||||||
|
recipe.category === 'Dinner' ? 'Lunch/Dinner' :
|
||||||
|
recipe.category === 'Appetizers' ? 'Appetizers' : recipe.category;
|
||||||
titleWrapper.appendChild(categoryLabel);
|
titleWrapper.appendChild(categoryLabel);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -280,7 +283,7 @@ const app = {
|
|||||||
}
|
}
|
||||||
const item = combinedIngredients.get(key);
|
const item = combinedIngredients.get(key);
|
||||||
item.additionalQty += prod.quantity;
|
item.additionalQty += prod.quantity;
|
||||||
const source = prod.source || 'Additional product';
|
const source = prod.source || 'Add. product';
|
||||||
if (!item.sources.includes(source)) {
|
if (!item.sources.includes(source)) {
|
||||||
item.sources.push(source);
|
item.sources.push(source);
|
||||||
}
|
}
|
||||||
@ -292,10 +295,10 @@ const app = {
|
|||||||
|
|
||||||
// 3. Group for display
|
// 3. Group for display
|
||||||
const groups = {
|
const groups = {
|
||||||
'Food': { ingredients: [] },
|
'Food': { label: 'Food', ingredients: [] },
|
||||||
'Drinks': { ingredients: [] },
|
'Drinks': { label: 'Drinks', ingredients: [] },
|
||||||
'Cooking and serving': { ingredients: [] },
|
'Cooking and serving': { label: 'Kitchen & serving', ingredients: [] },
|
||||||
'Tableware and consumables': { ingredients: [] }
|
'Tableware and consumables': { label: 'Tableware & consumables', ingredients: [] }
|
||||||
};
|
};
|
||||||
|
|
||||||
combinedIngredients.forEach((item, key) => {
|
combinedIngredients.forEach((item, key) => {
|
||||||
@ -321,7 +324,7 @@ const app = {
|
|||||||
const group = groups[groupName];
|
const group = groups[groupName];
|
||||||
if (group.ingredients.length > 0) {
|
if (group.ingredients.length > 0) {
|
||||||
totalIngredients += group.ingredients.length;
|
totalIngredients += group.ingredients.length;
|
||||||
html += `<h4 class="mt-3">${groupName}</h4>`;
|
html += `<h4 class="mt-3">${group.label}</h4>`;
|
||||||
html += '<ul class="list-group list-group-flush">';
|
html += '<ul class="list-group list-group-flush">';
|
||||||
group.ingredients.forEach((item, index) => {
|
group.ingredients.forEach((item, index) => {
|
||||||
const totalQty = item.recipeQty + item.additionalQty;
|
const totalQty = item.recipeQty + item.additionalQty;
|
||||||
@ -369,14 +372,14 @@ const app = {
|
|||||||
const row = document.createElement('div');
|
const row = document.createElement('div');
|
||||||
row.className = 'ingredient-row mb-3';
|
row.className = 'ingredient-row mb-3';
|
||||||
|
|
||||||
const units = ['g', 'kg', 'ml', 'l', 'piece', 'pack'];
|
const units = ['g', 'kg', 'ml', 'l', 'pcs', 'pack'];
|
||||||
const unitButtons = units.map(u =>
|
const unitButtons = units.map(u =>
|
||||||
`<button type="button" class="btn ${ingredient.unit === u ? 'btn-secondary' : 'btn-outline-secondary'} unit-btn">${u}</button>`
|
`<button type="button" class="btn ${ingredient.unit === u ? 'btn-secondary' : 'btn-outline-secondary'} unit-btn">${u}</button>`
|
||||||
).join('');
|
).join('');
|
||||||
|
|
||||||
row.innerHTML = `
|
row.innerHTML = `
|
||||||
<div class="mb-2">
|
<div class="mb-2">
|
||||||
<input type="text" class="form-control" placeholder="Ingredient Name" aria-label="Ingredient Name" value="${ingredient.name}">
|
<input type="text" class="form-control" placeholder="Ingredient name" aria-label="Ingredient Name" value="${ingredient.name}">
|
||||||
</div>
|
</div>
|
||||||
<div class="d-flex align-items-center">
|
<div class="d-flex align-items-center">
|
||||||
<input type="number" class="form-control me-2" placeholder="Qty" aria-label="Quantity" min="0" step="any" value="${ingredient.quantity}" style="width: 100px;">
|
<input type="number" class="form-control me-2" placeholder="Qty" aria-label="Quantity" min="0" step="any" value="${ingredient.quantity}" style="width: 100px;">
|
||||||
@ -396,9 +399,9 @@ const app = {
|
|||||||
app.dom.guestCountInput.value = '1';
|
app.dom.guestCountInput.value = '1';
|
||||||
app.dom.ingredientsContainer.innerHTML = '';
|
app.dom.ingredientsContainer.innerHTML = '';
|
||||||
app.ui.addIngredientRow();
|
app.ui.addIngredientRow();
|
||||||
app.dom.newRecipeBtn.textContent = 'Save Recipe';
|
app.dom.newRecipeBtn.textContent = 'Save recipe';
|
||||||
app.dom.cancelEditBtn.style.display = 'none';
|
app.dom.cancelEditBtn.style.display = 'none';
|
||||||
document.getElementById('recipe-form-modal-label').textContent = 'Add a Recipe';
|
document.getElementById('recipe-form-modal-label').textContent = 'Add recipe';
|
||||||
},
|
},
|
||||||
populateFormForEdit(recipeId) {
|
populateFormForEdit(recipeId) {
|
||||||
const recipe = app.state.recipes.find(r => r.id == recipeId);
|
const recipe = app.state.recipes.find(r => r.id == recipeId);
|
||||||
@ -416,9 +419,9 @@ const app = {
|
|||||||
app.ui.addIngredientRow();
|
app.ui.addIngredientRow();
|
||||||
}
|
}
|
||||||
|
|
||||||
app.dom.newRecipeBtn.textContent = 'Update Recipe';
|
app.dom.newRecipeBtn.textContent = 'Update recipe';
|
||||||
app.dom.cancelEditBtn.style.display = 'block';
|
app.dom.cancelEditBtn.style.display = 'block';
|
||||||
document.getElementById('recipe-form-modal-label').textContent = 'Edit Recipe';
|
document.getElementById('recipe-form-modal-label').textContent = 'Edit recipe';
|
||||||
app.dom.recipeFormModal.show();
|
app.dom.recipeFormModal.show();
|
||||||
|
|
||||||
app.dom.recipeNameInput.focus();
|
app.dom.recipeNameInput.focus();
|
||||||
@ -442,7 +445,10 @@ const app = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
document.getElementById('view-recipe-name').textContent = recipe.name;
|
document.getElementById('view-recipe-name').textContent = recipe.name;
|
||||||
document.getElementById('view-recipe-category').textContent = recipe.category || 'No category';
|
document.getElementById('view-recipe-category').textContent = recipe.category === 'Drinks' ? 'Drinks' :
|
||||||
|
recipe.category === 'Breakfast' ? 'Breakfast' :
|
||||||
|
recipe.category === 'Dinner' ? 'Lunch/Dinner' :
|
||||||
|
recipe.category === 'Appetizers' ? 'Appetizers' : 'No category';
|
||||||
document.getElementById('view-recipe-guests').textContent = recipe.guests;
|
document.getElementById('view-recipe-guests').textContent = recipe.guests;
|
||||||
|
|
||||||
const ingredientsList = document.getElementById('view-recipe-ingredients');
|
const ingredientsList = document.getElementById('view-recipe-ingredients');
|
||||||
@ -467,8 +473,10 @@ const app = {
|
|||||||
const ingredients = [];
|
const ingredients = [];
|
||||||
const rows = app.dom.ingredientsContainer.querySelectorAll('.ingredient-row');
|
const rows = app.dom.ingredientsContainer.querySelectorAll('.ingredient-row');
|
||||||
rows.forEach(row => {
|
rows.forEach(row => {
|
||||||
const name = row.querySelector('input[placeholder="Ingredient Name"]').value.trim();
|
const nameInput = row.querySelector('input[placeholder="Ingredient name"]');
|
||||||
const qty = parseFloat(row.querySelector('input[placeholder="Qty"]').value);
|
const name = nameInput ? nameInput.value.trim() : '';
|
||||||
|
const qtyInput = row.querySelector('input[placeholder="Qty"]');
|
||||||
|
const qty = qtyInput ? parseFloat(qtyInput.value) : NaN;
|
||||||
const activeButton = row.querySelector('.unit-selector .btn-secondary');
|
const activeButton = row.querySelector('.unit-selector .btn-secondary');
|
||||||
const unit = activeButton ? activeButton.textContent.trim() : 'g';
|
const unit = activeButton ? activeButton.textContent.trim() : 'g';
|
||||||
|
|
||||||
@ -504,7 +512,7 @@ const app = {
|
|||||||
<span class="text-muted">Welcome, ${app.state.user.email}</span>
|
<span class="text-muted">Welcome, ${app.state.user.email}</span>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<button class="btn btn-outline-primary btn-sm" id="logout-btn">Logout</button>
|
<button class="btn btn-outline-primary btn-sm" id="logout-btn">Log Out</button>
|
||||||
</li>
|
</li>
|
||||||
`;
|
`;
|
||||||
if (guestView) guestView.classList.add('d-none');
|
if (guestView) guestView.classList.add('d-none');
|
||||||
@ -613,11 +621,11 @@ const app = {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
alert('AI Scan failed: ' + result.error);
|
alert('AI scan error: ' + result.error);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error during AI scan:', error);
|
console.error('Error during AI scan:', error);
|
||||||
alert('An error occurred during AI scanning.');
|
alert('An error occurred during AI scan.');
|
||||||
} finally {
|
} finally {
|
||||||
app.dom.aiScanBtn.disabled = false;
|
app.dom.aiScanBtn.disabled = false;
|
||||||
app.dom.aiScanLoading.classList.add('d-none');
|
app.dom.aiScanLoading.classList.add('d-none');
|
||||||
@ -643,7 +651,7 @@ const app = {
|
|||||||
app.dom.newRecipeBtn.addEventListener('click', async function() {
|
app.dom.newRecipeBtn.addEventListener('click', async function() {
|
||||||
const recipeData = app.ui.getRecipeDataFromForm();
|
const recipeData = app.ui.getRecipeDataFromForm();
|
||||||
if (!recipeData) {
|
if (!recipeData) {
|
||||||
alert('Please fill out the recipe name, category, guests, and at least one ingredient before saving.');
|
alert('Please fill in recipe name, category, guest count, and at least one ingredient before saving.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -745,7 +753,7 @@ const app = {
|
|||||||
name: properName,
|
name: properName,
|
||||||
quantity: 0,
|
quantity: 0,
|
||||||
unit: unit,
|
unit: unit,
|
||||||
source: 'Additional product'
|
source: 'Add. product'
|
||||||
};
|
};
|
||||||
app.state.additionalProducts.push(productToModify);
|
app.state.additionalProducts.push(productToModify);
|
||||||
}
|
}
|
||||||
@ -769,7 +777,7 @@ const app = {
|
|||||||
name: properName,
|
name: properName,
|
||||||
quantity: 0,
|
quantity: 0,
|
||||||
unit: unit,
|
unit: unit,
|
||||||
source: 'Additional product'
|
source: 'Add. product'
|
||||||
};
|
};
|
||||||
app.state.additionalProducts.push(productToModify);
|
app.state.additionalProducts.push(productToModify);
|
||||||
}
|
}
|
||||||
@ -885,7 +893,7 @@ const app = {
|
|||||||
const category = app.dom.productCategory.value;
|
const category = app.dom.productCategory.value;
|
||||||
|
|
||||||
if (!name || isNaN(quantity) || quantity <= 0 || !unit) {
|
if (!name || isNaN(quantity) || quantity <= 0 || !unit) {
|
||||||
alert('Please fill out all fields with valid values.');
|
alert('Please fill in all fields with valid values.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -899,7 +907,7 @@ const app = {
|
|||||||
if (existingProduct) {
|
if (existingProduct) {
|
||||||
existingProduct.quantity += quantity;
|
existingProduct.quantity += quantity;
|
||||||
} else {
|
} else {
|
||||||
const newProduct = { name, quantity, unit, source: 'Additional product', category: category };
|
const newProduct = { name, quantity, unit, source: 'Add. product', category: category };
|
||||||
app.state.additionalProducts.push(newProduct);
|
app.state.additionalProducts.push(newProduct);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
151
index.php
151
index.php
@ -5,20 +5,20 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
|
||||||
<!-- SEO & Meta Tags -->
|
<!-- SEO & Meta Tags -->
|
||||||
<title>Smart Recipe & Shopping List</title>
|
<title>FoodieFlow — An AI-powered flow from recipe to grocery</title>
|
||||||
<meta name="description" content="Manage your recipes and generate smart shopping lists. Identify dishes with AI and calculate ingredients effortlessly.">
|
<meta name="description" content="FoodieFlow — An AI-powered flow from recipe to grocery — every day, every occasion. Manage recipes, create smart shopping lists, and optimize your grocery budget with AI.">
|
||||||
<meta name="keywords" content="recipe manager, shopping list generator, AI recipe scanner, cooking organizer, meal planner">
|
<meta name="keywords" content="FoodieFlow, recipe manager, shopping list, AI recipe scanner, cooking organizer, meal planner">
|
||||||
|
|
||||||
<!-- Open Graph / Facebook -->
|
<!-- Open Graph / Facebook -->
|
||||||
<meta property="og:type" content="website">
|
<meta property="og:type" content="website">
|
||||||
<meta property="og:title" content="Smart Recipe & Shopping List">
|
<meta property="og:title" content="FoodieFlow — An AI-powered flow from recipe to grocery">
|
||||||
<meta property="og:description" content="Manage your recipes and generate smart shopping lists.">
|
<meta property="og:description" content="Manage recipes and create smart shopping lists. An AI-powered flow from recipe to grocery — every day, every occasion.">
|
||||||
<meta property="og:image" content="">
|
<meta property="og:image" content="">
|
||||||
|
|
||||||
<!-- Twitter -->
|
<!-- Twitter -->
|
||||||
<meta name="twitter:card" content="summary_large_image">
|
<meta name="twitter:card" content="summary_large_image">
|
||||||
<meta name="twitter:title" content="Smart Recipe & Shopping List">
|
<meta name="twitter:title" content="FoodieFlow — An AI-powered flow from recipe to grocery">
|
||||||
<meta name="twitter:description" content="Manage your recipes and generate smart shopping lists.">
|
<meta name="twitter:description" content="An AI-powered flow from recipe to grocery — every day, every occasion.">
|
||||||
<meta name="twitter:image" content="">
|
<meta name="twitter:image" content="">
|
||||||
|
|
||||||
<!-- Styles -->
|
<!-- Styles -->
|
||||||
@ -27,7 +27,7 @@
|
|||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;600;700&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;600;700&display=swap" rel="stylesheet">
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
|
||||||
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>_v3">
|
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>_v5">
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@ -35,15 +35,15 @@
|
|||||||
<nav class="navbar navbar-expand-lg navbar-light bg-light shadow-sm sticky-top mb-4">
|
<nav class="navbar navbar-expand-lg navbar-light bg-light shadow-sm sticky-top mb-4">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<a class="navbar-brand d-flex align-items-center" href="/">
|
<a class="navbar-brand d-flex align-items-center" href="/">
|
||||||
<i class="bi bi-book-half me-2" style="color: var(--accent-color);"></i>
|
<i class="bi bi-flow me-2" style="color: var(--accent-color);"></i>
|
||||||
<span class="fw-bold">SmartRecipe</span>
|
<span class="fw-bold">FoodieFlow</span>
|
||||||
</a>
|
</a>
|
||||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
|
||||||
<span class="navbar-toggler-icon"></span>
|
<span class="navbar-toggler-icon"></span>
|
||||||
</button>
|
</button>
|
||||||
<div class="collapse navbar-collapse" id="navbarNav">
|
<div class="collapse navbar-collapse" id="navbarNav">
|
||||||
<ul class="navbar-nav ms-auto align-items-center" id="auth-nav">
|
<ul class="navbar-nav ms-auto align-items-center" id="auth-nav">
|
||||||
<!-- Auth elements will be injected here -->
|
<!-- Auth items will be inserted here -->
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<div class="spinner-border spinner-border-sm text-primary" role="status"></div>
|
<div class="spinner-border spinner-border-sm text-primary" role="status"></div>
|
||||||
</li>
|
</li>
|
||||||
@ -55,13 +55,46 @@
|
|||||||
<div id="guest-view" class="d-none auth-screen">
|
<div id="guest-view" class="d-none auth-screen">
|
||||||
<div class="container-fluid h-100 p-0">
|
<div class="container-fluid h-100 p-0">
|
||||||
<div class="row h-100 g-0">
|
<div class="row h-100 g-0">
|
||||||
<!-- Left Column: Image -->
|
<!-- Left Column: Image & Description -->
|
||||||
<div class="col-lg-6 d-none d-lg-block auth-image-container">
|
<div class="col-lg-6 d-none d-lg-block auth-image-container">
|
||||||
<div class="auth-image-overlay d-flex align-items-center justify-content-center p-5 text-white">
|
<div class="auth-image-overlay d-flex align-items-center justify-content-center p-5 text-white">
|
||||||
<div class="auth-branding-content text-center">
|
<div class="auth-branding-content">
|
||||||
<i class="bi bi-book-half display-1 mb-4" style="color: #FF7F50; filter: drop-shadow(0 0 10px rgba(255, 127, 80, 0.3));"></i>
|
<div class="text-center mb-4">
|
||||||
<h1 class="display-3 fw-bold mb-4">SmartRecipe</h1>
|
<i class="bi bi-flow display-1 mb-2" style="color: #FF7F50; filter: drop-shadow(0 0 10px rgba(255, 127, 80, 0.3));"></i>
|
||||||
<p class="lead fs-4">Manage your recipes and generate smart shopping lists. Identify dishes with AI and calculate ingredients effortlessly.</p>
|
<h1 class="display-3 fw-bold">FoodieFlow</h1>
|
||||||
|
<p class="lead mt-2">An AI-powered flow from recipe to grocery — every day, every occasion</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="feature-list mt-4 text-start">
|
||||||
|
<div class="d-flex mb-3">
|
||||||
|
<i class="bi bi-stars fs-3 me-3 text-warning"></i>
|
||||||
|
<div>
|
||||||
|
<h5 class="mb-1">AI Recipes from Photos</h5>
|
||||||
|
<p class="small mb-0">Upload a photo of any dish, and our AI will instantly create its recipe.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex mb-3">
|
||||||
|
<i class="bi bi-cart-check fs-3 me-3 text-info"></i>
|
||||||
|
<div>
|
||||||
|
<h5 class="mb-1">Smart Shopping</h5>
|
||||||
|
<p class="small mb-0">Automatic grocery lists. We'll find where it's cheaper and build your basket.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex mb-3">
|
||||||
|
<i class="bi bi-people fs-3 me-3 text-success"></i>
|
||||||
|
<div>
|
||||||
|
<h5 class="mb-1">For Holidays & Weekdays</h5>
|
||||||
|
<p class="small mb-0">Plan parties together: share your grocery list with friends and family.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex mb-3">
|
||||||
|
<i class="bi bi-receipt fs-3 me-3 text-danger"></i>
|
||||||
|
<div>
|
||||||
|
<h5 class="mb-1">Expense Control</h5>
|
||||||
|
<p class="small mb-0">Snap a photo of your receipt, and the app will help split the cost among all participants.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -72,12 +105,12 @@
|
|||||||
<!-- Login Form -->
|
<!-- Login Form -->
|
||||||
<div id="login-container">
|
<div id="login-container">
|
||||||
<div class="mb-5">
|
<div class="mb-5">
|
||||||
<h2 class="display-5 mb-2">Welcome back</h2>
|
<h2 class="display-5 mb-2">Welcome back!</h2>
|
||||||
<p class="text-muted">Enter your credentials to access your recipes.</p>
|
<p class="text-muted">Log in to access your recipes.</p>
|
||||||
</div>
|
</div>
|
||||||
<form id="login-form-landing">
|
<form id="login-form-landing">
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<label class="form-label">Email Address</label>
|
<label class="form-label">Email address</label>
|
||||||
<input type="email" class="form-control form-control-lg" name="email" placeholder="name@example.com" required>
|
<input type="email" class="form-control form-control-lg" name="email" placeholder="name@example.com" required>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
@ -85,7 +118,7 @@
|
|||||||
<input type="password" class="form-control form-control-lg" name="password" placeholder="••••••••" required>
|
<input type="password" class="form-control form-control-lg" name="password" placeholder="••••••••" required>
|
||||||
</div>
|
</div>
|
||||||
<div class="d-grid gap-2 mb-4">
|
<div class="d-grid gap-2 mb-4">
|
||||||
<button type="submit" class="btn btn-primary btn-lg">Sign In</button>
|
<button type="submit" class="btn btn-primary btn-lg">Log In</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<p class="text-center text-muted">
|
<p class="text-center text-muted">
|
||||||
@ -96,12 +129,12 @@
|
|||||||
<!-- Register Form -->
|
<!-- Register Form -->
|
||||||
<div id="register-container" class="d-none">
|
<div id="register-container" class="d-none">
|
||||||
<div class="mb-5">
|
<div class="mb-5">
|
||||||
<h2 class="display-5 mb-2">Create Account</h2>
|
<h2 class="display-5 mb-2">Create account</h2>
|
||||||
<p class="text-muted">Start your smart cooking journey today.</p>
|
<p class="text-muted">Start your smart culinary journey today.</p>
|
||||||
</div>
|
</div>
|
||||||
<form id="register-form-landing">
|
<form id="register-form-landing">
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<label class="form-label">Email Address</label>
|
<label class="form-label">Email address</label>
|
||||||
<input type="email" class="form-control form-control-lg" name="email" placeholder="name@example.com" required>
|
<input type="email" class="form-control form-control-lg" name="email" placeholder="name@example.com" required>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
@ -109,7 +142,7 @@
|
|||||||
<input type="password" class="form-control form-control-lg" name="password" placeholder="••••••••" required>
|
<input type="password" class="form-control form-control-lg" name="password" placeholder="••••••••" required>
|
||||||
</div>
|
</div>
|
||||||
<div class="d-grid gap-2 mb-4">
|
<div class="d-grid gap-2 mb-4">
|
||||||
<button type="submit" class="btn btn-primary btn-lg">Register</button>
|
<button type="submit" class="btn btn-primary btn-lg">Sign Up</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<p class="text-center text-muted">
|
<p class="text-center text-muted">
|
||||||
@ -126,8 +159,8 @@
|
|||||||
<div id="app-view" class="d-none">
|
<div id="app-view" class="d-none">
|
||||||
<main class="container my-5">
|
<main class="container my-5">
|
||||||
<div class="text-center mb-5" style="padding-top: 20px;">
|
<div class="text-center mb-5" style="padding-top: 20px;">
|
||||||
<h1 class="display-4 mt-4">My Recipe Book</h1>
|
<h1 class="display-4 mt-4">FoodieFlow</h1>
|
||||||
<p class="lead">Plan your meals and get your shopping lists sorted.</p>
|
<p class="lead">An AI-powered flow from recipe to grocery — every day, every occasion</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row g-4">
|
<div class="row g-4">
|
||||||
@ -136,7 +169,7 @@
|
|||||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||||
<h2 class="text-center mb-0">All Recipes</h2>
|
<h2 class="text-center mb-0">All Recipes</h2>
|
||||||
<button class="btn btn-primary" type="button" data-bs-toggle="modal" data-bs-target="#recipe-form-modal">
|
<button class="btn btn-primary" type="button" data-bs-toggle="modal" data-bs-target="#recipe-form-modal">
|
||||||
Add Recipe
|
Add recipe
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3 search-container">
|
<div class="mb-3 search-container">
|
||||||
@ -147,7 +180,7 @@
|
|||||||
<button class="btn btn-secondary active" data-category="all">All</button>
|
<button class="btn btn-secondary active" data-category="all">All</button>
|
||||||
<button class="btn btn-outline-secondary" data-category="Drinks">Drinks</button>
|
<button class="btn btn-outline-secondary" data-category="Drinks">Drinks</button>
|
||||||
<button class="btn btn-outline-secondary" data-category="Breakfast">Breakfast</button>
|
<button class="btn btn-outline-secondary" data-category="Breakfast">Breakfast</button>
|
||||||
<button class="btn btn-outline-secondary" data-category="Dinner">Dinner</button>
|
<button class="btn btn-outline-secondary" data-category="Dinner">Lunch/Dinner</button>
|
||||||
<button class="btn btn-outline-secondary" data-category="Appetizers">Appetizers</button>
|
<button class="btn btn-outline-secondary" data-category="Appetizers">Appetizers</button>
|
||||||
<button class="btn btn-outline-secondary" data-category="No category">No category</button>
|
<button class="btn btn-outline-secondary" data-category="No category">No category</button>
|
||||||
</div>
|
</div>
|
||||||
@ -161,16 +194,16 @@
|
|||||||
<!-- Right Column: Shopping List / Products -->
|
<!-- Right Column: Shopping List / Products -->
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||||
<h2 class="text-center mb-0">Shopping List</h2>
|
<h2 class="text-center mb-0">Shopping list</h2>
|
||||||
<div>
|
<div>
|
||||||
<button id="add-product-btn" class="btn btn-primary me-2" data-bs-toggle="modal" data-bs-target="#add-product-modal">Add Product</button>
|
<button id="add-product-btn" class="btn btn-primary me-2" data-bs-toggle="modal" data-bs-target="#add-product-modal">Add product</button>
|
||||||
<button id="print-shopping-list-btn" class="btn btn-outline-secondary"><i class="bi bi-printer"></i> Print</button>
|
<button id="print-shopping-list-btn" class="btn btn-outline-secondary"><i class="bi bi-printer"></i> Print</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card shadow">
|
<div class="card shadow">
|
||||||
<div class="card-body" id="shopping-list-container">
|
<div class="card-body" id="shopping-list-container">
|
||||||
<div class="text-center text-muted p-5">
|
<div class="text-center text-muted p-5">
|
||||||
<p>Your calculated list will appear here.</p>
|
<p>Your grocery list will appear here.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -184,7 +217,7 @@
|
|||||||
<div class="modal-dialog modal-dialog-centered">
|
<div class="modal-dialog modal-dialog-centered">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h5 class="modal-title" id="recipe-form-modal-label">Add a Recipe</h5>
|
<h5 class="modal-title" id="recipe-form-modal-label">Add recipe</h5>
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
@ -192,8 +225,8 @@
|
|||||||
<form id="recipe-form">
|
<form id="recipe-form">
|
||||||
<input type="hidden" id="recipeId">
|
<input type="hidden" id="recipeId">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="recipeName" class="form-label">Recipe Name</label>
|
<label for="recipeName" class="form-label">Recipe name</label>
|
||||||
<input type="text" class="form-control" id="recipeName" placeholder="e.g., Avocado Toast">
|
<input type="text" class="form-control" id="recipeName" placeholder="e.g. Avocado toast">
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="recipeCategory" class="form-label">Category</label>
|
<label for="recipeCategory" class="form-label">Category</label>
|
||||||
@ -201,31 +234,31 @@
|
|||||||
<option value="" selected disabled>Choose...</option>
|
<option value="" selected disabled>Choose...</option>
|
||||||
<option value="Drinks">Drinks</option>
|
<option value="Drinks">Drinks</option>
|
||||||
<option value="Breakfast">Breakfast</option>
|
<option value="Breakfast">Breakfast</option>
|
||||||
<option value="Dinner">Dinner</option>
|
<option value="Dinner">Lunch/Dinner</option>
|
||||||
<option value="Appetizers">Appetizers</option>
|
<option value="Appetizers">Appetizers</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="recipeImage" class="form-label d-flex justify-content-between">
|
<label for="recipeImage" class="form-label d-flex justify-content-between">
|
||||||
Recipe Image
|
Image
|
||||||
<button type="button" id="ai-scan-btn" class="btn btn-outline-primary btn-sm">
|
<button type="button" id="ai-scan-btn" class="btn btn-outline-primary btn-sm">
|
||||||
<i class="bi bi-magic"></i> AI Scan
|
<i class="bi bi-magic"></i> AI Scanning
|
||||||
</button>
|
</button>
|
||||||
</label>
|
</label>
|
||||||
<input type="file" class="form-control" id="recipeImage">
|
<input type="file" class="form-control" id="recipeImage">
|
||||||
<div id="ai-scan-loading" class="text-primary mt-2 d-none">
|
<div id="ai-scan-loading" class="text-primary mt-2 d-none">
|
||||||
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
|
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
|
||||||
AI is identifying the dish and recipe...
|
AI is recognizing the dish and creating a recipe...
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hr class="my-4 border-secondary">
|
<hr class="my-4 border-secondary">
|
||||||
|
|
||||||
<h3 class="h5 mb-3">Ingredients (for 1 person)</h3>
|
<h3 class="h5 mb-3">Ingredients (per 1 person)</h3>
|
||||||
<div id="ingredients-container">
|
<div id="ingredients-container">
|
||||||
<!-- Ingredient rows will be injected here by JS -->
|
<!-- Ingredient rows will be injected here by JS -->
|
||||||
</div>
|
</div>
|
||||||
<button type="button" id="add-ingredient" class="btn btn-secondary btn-sm mt-2">+ Add Ingredient</button>
|
<button type="button" id="add-ingredient" class="btn btn-secondary btn-sm mt-2">+ Add ingredient</button>
|
||||||
|
|
||||||
<hr class="my-4 border-secondary">
|
<hr class="my-4 border-secondary">
|
||||||
|
|
||||||
@ -233,19 +266,19 @@
|
|||||||
<div class="col">
|
<div class="col">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="guestCount" class="form-label">How many guests?</label>
|
<label for="guestCount" class="form-label">How many guests?</label>
|
||||||
<input type="number" class="form-control" id="guestCount" placeholder="e.g., 8" min="1" value="1">
|
<input type="number" class="form-control" id="guestCount" placeholder="e.g. 8" min="1" value="1">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="portionsPerGuest" class="form-label">Portions/guest</label>
|
<label for="portionsPerGuest" class="form-label">Portions per guest</label>
|
||||||
<input type="number" class="form-control" id="portionsPerGuest" placeholder="e.g., 2" min="1" value="1">
|
<input type="number" class="form-control" id="portionsPerGuest" placeholder="e.g. 2" min="1" value="1">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="d-grid gap-2 mt-4">
|
<div class="d-grid gap-2 mt-4">
|
||||||
<button type="button" id="new-recipe-btn" class="btn btn-primary">Save Recipe</button>
|
<button type="button" id="new-recipe-btn" class="btn btn-primary">Save recipe</button>
|
||||||
<button type="button" id="cancel-edit-btn" class="btn btn-outline-secondary" data-bs-dismiss="modal">Cancel</button>
|
<button type="button" id="cancel-edit-btn" class="btn btn-outline-secondary" data-bs-dismiss="modal">Cancel</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
@ -260,20 +293,20 @@
|
|||||||
<div class="modal-dialog modal-dialog-centered">
|
<div class="modal-dialog modal-dialog-centered">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h5 class="modal-title" id="add-product-modal-label">Add a Product</h5>
|
<h5 class="modal-title" id="add-product-modal-label">Add product</h5>
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<form id="add-product-form">
|
<form id="add-product-form">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="productName" class="form-label">Product Name</label>
|
<label for="productName" class="form-label">Product name</label>
|
||||||
<input type="text" class="form-control" id="productName" placeholder="e.g., Milk">
|
<input type="text" class="form-control" id="productName" placeholder="e.g. Milk">
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="productQuantity" class="form-label">Quantity</label>
|
<label for="productQuantity" class="form-label">Quantity</label>
|
||||||
<input type="number" class="form-control" id="productQuantity" placeholder="e.g., 1" min="1" value="1">
|
<input type="number" class="form-control" id="productQuantity" placeholder="1" min="1" value="1">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col">
|
<div class="col">
|
||||||
@ -284,7 +317,7 @@
|
|||||||
<button type="button" class="btn btn-outline-secondary unit-btn">kg</button>
|
<button type="button" class="btn btn-outline-secondary unit-btn">kg</button>
|
||||||
<button type="button" class="btn btn-outline-secondary unit-btn">ml</button>
|
<button type="button" class="btn btn-outline-secondary unit-btn">ml</button>
|
||||||
<button type="button" class="btn btn-outline-secondary unit-btn">l</button>
|
<button type="button" class="btn btn-outline-secondary unit-btn">l</button>
|
||||||
<button type="button" class="btn btn-outline-secondary unit-btn">piece</button>
|
<button type="button" class="btn btn-outline-secondary unit-btn">pcs</button>
|
||||||
<button type="button" class="btn btn-outline-secondary unit-btn">pack</button>
|
<button type="button" class="btn btn-outline-secondary unit-btn">pack</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -295,12 +328,12 @@
|
|||||||
<select class="form-select" id="productCategory">
|
<select class="form-select" id="productCategory">
|
||||||
<option value="Food" selected>Food</option>
|
<option value="Food" selected>Food</option>
|
||||||
<option value="Drinks">Drinks</option>
|
<option value="Drinks">Drinks</option>
|
||||||
<option value="Cooking and serving">Cooking and serving</option>
|
<option value="Cooking and serving">Kitchen & serving</option>
|
||||||
<option value="Tableware and consumables">Tableware and consumables</option>
|
<option value="Tableware and consumables">Tableware & consumables</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="d-grid gap-2 mt-4">
|
<div class="d-grid gap-2 mt-4">
|
||||||
<button type="submit" class="btn btn-primary">Add Product</button>
|
<button type="submit" class="btn btn-primary">Add product</button>
|
||||||
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Cancel</button>
|
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Cancel</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
@ -314,13 +347,13 @@
|
|||||||
<div class="modal-dialog modal-dialog-centered">
|
<div class="modal-dialog modal-dialog-centered">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h5 class="modal-title" id="view-recipe-modal-label">View Recipe</h5>
|
<h5 class="modal-title" id="view-recipe-modal-label">View recipe</h5>
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<h2 id="view-recipe-name"></h2>
|
<h2 id="view-recipe-name"></h2>
|
||||||
<p><strong>Category:</strong> <span id="view-recipe-category"></span></p>
|
<p><strong>Category:</strong> <span id="view-recipe-category"></span></p>
|
||||||
<p><strong>Serves:</strong> <span id="view-recipe-guests"></span></p>
|
<p><strong>Guest count:</strong> <span id="view-recipe-guests"></span></p>
|
||||||
<hr>
|
<hr>
|
||||||
<h3>Ingredients</h3>
|
<h3>Ingredients</h3>
|
||||||
<ul id="view-recipe-ingredients" class="list-group">
|
<ul id="view-recipe-ingredients" class="list-group">
|
||||||
@ -334,29 +367,29 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<footer class="text-center py-4 mt-5">
|
<footer class="text-center py-4 mt-5">
|
||||||
<p class="mb-0">© <?php echo date("Y"); ?> Smart Recipe & Shopping List.</p>
|
<p class="mb-0">© <?php echo date("Y"); ?> FoodieFlow — An AI-powered flow from recipe to grocery — every day, every occasion.</p>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
<!-- Scripts -->
|
<!-- Scripts -->
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
|
<script src="assets/js/main.js?v=<?php echo time(); ?>_v5"></script>
|
||||||
|
|
||||||
<!-- Confirmation Modal -->
|
<!-- Confirmation Modal -->
|
||||||
<div class="modal fade" id="confirmRemoveModal" tabindex="-1" aria-labelledby="confirmRemoveModalLabel" aria-hidden="true">
|
<div class="modal fade" id="confirmRemoveModal" tabindex="-1" aria-labelledby="confirmRemoveModalLabel" aria-hidden="true">
|
||||||
<div class="modal-dialog">
|
<div class="modal-dialog">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h5 class="modal-title" id="confirmRemoveModalLabel">Confirm Removal</h5>
|
<h5 class="modal-title" id="confirmRemoveModalLabel">Confirm Deletion</h5>
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<p>You are about to remove an ingredient from a recipe. This will affect the recipe itself. Are you sure you want to continue?</p>
|
<p>You are about to remove an ingredient from the recipe. This will modify the recipe. Are you sure?</p>
|
||||||
<p><strong>Recipe:</strong> <span id="modal-recipe-name"></span></p>
|
<p><strong>Recipe:</strong> <span id="modal-recipe-name"></span></p>
|
||||||
<p><strong>Ingredient:</strong> <span id="modal-ingredient-name"></span></p>
|
<p><strong>Ingredient:</strong> <span id="modal-ingredient-name"></span></p>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
||||||
<button type="button" class="btn btn-danger" id="confirm-remove-btn">Remove</button>
|
<button type="button" class="btn btn-danger" id="confirm-remove-btn">Delete</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user