Hey, it's Christmas time!
+Hey, it's Christmas time!
Let's get your holiday recipes sorted.
@@ -211,6 +215,30 @@ + + +
diff --git a/api/save_recipe.php b/api/save_recipe.php index e3d4581..8fb2603 100644 --- a/api/save_recipe.php +++ b/api/save_recipe.php @@ -2,62 +2,93 @@ header('Content-Type: application/json'); require_once __DIR__ . '/../db/config.php'; -$data = json_decode(file_get_contents('php://input'), true); +// The request is now multipart/form-data, so we use $_POST and $_FILES +$data = $_POST; +$files = $_FILES; -if (!$data || !isset($data['name']) || !isset($data['guests']) || !isset($data['ingredients'])) { +if (!isset($data['name']) || !isset($data['guests']) || !isset($data['ingredients'])) { echo json_encode(['success' => false, 'error' => 'Invalid input.']); exit; } +$ingredients = json_decode($data['ingredients'], true); +if (json_last_error() !== JSON_ERROR_NONE) { + echo json_encode(['success' => false, 'error' => 'Invalid ingredients format.']); + exit; +} + $pdo = db(); +$imageUrl = null; try { + // Handle file upload + if (isset($files['image']) && $files['image']['error'] === UPLOAD_ERR_OK) { + $uploadDir = __DIR__ . '/../assets/images/recipes/'; + if (!is_dir($uploadDir)) { + mkdir($uploadDir, 0777, true); + } + $filename = uniqid() . '-' . basename($files['image']['name']); + $uploadFile = $uploadDir . $filename; + + if (move_uploaded_file($files['image']['tmp_name'], $uploadFile)) { + $imageUrl = 'assets/images/recipes/' . $filename; + } else { + throw new Exception('Failed to move uploaded file.'); + } + } + $pdo->beginTransaction(); if (isset($data['id']) && !empty($data['id'])) { // Update existing recipe $recipeId = $data['id']; $category = !empty($data['category']) ? $data['category'] : 'No category'; - $stmt = $pdo->prepare("UPDATE recipes SET name = ?, guests = ?, category = ? WHERE id = ?"); - $stmt->execute([$data['name'], $data['guests'], $category, $recipeId]); + + // Fetch existing image URL if a new one isn't uploaded + if ($imageUrl === null) { + $stmt = $pdo->prepare("SELECT image_url FROM recipes WHERE id = ?"); + $stmt->execute([$recipeId]); + $existing = $stmt->fetch(); + $imageUrl = $existing ? $existing['image_url'] : null; + } + + $stmt = $pdo->prepare("UPDATE recipes SET name = ?, guests = ?, category = ?, image_url = ? WHERE id = ?"); + $stmt->execute([$data['name'], $data['guests'], $category, $imageUrl, $recipeId]); // Easiest way to handle ingredients is to delete old ones and insert new ones $stmt = $pdo->prepare("DELETE FROM ingredients WHERE recipe_id = ?"); $stmt->execute([$recipeId]); - $stmt = $pdo->prepare("INSERT INTO ingredients (recipe_id, name, quantity, unit) VALUES (?, ?, ?, ?)"); - foreach ($data['ingredients'] as $ing) { - $stmt->execute([$recipeId, $ing['name'], $ing['quantity'], $ing['unit']]); - } - } else { // Insert new recipe $category = !empty($data['category']) ? $data['category'] : 'No category'; - $stmt = $pdo->prepare("INSERT INTO recipes (name, guests, category) VALUES (?, ?, ?)"); - $stmt->execute([$data['name'], $data['guests'], $category]); + $stmt = $pdo->prepare("INSERT INTO recipes (name, guests, category, image_url) VALUES (?, ?, ?, ?)"); + $stmt->execute([$data['name'], $data['guests'], $category, $imageUrl]); $recipeId = $pdo->lastInsertId(); + } - $stmt = $pdo->prepare("INSERT INTO ingredients (recipe_id, name, quantity, unit) VALUES (?, ?, ?, ?)"); - foreach ($data['ingredients'] as $ing) { - $stmt->execute([$recipeId, $ing['name'], $ing['quantity'], $ing['unit']]); - } + // Insert ingredients + $stmt = $pdo->prepare("INSERT INTO ingredients (recipe_id, name, quantity, unit) VALUES (?, ?, ?, ?)"); + foreach ($ingredients as $ing) { + $stmt->execute([$recipeId, $ing['name'], $ing['quantity'], $ing['unit']]); } $pdo->commit(); - // Fetch the newly created recipe to return it to the client + // Fetch the newly created/updated recipe to return it to the client $stmt = $pdo->prepare("SELECT * FROM recipes WHERE id = ?"); $stmt->execute([$recipeId]); $recipe = $stmt->fetch(); $stmt = $pdo->prepare("SELECT * FROM ingredients WHERE recipe_id = ?"); $stmt->execute([$recipeId]); - $ingredients = $stmt->fetchAll(); - $recipe['ingredients'] = $ingredients; + $recipe['ingredients'] = $stmt->fetchAll(); echo json_encode(['success' => true, 'recipe' => $recipe]); -} catch (PDOException $e) { - $pdo->rollBack(); +} catch (Exception $e) { + if ($pdo->inTransaction()) { + $pdo->rollBack(); + } echo json_encode(['success' => false, 'error' => $e->getMessage()]); -} +} \ No newline at end of file diff --git a/assets/css/custom.css b/assets/css/custom.css index 4831251..e408343 100644 --- a/assets/css/custom.css +++ b/assets/css/custom.css @@ -3,6 +3,7 @@ body { background-color: #142E35; /* Dark green background */ color: #ffffff; /* White text */ font-family: 'Poppins', sans-serif; + padding-top: 50px; } /* Headings */ @@ -27,7 +28,7 @@ body::before { width: 110%; height: 20px; background: - radial-gradient(circle, #C83434 4px, transparent 5px), + radial-gradient(circle, #de4950 4px, transparent 5px), radial-gradient(circle, #142E35 4px, transparent 5px), radial-gradient(circle, #FFAFCA 4px, transparent 5px), radial-gradient(circle, #ffff24 4px, transparent 5px), @@ -103,8 +104,8 @@ body::before { /* Buttons */ .btn-primary, .btn-danger { - background-color: #C83434 !important; /* Coral red */ - border-color: #C83434 !important; + background-color: #de4950 !important; /* Coral red */ + border-color: #de4950 !important; font-weight: 600; padding: 12px 30px; transition: all 0.3s ease; @@ -114,7 +115,7 @@ body::before { background-color: #a02929 !important; border-color: #a02929 !important; transform: translateY(-2px); - box-shadow: 0 4px 15px rgba(200, 52, 52, 0.2); + box-shadow: 0 4px 15px rgba(222, 73, 80, 0.2); } .btn-outline-secondary { @@ -253,8 +254,8 @@ animation: fall linear infinite; } .form-check-input:checked { - background-color: #C83434; - border-color: #C83434; + background-color: #de4950; + border-color: #de4950; } .form-check-input:focus { @@ -386,5 +387,49 @@ animation: fall linear infinite; padding: .25rem .5rem; } +#christmas-decorations-right { + position: fixed; + top: 0px; + right: 250px; + width: 200px; + z-index: 1033; +} + +#christmas-decorations-right img { + width: 100%; +} + +/* Recipe Card Image */ +.card .card-img-top { + height: 200px; + object-fit: cover; +} + +/* Overlay category label */ +.card .recipe-category-label { + top: 15px; + right: 15px; + background-color: rgba(20, 46, 53, 0.8); + backdrop-filter: blur(5px); +} + +/* Recipe Card Animation */ +.recipe-card-enter { + animation: fade-in 0.5s ease-in-out; +} + +@keyframes fade-in { + from { + opacity: 0; + transform: translateY(10px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + + + diff --git a/assets/js/main.js b/assets/js/main.js index 654379d..70f8a5f 100644 --- a/assets/js/main.js +++ b/assets/js/main.js @@ -20,12 +20,11 @@ const app = { app.dom.recipeCardsContainer.innerHTML = '
Could not connect to the server.
+ Let's get your holiday recipes sorted.