From 96ab7c056e4bdc7d2d37f52a18ac0e1c839c8e59 Mon Sep 17 00:00:00 2001 From: "[esekyi]" <[sskert10@gmail.com]> Date: Fri, 6 Sep 2024 04:37:50 +0000 Subject: [PATCH] =?UTF-8?q?[Updates=F0=9F=93=A2]=20MVP=20Complete?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/routes/recipe_routes.py | 32 ++-- app/services/recipe_service.py | 55 +++--- app/static/css/recipe_form.css | 1 - app/static/js/edit_script.js | 168 ++++++++++++++++++ app/static/js/recipe_form.js | 4 +- app/templates/recipes/createPages/create.html | 6 + .../recipes/createPages/create_layout.html | 3 +- app/templates/recipes/createPages/edit.html | 145 +++++++++++++++ .../recipes/readPages/allRecipes.html | 25 +-- .../recipes/readPages/recipe_detail.html | 19 +- 10 files changed, 401 insertions(+), 57 deletions(-) create mode 100644 app/static/js/edit_script.js diff --git a/app/routes/recipe_routes.py b/app/routes/recipe_routes.py index 351507b..3ec4be7 100644 --- a/app/routes/recipe_routes.py +++ b/app/routes/recipe_routes.py @@ -7,6 +7,7 @@ from app.services.comment_service import CommentService from flask_login import login_required, current_user from app.models.user import User +from app.models.category import Category from app.models.comment import Comment import os @@ -102,42 +103,49 @@ def edit_recipe(recipe_id): if request.method == 'POST': if recipe and recipe.user_id == current_user.id: - data = request.form.to_dict() - ingredients = request.form.getlist('ingredients[]') - instructions = request.form.getlist('instructions[]') + data = request.form.to_dict() # Ensure this is a dictionary + + form_ingredients = request.form.getlist('ingredients[]') + form_instructions = request.form.getlist('instructions[]') image = request.files.get('image') + # If they are strings instead of lists + if isinstance(ingredients, str): + ingredients = [ingredients] + if isinstance(instructions, str): + instructions = [instructions] + + errors = validate_recipe_data(data) if errors: for error in errors: flash(error, 'error') - return redirect(url_for('recipe_routes.add_recipe', recipe_id=recipe_id)) + return redirect(url_for('recipe_routes.edit_recipe', recipe_id=recipe_id)) image_url = recipe.image_url if image: try: image_url = upload_image_to_s3(image) - flash("Image updated successfully", "succes") + flash("Image updated successfully", "success") except ValueError as e: flash(str(e), 'error') return redirect(url_for('recipe_routes.edit_recipe', recipe_id=recipe_id)) try: - update_recipe(recipe, data, ingredients, - instructions, image_url) - flash( - f"Recipe {recipe.title} updated successfully!", 'success') + update_recipe(recipe, data, form_ingredients, form_instructions, image_url) # Ensure data is a dict + flash(f"Recipe {recipe.title} updated successfully!", 'success') return redirect(url_for('recipe_routes.view_recipe', recipe_id=recipe_id)) except Exception as e: # Rollback db session in case of an error db.session.rollback() - flash( - f"An error occurred while creating the recipe: {str(e)}", "error") + flash(f"An error occurred while updating the recipe: {str(e)}", "error") return redirect(url_for('recipe_routes.edit_recipe', recipe_id=recipe_id)) categories = CategoryService.get_all_categories() - return render_template('recipes/createPages/edit1.html', recipe=recipe, ingredients=ingredients, instructions=instructions, categories=categories, title=f'Edit Recipe {recipe.title} | SpiceShare Inc.') + current_category = db.session.query( + Category).filter_by(id=recipe.category_id).first() + return render_template('recipes/createPages/edit.html', recipe=recipe, ingredients=ingredients, current_category=current_category, instructions=instructions, categories=categories, title=f'Edit Recipe {recipe.title} | SpiceShare Inc.') @bp.route('/recipes//delete', methods=['POST'], strict_slashes=False) diff --git a/app/services/recipe_service.py b/app/services/recipe_service.py index 2f6869c..5f16d8c 100644 --- a/app/services/recipe_service.py +++ b/app/services/recipe_service.py @@ -79,7 +79,7 @@ def get_all_recipes(page=1, per_page=3): dict: A dictionary containing paginated recipes and pagination info. """ - recipes = Recipe.query.order_by(Recipe.created_at.desc()).all() + recipes = Recipe.query.order_by(Recipe.updated_at.desc()).all() return paginate(recipes, page, per_page) @@ -88,7 +88,7 @@ def update_recipe(recipe, data, ingredients, instructions, image_url): try: recipe.title = data.get('title', recipe.title) recipe.description = data.get('description', recipe.description) - recipe.category_id = data.grt('category_id', recipe.category_id) + recipe.category_id = data.get('category_id', recipe.category_id) recipe.oven_temp = int(data.get('oven_temp', recipe.oven_temp)) if data.get( 'oven_temp') else recipe.oven_temp recipe.prep_time = int(data.get('prep_time', recipe.prep_time)) if data.get( @@ -101,36 +101,47 @@ def update_recipe(recipe, data, ingredients, instructions, image_url): # Update ingredients existing_ingredients = { - ing.id: ing for ing in Ingredient.query.filter_by(recipe_id=recipe.id).all()} - for ingredient_data in ingredients: - if ingredient_data: - ingredient_id = ingredient_data.get('id') - if ingredient_id and ingredient_id in existing_ingredients: - existing_ingredients[ingredient_id].name = ingredient_data['name'] + ing.id: ing for ing in Ingredient.query.filter_by(recipe_id=recipe.id).all() + } + for idx, ingredient_name in enumerate(ingredients): + if ingredient_name: + ingredient_id = list(existing_ingredients.keys())[ + idx] if idx < len(existing_ingredients) else None + if ingredient_id: + # Update existing ingredient + existing_ingredients[ingredient_id].name = ingredient_name else: - # add new ingredient + # Add new ingredient new_ingredient = Ingredient( - name=ingredient_data['name'], - recipe_id=recipe.id - ) - deb.session.add(new_ingredient) + name=ingredient_name, recipe_id=recipe.id) + db.session.add(new_ingredient) + + # Remove any extra ingredients not submitted in the form + for extra_idx in range(len(ingredients), len(existing_ingredients)): + db.session.delete(list(existing_ingredients.values())[extra_idx]) + # Update instructions - existing_instructions = { - instr.step_number: instr for instr in Instruction.query.filter_by(recipe_id=recipe.id).all()} - for i, instruction in enumerate(instructions): - if instruction: - if i + 1 in existing_instructions: - # update existing instructions - existing_instructions[i + 1].name = instruction + existing_instructions = {instr.step_number: instr for instr in Instruction.query.filter_by(recipe_id=recipe.id).all()} + for idx, instruction_text in enumerate(instructions): + if instruction_text: + step_number = idx + 1 + if step_number in existing_instructions: + # Update existing instruction + existing_instructions[step_number].name = instruction_text else: + # Add new instruction new_instruction = Instruction( - step_number=i + 1, - name=instruction, + step_number=step_number, + name=instruction_text, recipe_id=recipe.id ) db.session.add(new_instruction) + # Remove any extra instructions not submitted in the form + for extra_idx in range(len(instructions) + 1, len(existing_instructions) + 1): + db.session.delete(existing_instructions[extra_idx]) + db.session.commit() except Exception as e: diff --git a/app/static/css/recipe_form.css b/app/static/css/recipe_form.css index 03e232b..12cc714 100644 --- a/app/static/css/recipe_form.css +++ b/app/static/css/recipe_form.css @@ -19,7 +19,6 @@ padding: 16px; border-radius: 8px; background-color: #F9FAFB; - margin-bottom: 16px; } .dropdown-menu { diff --git a/app/static/js/edit_script.js b/app/static/js/edit_script.js new file mode 100644 index 0000000..c967063 --- /dev/null +++ b/app/static/js/edit_script.js @@ -0,0 +1,168 @@ +document.addEventListener('DOMContentLoaded', function () +{ + const flashMessages = document.querySelectorAll('.flash-message'); + + flashMessages.forEach(function (flash) + { + setTimeout(function () + { + flash.style.display = 'none'; + }, 5000); // Flash message disappears after 5 seconds + }); +}); + + +// handle upload +document.addEventListener('DOMContentLoaded', function () +{ + document.getElementById('add-photo-btn').addEventListener('click', function (event) + { + event.preventDefault(); // Prevent default button action + document.getElementById('image-input').click(); + }); + + document.getElementById('image-input').addEventListener('change', function (event) + { + const file = event.target.files[0]; + if (file) + { + const reader = new FileReader(); + reader.onload = function (e) + { + const imagePreview = document.getElementById('image-preview'); + console.log('Image data URL:', e.target.result); // Debugging + imagePreview.src = e.target.result; + imagePreview.style.display = 'block'; // Show the image preview + }; + reader.readAsDataURL(file); + } + }); + +}); + +// handle form drop down - not using select input +function toggleDropdown() +{ + const dropdownMenu = document.getElementById('dropdownMenu'); + dropdownMenu.classList.toggle('show'); +} + +function selectCategory(categoryName, categoryId) +{ + const categoryInput = document.getElementById('categoryInput'); + const categoryIdInput = document.getElementById('categoryIdInput'); + + // Set the selected category name in the visible input + categoryInput.value = categoryName; + // Set the selected category ID in the hidden input + categoryIdInput.value = categoryId; + toggleDropdown(); +} + +// Close dropdown if clicked outside +document.addEventListener('click', function (event) +{ + const dropdownMenu = document.getElementById('dropdownMenu'); + const categoryInput = document.getElementById('categoryInput'); + if (!categoryInput.contains(event.target) && !dropdownMenu.contains(event.target)) + { + dropdownMenu.classList.remove('show'); + } +}); + +function updateStepNumbers() +{ + const directionCards = document.querySelectorAll('.direction-card .step-number'); + directionCards.forEach((step, index) => + { + step.textContent = index + 1; + }); +} + +function addIngredient() +{ + const ingredientCard = document.createElement('div'); + ingredientCard.className = 'recipe-card'; + ingredientCard.innerHTML = ` +
+ + +
+ `; + document.getElementById('ingredients-list').appendChild(ingredientCard); +} + +function removeIngredient(element) +{ + element.closest('.recipe-card').remove(); +} + +function addDirection() +{ + const directionCard = document.createElement('div'); + directionCard.className = 'recipe-card mb-2'; + directionCard.innerHTML = ` +
+ {{ loop.index }} + + +
+ `; + document.getElementById('directions-list').appendChild(directionCard); + updateStepNumbers() +} + +function removeDirection(element) +{ + element.closest('.recipe-card').remove(); + updateStepNumbers() +} + +function initializeSortable() +{ + new Sortable(document.getElementById('ingredients-list'), { + animation: 150, + ghostClass: 'bg-yellow-100', + onEnd: function () + { + updateStepNumbers(); // Update step numbers after sorting + } + }); + + new Sortable(document.getElementById('directions-list'), { + animation: 150, + ghostClass: 'bg-yellow-100' + }); +} + +document.addEventListener('DOMContentLoaded', function () +{ + initializeSortable() + updateStepNumbers(); + // Handle form submission and log to console + document.getElementById('recipe-form').addEventListener('submit', function (event) + { + //event.preventDefault(); // Prevent form from submitting + + // Get all ingredients + const ingredients = Array.from(document.querySelectorAll('.ingredient-card input')).map( + input => input.value).filter(value => value.trim() != ''); + + // Get all directions + const directions = Array.from(document.querySelectorAll('.direction-card input')).map( + input => input.value).filter(value => value.trim() != ''); + + // Log the data to the console + console.log('Ingredients:', ingredients); + console.log('Directions:', directions); + + // You can remove or comment out this part when you want to submit to the backend + this.submit(); // Uncomment to submit the form to the backend after testing + }); +}); + + + + diff --git a/app/static/js/recipe_form.js b/app/static/js/recipe_form.js index 0d1f040..f90241b 100644 --- a/app/static/js/recipe_form.js +++ b/app/static/js/recipe_form.js @@ -83,7 +83,7 @@ function addDirection() const directionCard = document.createElement('div'); directionCard.className = 'recipe-card'; directionCard.innerHTML = ` -
+
@@ -131,4 +131,4 @@ document.addEventListener('DOMContentLoaded', function () // You can remove or comment out this part when you want to submit to the backend this.submit(); // Uncomment to submit the form to the backend after testing }); - }); \ No newline at end of file +}); diff --git a/app/templates/recipes/createPages/create.html b/app/templates/recipes/createPages/create.html index 875b9f7..a8cf4ea 100644 --- a/app/templates/recipes/createPages/create.html +++ b/app/templates/recipes/createPages/create.html @@ -116,3 +116,9 @@

Instructions

{% endblock %} + + {% block scripts %} + + {% endblock scripts %} + + diff --git a/app/templates/recipes/createPages/create_layout.html b/app/templates/recipes/createPages/create_layout.html index 93843eb..0b147d8 100644 --- a/app/templates/recipes/createPages/create_layout.html +++ b/app/templates/recipes/createPages/create_layout.html @@ -74,8 +74,9 @@
- + {% block scripts %} + {% endblock scripts %} \ No newline at end of file diff --git a/app/templates/recipes/createPages/edit.html b/app/templates/recipes/createPages/edit.html index e69de29..1c5f695 100644 --- a/app/templates/recipes/createPages/edit.html +++ b/app/templates/recipes/createPages/edit.html @@ -0,0 +1,145 @@ +{% extends "recipes/createPages/create_layout.html" %} + +{% block content %} +
+ + +

Make it Even Better...

+
+ +
+ {% if recipe.image_url %} + Image Preview + {% else %} + Image Preview + {% endif %} +
+ +
+ +
+
+ {% if recipe %} + +
+
+ + +
+
+ + +
+
+ +
+ + °F +
+
+ {% endif %} +
+ {% if categories %} + + + + + + + +
+
+ +
+
+ +
+ + min +
+
+
+ +
+ + min +
+
+
+ +
+ servings +
+
+
+ + +
+

Ingredients

+
+ + {% for ingredient in ingredients %} +
+
+ + +
+
+ {% endfor %} +
+
+ +
+
+ + +
+

Instructions

+
+ + {% for instruction in instructions %} +
+
+ {{ loop.index }} + + +
+
+ + {% endfor %} +
+
+ +
+
+ + +
+ + +
+
+{% endblock %} + +{% block scripts %} + +{% endblock scripts %} \ No newline at end of file diff --git a/app/templates/recipes/readPages/allRecipes.html b/app/templates/recipes/readPages/allRecipes.html index 2b1bd64..af63943 100644 --- a/app/templates/recipes/readPages/allRecipes.html +++ b/app/templates/recipes/readPages/allRecipes.html @@ -10,7 +10,7 @@

Recipes

class="w-full border border-gray-300 rounded-lg py-2 px-4 mb-6">
- + {% for recipe in recipes %}
@@ -31,6 +31,19 @@

{{recipe.title}}

{% endfor %}
+ +
+
+ {% if current_page > 1 %} + Previous + {% endif %} + Page {{ current_page }} of {{ total_pages }} + {% if current_page < total_pages %} Next + {% endif %} +
+

Most Recent

@@ -64,14 +77,4 @@

Savory Porridge

- - {% endblock %} \ No newline at end of file diff --git a/app/templates/recipes/readPages/recipe_detail.html b/app/templates/recipes/readPages/recipe_detail.html index 29f86c5..5d6f12f 100644 --- a/app/templates/recipes/readPages/recipe_detail.html +++ b/app/templates/recipes/readPages/recipe_detail.html @@ -9,19 +9,22 @@ {% if recipe %}

{{recipe.title}}

-

{{ recipe.created_at.strftime('%B %d, %Y') }} at {{ recipe.created_at.strftime('%I:%M %p') }}

+

+ PUBLISHED: {{ recipe.created_at.strftime('%d %b \'%y') if recipe.created_at else 'N/A' }}   + UPDATED: {{ recipe.updated_at.strftime('%d %b \'%y') if recipe.updated_at else 'N/A' }} +

By: {{recipe.user.username}}

+ {% if recipe.image_url %}
- {% if recipe.image_url %} - {{ recipe.title }} - {% else %} - {{ recipe.title }} - {% endif %} + {{ recipe.title }} + {% else %} + {{ recipe.title }} + {% endif %}