diff --git a/frontend/components/Domain/Recipe/RecipePage/RecipePage.vue b/frontend/components/Domain/Recipe/RecipePage/RecipePage.vue index 921316015..b3e578b7e 100644 --- a/frontend/components/Domain/Recipe/RecipePage/RecipePage.vue +++ b/frontend/components/Domain/Recipe/RecipePage/RecipePage.vue @@ -13,6 +13,22 @@ {{ $t("general.discard-changes-description") }} + + +

+ {{ saveErrorMessage }} +

+

+ {{ $t('recipe.save-error-description') }} +

+
+
{ */ const originalRecipe = ref(null); const discardDialog = ref(false); +const saveErrorDialog = ref(false); +const saveErrorMessage = ref(""); const pendingRoute = ref(null); invoke(async () => { @@ -353,8 +371,9 @@ async function saveRecipe() { if (!error) { setMode(PageMode.VIEW); } else { - // Restore original recipe data on error to prevent data loss - restoreOriginalRecipe(); + // Show prominent error dialog for save failures + saveErrorMessage.value = error?.response?.data?.detail?.message || "An error occurred while saving the recipe."; + saveErrorDialog.value = true; return; } if (data?.slug) { diff --git a/frontend/lang/messages/en-US.json b/frontend/lang/messages/en-US.json index 9e67a2adc..49c0afb97 100644 --- a/frontend/lang/messages/en-US.json +++ b/frontend/lang/messages/en-US.json @@ -530,6 +530,8 @@ "recipe-settings": "Recipe Settings", "recipe-update-failed": "Recipe update failed", "recipe-updated": "Recipe updated", + "save-error": "Unable to Save Recipe", + "save-error-description": "Your changes have been preserved in the editor. Please fix the issue and try saving again.", "remove-from-favorites": "Remove from Favorites", "remove-section": "Remove Section", "saturated-fat-content": "Saturated fat", diff --git a/mealie/routes/recipe/recipe_crud_routes.py b/mealie/routes/recipe/recipe_crud_routes.py index 14c348ca4..8c88b761b 100644 --- a/mealie/routes/recipe/recipe_crud_routes.py +++ b/mealie/routes/recipe/recipe_crud_routes.py @@ -92,7 +92,10 @@ class RecipeController(BaseRecipeController): elif thrownType == sqlalchemy.exc.IntegrityError: self.logger.error("SQL Integrity Error on recipe controller action") raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, detail=ErrorResponse.respond(message="Recipe already exists") + status_code=status.HTTP_400_BAD_REQUEST, + detail=ErrorResponse.respond( + message="A recipe with this name already exists. Please choose a different name and try saving again." + ) ) elif thrownType == exceptions.RecursiveRecipe: self.logger.error("Recursive Recipe Link Error on recipe controller action") diff --git a/tests/integration_tests/user_recipe_tests/test_recipe_crud.py b/tests/integration_tests/user_recipe_tests/test_recipe_crud.py index 733bb3bdd..6d4f75735 100644 --- a/tests/integration_tests/user_recipe_tests/test_recipe_crud.py +++ b/tests/integration_tests/user_recipe_tests/test_recipe_crud.py @@ -1327,7 +1327,7 @@ def test_create_recipe_slug_length_validation(api_client: TestClient, unique_use def test_recipe_update_duplicate_name_error(api_client: TestClient, unique_user: TestUser): - """Test that updating a recipe with a duplicate name returns a 400 error.""" + """Test that updating a recipe with a duplicate name returns a 400 error with a helpful message.""" # Create two recipes with different names recipe1_name = random_string(10) recipe2_name = random_string(10) @@ -1353,12 +1353,15 @@ def test_recipe_update_duplicate_name_error(api_client: TestClient, unique_user: headers=unique_user.token ) - # Should return 400 error with appropriate message + # Should return 400 error with helpful message assert update_response.status_code == 400 response_data = json.loads(update_response.text) - assert "already exists" in response_data["detail"]["message"].lower() + error_message = response_data["detail"]["message"] + assert "already exists" in error_message.lower() + assert "choose a different name" in error_message.lower() + assert "try saving again" in error_message.lower() - # Verify original recipe2 is unchanged + # Verify original recipe2 is unchanged in the database get_response = api_client.get(api_routes.recipes_slug(recipe2_slug), headers=unique_user.token) assert get_response.status_code == 200 unchanged_recipe = json.loads(get_response.text)