diff --git a/application/recipes/__pycache__/database_recipes.cpython-313.pyc b/application/recipes/__pycache__/database_recipes.cpython-313.pyc index 66f6f93..147a3b5 100644 Binary files a/application/recipes/__pycache__/database_recipes.cpython-313.pyc and b/application/recipes/__pycache__/database_recipes.cpython-313.pyc differ diff --git a/application/recipes/__pycache__/recipes_api.cpython-313.pyc b/application/recipes/__pycache__/recipes_api.cpython-313.pyc index ee5a6fa..241808b 100644 Binary files a/application/recipes/__pycache__/recipes_api.cpython-313.pyc and b/application/recipes/__pycache__/recipes_api.cpython-313.pyc differ diff --git a/application/recipes/database_recipes.py b/application/recipes/database_recipes.py index 920822d..b6fad27 100644 --- a/application/recipes/database_recipes.py +++ b/application/recipes/database_recipes.py @@ -190,6 +190,20 @@ def postDeleteRecipeItem(site:str, payload:tuple, convert:bool=True): database_config = config.config() deleted = () sql = f"DELETE FROM {site}_recipe_items WHERE id=%s RETURNING *;" + with psycopg2.connect(**database_config) as conn: + with conn.cursor() as cur: + cur.execute(sql, payload) + rows = cur.fetchone() + if rows and convert: + deleted = postsqldb.tupleDictionaryFactory(cur.description, rows) + elif rows and not convert: + deleted = rows + return deleted + +def deleteRecipe(site:str, payload:tuple, convert:bool=True): + database_config = config.config() + deleted = () + sql = f"DELETE FROM {site}_recipes WHERE id=%s RETURNING *;" with psycopg2.connect(**database_config) as conn: with conn.cursor() as cur: cur.execute(sql, payload) diff --git a/application/recipes/recipes_api.py b/application/recipes/recipes_api.py index 8157925..45a4b83 100644 --- a/application/recipes/recipes_api.py +++ b/application/recipes/recipes_api.py @@ -46,6 +46,16 @@ def getRecipes(): return jsonify({'recipes': recipes, 'end': math.ceil(count/limit), 'error': False, 'message': 'fetch was successful!'}) return jsonify({'recipes': recipes, 'end': math.ceil(count/limit), 'error': True, 'message': f'method is not allowed: {request.method}'}) +@recipes_api.route('/api/deleteRecipe', methods=["POST"]) +@access_api.login_required +def deleteRecipe(): + if request.method == "POST": + recipe_id = request.get_json()['recipe_id'] + site_name = session['selected_site'] + database_recipes.deleteRecipe(site_name, (recipe_id, )) + return jsonify(status=201, message="Recipe deleted successfully!") + return jsonify(status=405, message=f"{request.method} is not an allowed method on this endpoint.") + @recipes_api.route('/getRecipe', methods=["GET"]) @access_api.login_required def getRecipe(): diff --git a/application/recipes/static/js/recipesListHandler.js b/application/recipes/static/js/recipesListHandler.js index cda669f..9989c49 100644 --- a/application/recipes/static/js/recipesListHandler.js +++ b/application/recipes/static/js/recipesListHandler.js @@ -177,17 +177,25 @@ async function replenishRecipesCards() { footer_div.style = 'height: 40px; border: none;' let editOp = document.createElement('a') - editOp.setAttribute('class', 'uk-button uk-button-small uk-button-default') + editOp.setAttribute('class', 'uk-button uk-button-small uk-button-primary') editOp.innerHTML = ' Edit' editOp.style = "margin-right: 10px;" editOp.href = `/recipes/edit/${recipes[i].id}` let viewOp = document.createElement('a') - viewOp.setAttribute('class', 'uk-button uk-button-small uk-button-default') + viewOp.setAttribute('class', 'uk-button uk-button-small uk-button-primary') viewOp.innerHTML = ' View' viewOp.href = `/recipes/view/${recipes[i].id}` + + let deleteOp = document.createElement('button') + deleteOp.setAttribute('class', 'uk-button uk-button-small uk-button-danger') + deleteOp.innerHTML = ' Delete' + deleteOp.onclick = async function() { + await openDeleteRecipeModal(recipes[i].id) + } - footer_div.append(editOp, viewOp) + + footer_div.append(editOp, viewOp, deleteOp) main_div.append(card_header_div, body_div, footer_div) @@ -242,6 +250,53 @@ async function addRecipe() { } +var select_recipe_id_to_delete = 0 +async function openDeleteRecipeModal(recipe_id) { + select_recipe_id_to_delete = recipe_id + document.getElementById('deleteRecipeConfirm').value = "" + UIkit.modal(document.getElementById('deleteRecipeModal')).show(); +} + + +async function deleteRecipe() { + let confirm = String(document.getElementById('deleteRecipeConfirm').value) + if (confirm === "DELETE"){ + const response = await fetch(`/recipes/api/deleteRecipe`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + recipe_id: select_recipe_id_to_delete + }), + }); + data = await response.json(); + transaction_status = "success" + if (data.error){ + transaction_status = "danger" + } + + UIkit.notification({ + message: data.message, + status: transaction_status, + pos: 'top-right', + timeout: 5000 + }); + recipes = await getRecipes() + await replenishRecipes() + await updatePaginationElement() + UIkit.modal(document.getElementById('deleteRecipeModal')).hide(); + } else { + UIkit.modal(document.getElementById('deleteRecipeModal')).hide(); + UIkit.notification({ + message: "Confirmation Incorrect!", + status: "danger", + pos: 'top-right', + timeout: 5000 + }); + } +} + async function updatePaginationElement() { let paginationElement = document.getElementById("paginationElement"); paginationElement.innerHTML = ""; diff --git a/application/recipes/templates/recipes_index.html b/application/recipes/templates/recipes_index.html index ed76676..841695c 100644 --- a/application/recipes/templates/recipes_index.html +++ b/application/recipes/templates/recipes_index.html @@ -156,6 +156,26 @@ + +