diff --git a/src/main/java/com/example/demo/config/SecurityConfig.java b/src/main/java/com/example/demo/config/SecurityConfig.java index 1a18e3a..e15f2cc 100644 --- a/src/main/java/com/example/demo/config/SecurityConfig.java +++ b/src/main/java/com/example/demo/config/SecurityConfig.java @@ -19,18 +19,11 @@ public class SecurityConfig { public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth - // public pages and static files - .requestMatchers("/", "/login", "/register", "/css/**", "/images/**").permitAll() + .requestMatchers("/", "/login", "/register", "/css/**", "/images/**", "/uploads/**").permitAll() .requestMatchers("/api/users").permitAll() .requestMatchers("/api/admin/**").hasRole("ADMIN") - - // protected create recipe routes .requestMatchers("/create", "/api/recipes/upload").authenticated() - - // protected my profile routes .requestMatchers("/my-profile", "/my-profile/update").authenticated() - - // everything else public .anyRequest().permitAll() ) .formLogin(form -> form diff --git a/src/main/java/com/example/demo/config/WebConfig.java b/src/main/java/com/example/demo/config/WebConfig.java index 5bb60eb..6bb7d65 100644 --- a/src/main/java/com/example/demo/config/WebConfig.java +++ b/src/main/java/com/example/demo/config/WebConfig.java @@ -3,6 +3,7 @@ package com.example.demo.config; import java.nio.file.Path; import java.nio.file.Paths; +import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @@ -10,11 +11,14 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class WebConfig implements WebMvcConfigurer { + @Value("${app.upload.dir}") + private String uploadDir; + @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { - Path uploadPath = Paths.get("uploads").toAbsolutePath().normalize(); + Path uploadPath = Paths.get(uploadDir).toAbsolutePath().normalize(); registry.addResourceHandler("/uploads/**") - .addResourceLocations("file:///" + uploadPath.toString().replace("\\", "/") + "/"); + .addResourceLocations(uploadPath.toUri().toString()); } } \ No newline at end of file diff --git a/src/main/java/com/example/demo/controller/ProfileController.java b/src/main/java/com/example/demo/controller/ProfileController.java index 546d3bb..069c3bd 100644 --- a/src/main/java/com/example/demo/controller/ProfileController.java +++ b/src/main/java/com/example/demo/controller/ProfileController.java @@ -23,9 +23,10 @@ public class ProfileController { } @GetMapping("/users/{id}") - public String viewPublicProfile(@PathVariable Integer id, Model model) { + public String viewPublicProfile(@PathVariable Integer id, Model model, Principal principal) { ProfileDto profile = userService.getProfileByUserId(id); model.addAttribute("profile", profile); + model.addAttribute("guest", principal == null); return "public-profile"; } @@ -45,6 +46,21 @@ public class ProfileController { return "my-profile"; } + @PostMapping("/my-profile/update") + public String updateMyProfile(@ModelAttribute UpdateProfileDto dto, Principal principal) { + String username = principal.getName(); + userService.updateProfile(username, dto); + return "redirect:/my-profile"; + } +} + updateProfileDto.setBio(profile.getBio()); + + model.addAttribute("profile", profile); + model.addAttribute("updateProfileDto", updateProfileDto); + + return "my-profile"; + } + @PostMapping("/my-profile/update") public String updateMyProfile(@ModelAttribute UpdateProfileDto dto, Principal principal) { String username = principal.getName(); diff --git a/src/main/java/com/example/demo/controller/RecipeUploadForm.java b/src/main/java/com/example/demo/controller/RecipeUploadForm.java index c0c2a2e..1477ea8 100644 --- a/src/main/java/com/example/demo/controller/RecipeUploadForm.java +++ b/src/main/java/com/example/demo/controller/RecipeUploadForm.java @@ -1,141 +1,233 @@ package com.example.demo.controller; +import java.io.IOException; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.security.Principal; +import java.util.ArrayList; import java.util.List; +import java.util.UUID; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.util.StringUtils; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; -public class RecipeUploadForm { +import com.example.demo.dto.ImageDto; +import com.example.demo.dto.RecipeDto; +import com.example.demo.dto.RecipeIngredientDto; +import com.example.demo.dto.StepDto; +import com.example.demo.dto.TagDto; +import com.example.demo.service.RecipeService; - private String title; - private String description; - private String prepTimeMinutes; - private String cookTimeMinutes; - private String servings; - private String cost; +@RestController +public class RecipeUploadController { - private List ingredientName; - private List ingredientQuantity; - private List ingredientUnit; - private List ingredientNotes; + private final RecipeService recipeService; - private List stepInstruction; - private List tags; - - private MultipartFile image; - private Boolean removeImage; - - public RecipeUploadForm() { + public RecipeUploadController(RecipeService recipeService) { + this.recipeService = recipeService; } - public String getTitle() { - return title; + @PostMapping(value = "/api/recipes/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + public ResponseEntity createRecipeWithUpload(@ModelAttribute RecipeUploadForm form, Principal principal) { + try { + RecipeDto dto = buildRecipeDto(form, true); + String currentUsername = principal.getName(); + RecipeDto saved = recipeService.saveRecipe(dto, currentUsername); + return new ResponseEntity<>(saved, HttpStatus.CREATED); + + } catch (IllegalArgumentException e) { + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage()); + } catch (Exception e) { + e.printStackTrace(); + return ResponseEntity.status(HttpStatus.BAD_REQUEST) + .body("Recipe creation failed: " + e.getMessage()); + } } - public void setTitle(String title) { - this.title = title; + @PostMapping(value = "/api/recipes/{id}/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + public ResponseEntity updateRecipeWithUpload( + @PathVariable Integer id, + @ModelAttribute RecipeUploadForm form, + Principal principal) { + try { + RecipeDto dto = buildRecipeDto(form, false); + String currentUsername = principal.getName(); + RecipeDto updated = recipeService.updateRecipe(dto, id, currentUsername); + return new ResponseEntity<>(updated, HttpStatus.OK); + + } catch (IllegalArgumentException e) { + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage()); + } catch (Exception e) { + e.printStackTrace(); + return ResponseEntity.status(HttpStatus.BAD_REQUEST) + .body("Recipe update failed: " + e.getMessage()); + } } - public String getDescription() { - return description; + private RecipeDto buildRecipeDto(RecipeUploadForm form, boolean isCreate) throws IOException { + RecipeDto dto = new RecipeDto(); + dto.setTitle(safeTrim(form.getTitle())); + dto.setDescription(safeTrim(form.getDescription())); + dto.setPrepTimeMinutes(parseInteger(form.getPrepTimeMinutes())); + dto.setCookTimeMinutes(parseInteger(form.getCookTimeMinutes())); + dto.setServings(parseInteger(form.getServings())); + dto.setStatus("DRAFT"); + dto.setCost(parseInteger(form.getCost())); + + List ingredientDtos = new ArrayList<>(); + if (form.getIngredientName() != null) { + for (int i = 0; i < form.getIngredientName().size(); i++) { + String ingredientName = getListValue(form.getIngredientName(), i); + if (ingredientName == null || ingredientName.isBlank()) { + continue; + } + + String quantityString = getListValue(form.getIngredientQuantity(), i); + BigDecimal quantity = null; + if (quantityString != null && !quantityString.isBlank()) { + quantity = parseQuantity(quantityString.trim()); + } + + String unit = getListValue(form.getIngredientUnit(), i); + String notes = getListValue(form.getIngredientNotes(), i); + + ingredientDtos.add(new RecipeIngredientDto( + ingredientName.trim(), + quantity, + unit != null ? unit.trim() : "", + notes != null ? notes.trim() : "")); + } + } + dto.setIngredients(ingredientDtos); + + List stepDtos = new ArrayList<>(); + if (form.getStepInstruction() != null) { + for (int i = 0; i < form.getStepInstruction().size(); i++) { + String instruction = form.getStepInstruction().get(i); + if (instruction == null || instruction.isBlank()) { + continue; + } + stepDtos.add(new StepDto(i + 1, instruction.trim())); + } + } + dto.setSteps(stepDtos); + + List tagDtos = new ArrayList<>(); + if (form.getTags() != null) { + for (String tag : form.getTags()) { + if (tag != null && !tag.isBlank()) { + tagDtos.add(new TagDto(tag.trim())); + } + } + } + dto.setTags(tagDtos); + + MultipartFile imageFile = form.getImage(); + boolean removeImage = Boolean.TRUE.equals(form.getRemoveImage()); + + if (imageFile != null && !imageFile.isEmpty()) { + List imageDtos = new ArrayList<>(); + String imageUrl = saveUploadedFile(imageFile); + imageDtos.add(new ImageDto(imageUrl)); + dto.setImages(imageDtos); + } else if (removeImage) { + dto.setImages(new ArrayList<>()); + } else if (isCreate) { + dto.setImages(new ArrayList<>()); + } else { + dto.setImages(null); + } + + return dto; } - public void setDescription(String description) { - this.description = description; + private Integer parseInteger(String value) { + if (value == null || value.isBlank()) { + return null; + } + return Integer.valueOf(value.trim()); } - public String getPrepTimeMinutes() { - return prepTimeMinutes; + private String getListValue(List list, int index) { + if (list == null || index >= list.size()) { + return null; + } + return list.get(index); } - public void setPrepTimeMinutes(String prepTimeMinutes) { - this.prepTimeMinutes = prepTimeMinutes; + private String safeTrim(String value) { + return value == null ? null : value.trim(); } - public String getCookTimeMinutes() { - return cookTimeMinutes; + private BigDecimal parseQuantity(String value) { + String cleaned = value.trim(); + + if (cleaned.matches("^\\d+(\\.\\d+)?$")) { + return new BigDecimal(cleaned); + } + + if (cleaned.matches("^\\d+/\\d+$")) { + String[] parts = cleaned.split("/"); + BigDecimal numerator = new BigDecimal(parts[0]); + BigDecimal denominator = new BigDecimal(parts[1]); + + if (denominator.compareTo(BigDecimal.ZERO) == 0) { + throw new IllegalArgumentException("Ingredient quantity cannot have a zero denominator."); + } + + return numerator.divide(denominator, 8, RoundingMode.HALF_UP).stripTrailingZeros(); + } + + if (cleaned.matches("^\\d+\\s+\\d+/\\d+$")) { + String[] mixed = cleaned.split("\\s+"); + BigDecimal whole = new BigDecimal(mixed[0]); + + String[] fraction = mixed[1].split("/"); + BigDecimal numerator = new BigDecimal(fraction[0]); + BigDecimal denominator = new BigDecimal(fraction[1]); + + if (denominator.compareTo(BigDecimal.ZERO) == 0) { + throw new IllegalArgumentException("Ingredient quantity cannot have a zero denominator."); + } + + BigDecimal fractionalPart = numerator.divide(denominator, 8, RoundingMode.HALF_UP); + return whole.add(fractionalPart).stripTrailingZeros(); + } + + throw new IllegalArgumentException( + "Invalid ingredient quantity: " + value + ". Use values like 1, 1.5, 1/4, or 2 1/2."); } - public void setCookTimeMinutes(String cookTimeMinutes) { - this.cookTimeMinutes = cookTimeMinutes; - } + private String saveUploadedFile(MultipartFile file) throws IOException { + String originalFilename = StringUtils.cleanPath(file.getOriginalFilename()); + String extension = ""; - public String getServings() { - return servings; - } + int dotIndex = originalFilename.lastIndexOf('.'); + if (dotIndex >= 0) { + extension = originalFilename.substring(dotIndex); + } - public void setServings(String servings) { - this.servings = servings; - } + String storedFilename = UUID.randomUUID() + extension; - public String getCost() { - return cost; - } + Path uploadDir = Paths.get("uploads"); + if (!Files.exists(uploadDir)) { + Files.createDirectories(uploadDir); + } - public void setCost(String cost) { - this.cost = cost; - } + Path destination = uploadDir.resolve(storedFilename); + Files.copy(file.getInputStream(), destination, StandardCopyOption.REPLACE_EXISTING); - public List getIngredientName() { - return ingredientName; - } - - public void setIngredientName(List ingredientName) { - this.ingredientName = ingredientName; - } - - public List getIngredientQuantity() { - return ingredientQuantity; - } - - public void setIngredientQuantity(List ingredientQuantity) { - this.ingredientQuantity = ingredientQuantity; - } - - public List getIngredientUnit() { - return ingredientUnit; - } - - public void setIngredientUnit(List ingredientUnit) { - this.ingredientUnit = ingredientUnit; - } - - public List getIngredientNotes() { - return ingredientNotes; - } - - public void setIngredientNotes(List ingredientNotes) { - this.ingredientNotes = ingredientNotes; - } - - public List getStepInstruction() { - return stepInstruction; - } - - public void setStepInstruction(List stepInstruction) { - this.stepInstruction = stepInstruction; - } - - public List getTags() { - return tags; - } - - public void setTags(List tags) { - this.tags = tags; - } - - public MultipartFile getImage() { - return image; - } - - public void setImage(MultipartFile image) { - this.image = image; - } - - public Boolean getRemoveImage() { - return removeImage; - } - - public void setRemoveImage(Boolean removeImage) { - this.removeImage = removeImage; + return "/uploads/" + storedFilename; } } \ No newline at end of file diff --git a/src/main/java/com/example/demo/controller/SiteController.java b/src/main/java/com/example/demo/controller/SiteController.java index 18c225e..e399ba4 100644 --- a/src/main/java/com/example/demo/controller/SiteController.java +++ b/src/main/java/com/example/demo/controller/SiteController.java @@ -1,9 +1,11 @@ package com.example.demo.controller; +import java.security.Principal; import java.util.List; -import com.example.demo.service.RecipeService; import com.example.demo.dto.RecipeDto; +import com.example.demo.service.RecipeService; +import com.example.demo.service.UserService; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; @@ -19,22 +21,29 @@ import org.springframework.web.multipart.MultipartFile; public class SiteController { private final RecipeService recipeService; + private final UserService userService; - public SiteController(RecipeService recipeService) { + public SiteController(RecipeService recipeService, UserService userService) { this.recipeService = recipeService; + this.userService = userService; } @GetMapping("/") - public String viewHomePage(Model model) { - List recipes = recipeService.getAllRecipes(); - List newest = recipeService.getNewestRecipes(5); - model.addAttribute("recipes", recipes); - model.addAttribute("newestRecipes", newest); + public String viewHomePage(Model model, Principal principal) { + model.addAttribute("guest", principal == null); + + if (principal == null) { + model.addAttribute("recipes", List.of()); + } else { + model.addAttribute("recipes", userService.getFavoriteRecipesByUsername(principal.getName())); + } + return "home"; } @GetMapping("/login") - public String viewLoginPage(Model model) { + public String viewLoginPage(@RequestParam(required = false) String redirect, Model model) { + model.addAttribute("redirect", redirect); return "login"; } @@ -76,12 +85,14 @@ public class SiteController { @RequestParam(required = false) List prices, @RequestParam(required = false) List cookTime, @RequestParam(required = false) List prepTime, - Model model + Model model, + Principal principal ) { List recipes = recipeService.getRecipes(q, tags, prices, cookTime, prepTime); model.addAttribute("recipes", recipes); model.addAttribute("q", q); model.addAttribute("tags", tags); + model.addAttribute("guest", principal == null); return "explore"; } @@ -91,4 +102,5 @@ public class SiteController { @RequestParam("image") MultipartFile image) { recipeService.updateRecipeImage(id, image); return ResponseEntity.ok().build(); - }} + } +} \ No newline at end of file diff --git a/src/main/java/com/example/demo/service/Impl/RecipeServiceImpl.java b/src/main/java/com/example/demo/service/Impl/RecipeServiceImpl.java index d20c6d9..2906f84 100644 --- a/src/main/java/com/example/demo/service/Impl/RecipeServiceImpl.java +++ b/src/main/java/com/example/demo/service/Impl/RecipeServiceImpl.java @@ -142,24 +142,56 @@ public class RecipeServiceImpl implements RecipeService { ensureUserNotBanned(currentUser); enforceUploadLimit(currentUser); - Recipe recipe = new Recipe(dto.getTitle(), dto.getDescription(), dto.getPrepTimeMinutes(), - dto.getCookTimeMinutes(), dto.getServings(), currentUser, dto.getStatus(), dto.getCost()); + Recipe recipe = new Recipe( + dto.getTitle(), + dto.getDescription(), + dto.getPrepTimeMinutes(), + dto.getCookTimeMinutes(), + dto.getServings(), + currentUser, + dto.getStatus(), + dto.getCost() + ); if (dto.getIngredients() != null) { - for (RecipeIngredientDto riDto : dto.getIngredients()) { + java.util.Set seenIngredientNames = new java.util.HashSet<>(); + java.util.Set seenIngredientIds = new java.util.HashSet<>(); - Ingredient ingredient = ingredientRepository.findByNameIgnoreCase(riDto.getIngredientName()) - .orElseGet(() -> new Ingredient(riDto.getIngredientName())); + for (int i = 0; i < dto.getIngredients().size(); i++) { + RecipeIngredientDto riDto = dto.getIngredients().get(i); + + String rawName = riDto.getIngredientName(); + String normalizedName = rawName == null ? "" : rawName.trim().toLowerCase(); + + if (normalizedName.isBlank()) { + continue; + } + + if (!seenIngredientNames.add(normalizedName)) { + throw new IllegalArgumentException("Duplicate ingredient entry: " + rawName.trim()); + } + + Ingredient ingredient = ingredientRepository.findByNameIgnoreCase(rawName.trim()) + .orElseGet(() -> new Ingredient(rawName.trim())); if (ingredient.getId() == null) { ingredientRepository.save(ingredient); } - RecipeIngredient ri = new RecipeIngredient(recipe, ingredient, riDto.getQuantity(), riDto.getUnit(), - riDto.getNotes()); + if (ingredient.getId() != null && !seenIngredientIds.add(ingredient.getId())) { + throw new IllegalArgumentException("Duplicate ingredient entry: " + rawName.trim()); + } + RecipeIngredient ri = new RecipeIngredient( + recipe, + ingredient, + riDto.getQuantity(), + riDto.getUnit(), + riDto.getNotes() + ); + + ri.setOrderIndex(i); recipe.getRecipeIngredients().add(ri); - } } @@ -179,7 +211,6 @@ public class RecipeServiceImpl implements RecipeService { if (dto.getTags() != null) { for (TagDto tDto : dto.getTags()) { - Tag tag = tagRepository.findByName(tDto.getName()).orElseGet(() -> new Tag(tDto.getName())); if (tag.getId() == null) { @@ -193,7 +224,7 @@ public class RecipeServiceImpl implements RecipeService { return getRecipeById(saved.getId()); } - + @Override @Transactional public List getAllRecipes() { @@ -423,7 +454,22 @@ public class RecipeServiceImpl implements RecipeService { if (prices != null && !prices.isEmpty() && !recipes.isEmpty()) { recipes = recipes.stream() - .filter(recipe -> prices.contains(recipe.getCost())) + .filter(recipe -> { + Integer cost = recipe.getCost(); + if (cost == null) { + return false; + } + + for (Integer price : prices) { + if (price == 4 && cost >= 4) { + return true; + } + if (price != 4 && cost.equals(price)) { + return true; + } + } + return false; + }) .collect(Collectors.toList()); } diff --git a/src/main/java/com/example/demo/service/Impl/UserServiceImpl.java b/src/main/java/com/example/demo/service/Impl/UserServiceImpl.java index 3283988..eb10191 100644 --- a/src/main/java/com/example/demo/service/Impl/UserServiceImpl.java +++ b/src/main/java/com/example/demo/service/Impl/UserServiceImpl.java @@ -7,9 +7,9 @@ import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import com.example.demo.dto.ProfileDto; +import com.example.demo.dto.RecipeDto; import com.example.demo.dto.UpdateProfileDto; import com.example.demo.dto.UserDto; -import com.example.demo.dto.RecipeDto; import com.example.demo.entity.Recipe; import com.example.demo.entity.User; import com.example.demo.exception.NotFoundException; @@ -220,4 +220,19 @@ public class UserServiceImpl implements UserService { return getProfileByUserId(user.getId()); } + + @Override + @Transactional + public List getFavoriteRecipesByUsername(String username) { + User user = userRepository.findByUsername(username) + .orElseThrow(() -> new NotFoundException("User", "username", username)); + + List favoriteDtos = new ArrayList<>(); + + for (Recipe recipe : user.getFavRecipes()) { + favoriteDtos.add(recipeService.convertToDto(recipe)); + } + + return favoriteDtos; + } } \ No newline at end of file diff --git a/src/main/java/com/example/demo/service/UserService.java b/src/main/java/com/example/demo/service/UserService.java index ec96be1..68d1de3 100644 --- a/src/main/java/com/example/demo/service/UserService.java +++ b/src/main/java/com/example/demo/service/UserService.java @@ -3,6 +3,7 @@ package com.example.demo.service; import java.util.List; import com.example.demo.dto.ProfileDto; +import com.example.demo.dto.RecipeDto; import com.example.demo.dto.UpdateProfileDto; import com.example.demo.dto.UserDto; import com.example.demo.entity.User; @@ -39,4 +40,6 @@ public interface UserService { ProfileDto getCurrentUserProfile(String username); ProfileDto updateProfile(String username, UpdateProfileDto dto); -} + + List getFavoriteRecipesByUsername(String username); +} \ No newline at end of file diff --git a/src/main/resources/templates/explore.html b/src/main/resources/templates/explore.html index 97f1db0..accf9b2 100644 --- a/src/main/resources/templates/explore.html +++ b/src/main/resources/templates/explore.html @@ -19,40 +19,42 @@
-
- - - Create New Recipe Icon (Red mixing bowl with a spoon and yellow addition symbol. - -
+ + + Create New Recipe Icon (Red mixing bowl with a spoon and yellow addition symbol.) + +
-
- + +

There are no recipes that fit this description.

@@ -72,109 +74,107 @@
-
+ - + \ No newline at end of file diff --git a/src/main/resources/templates/home.html b/src/main/resources/templates/home.html index 4385102..0a2147c 100644 --- a/src/main/resources/templates/home.html +++ b/src/main/resources/templates/home.html @@ -2,7 +2,7 @@ - Thyme Crunch Home + Thyme Crunch Favorites @@ -15,73 +15,82 @@
- - -
-

You have not saved any recipes.

+
+

Log in to view and save your favorite recipes.

+ + Login + +
-
-
- + \ No newline at end of file diff --git a/src/main/resources/templates/my-profile.html b/src/main/resources/templates/my-profile.html index c4ead38..ec78a60 100644 --- a/src/main/resources/templates/my-profile.html +++ b/src/main/resources/templates/my-profile.html @@ -22,8 +22,8 @@
- + Create New Recipe Icon (Red mixing bowl with a spoon and yellow addition symbol.)
@@ -92,10 +92,16 @@ diff --git a/src/main/resources/templates/public-profile.html b/src/main/resources/templates/public-profile.html index 30bf0e1..54475af 100644 --- a/src/main/resources/templates/public-profile.html +++ b/src/main/resources/templates/public-profile.html @@ -18,28 +18,31 @@
- -
-

User's Recipes

This user has not created any recipes yet.

@@ -62,16 +65,14 @@
- -
@@ -79,10 +80,15 @@ diff --git a/src/main/resources/templates/view-recipe.html b/src/main/resources/templates/view-recipe.html index c8cf433..377fa7c 100644 --- a/src/main/resources/templates/view-recipe.html +++ b/src/main/resources/templates/view-recipe.html @@ -18,12 +18,11 @@
-
- - Create New Recipe Icon (Red mixing bowl with a spoon and yellow addition symbol. + + Create New Recipe Icon (Red mixing bowl with a spoon and yellow addition symbol.)
-
@@ -93,10 +91,9 @@

Prep Time: 0 minutes

Cook Time: 0 minutes

Servings: 0

-

Cost: $

+

Cost: $

-

Tags:

@@ -111,7 +108,7 @@
- +
@@ -119,9 +116,59 @@

Recipe not found

-
+ +
- + \ No newline at end of file