From 267d5c7bdfdc4fd659d2335ab0d2066cb819e2da Mon Sep 17 00:00:00 2001 From: Madeleine Stamp Date: Tue, 21 Apr 2026 13:37:02 -0600 Subject: [PATCH] Update 17 files - /src/main/resources/application.properties - /src/main/resources/templates/view-recipe.html - /src/main/resources/templates/create-recipe.html - /src/main/resources/templates/home.html - /src/main/resources/templates/explore.html - /src/main/resources/templates/public-profile.html - /src/main/resources/templates/my-profile.html - /src/main/resources/templates/update-recipe.html - /src/main/resources/static/css/view-recipe.css - /src/main/java/com/example/demo/controller/SiteController.java - /src/main/java/com/example/demo/controller/RecipeUploadController.java - /src/main/java/com/example/demo/controller/RecipeUploadForm.java - /src/main/java/com/example/demo/config/WebConfig - /src/main/java/com/example/demo/config/SecurityConfig.java - /src/main/java/com/example/demo/config/WebConfig.java - /src/main/java/com/example/demo/service/Impl/RecipeServiceImpl.java - /src/main/java/com/example/demo/service/RecipeService.java --- .../example/demo/config/SecurityConfig.java | 16 +- .../java/com/example/demo/config/WebConfig | 0 .../com/example/demo/config/WebConfig.java | 20 + .../controller/RecipeUploadController.java | 212 ++++++++ .../demo/controller/RecipeUploadForm.java | 141 +++++ .../demo/controller/SiteController.java | 21 +- .../demo/service/Impl/RecipeServiceImpl.java | 94 ++-- .../example/demo/service/RecipeService.java | 4 +- src/main/resources/application.properties | 5 + src/main/resources/static/css/view-recipe.css | 3 +- .../resources/templates/create-recipe.html | 7 +- src/main/resources/templates/explore.html | 13 +- src/main/resources/templates/home.html | 15 +- src/main/resources/templates/my-profile.html | 15 +- .../resources/templates/public-profile.html | 69 +-- .../resources/templates/update-recipe.html | 484 +++++++++++------- src/main/resources/templates/view-recipe.html | 132 +++-- 17 files changed, 924 insertions(+), 327 deletions(-) create mode 100644 src/main/java/com/example/demo/config/WebConfig create mode 100644 src/main/java/com/example/demo/config/WebConfig.java create mode 100644 src/main/java/com/example/demo/controller/RecipeUploadController.java create mode 100644 src/main/java/com/example/demo/controller/RecipeUploadForm.java diff --git a/src/main/java/com/example/demo/config/SecurityConfig.java b/src/main/java/com/example/demo/config/SecurityConfig.java index 0831b95..1a18e3a 100644 --- a/src/main/java/com/example/demo/config/SecurityConfig.java +++ b/src/main/java/com/example/demo/config/SecurityConfig.java @@ -19,15 +19,23 @@ public class SecurityConfig { public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth - .requestMatchers("/login", "/register", "/css/**", "/images/**").permitAll() + // public pages and static files + .requestMatchers("/", "/login", "/register", "/css/**", "/images/**").permitAll() .requestMatchers("/api/users").permitAll() - .requestMatchers("/users/**").permitAll() .requestMatchers("/api/admin/**").hasRole("ADMIN") - .anyRequest().authenticated() + + // 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 .loginPage("/login") - .defaultSuccessUrl("/", true) + .defaultSuccessUrl("/") .permitAll() ) .logout(logout -> logout.permitAll()); diff --git a/src/main/java/com/example/demo/config/WebConfig b/src/main/java/com/example/demo/config/WebConfig new file mode 100644 index 0000000..e69de29 diff --git a/src/main/java/com/example/demo/config/WebConfig.java b/src/main/java/com/example/demo/config/WebConfig.java new file mode 100644 index 0000000..5bb60eb --- /dev/null +++ b/src/main/java/com/example/demo/config/WebConfig.java @@ -0,0 +1,20 @@ +package com.example.demo.config; + +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class WebConfig implements WebMvcConfigurer { + + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) { + Path uploadPath = Paths.get("uploads").toAbsolutePath().normalize(); + + registry.addResourceHandler("/uploads/**") + .addResourceLocations("file:///" + uploadPath.toString().replace("\\", "/") + "/"); + } +} \ No newline at end of file diff --git a/src/main/java/com/example/demo/controller/RecipeUploadController.java b/src/main/java/com/example/demo/controller/RecipeUploadController.java new file mode 100644 index 0000000..ba426ef --- /dev/null +++ b/src/main/java/com/example/demo/controller/RecipeUploadController.java @@ -0,0 +1,212 @@ +package com.example.demo.controller; + +import java.io.IOException; +import java.math.BigDecimal; +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; + +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; + +@RestController +public class RecipeUploadController { + + private final RecipeService recipeService; + + public RecipeUploadController(RecipeService recipeService) { + this.recipeService = recipeService; + } + + @PostMapping(value = "/api/recipes/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + public ResponseEntity createRecipeWithUpload(@ModelAttribute RecipeUploadForm form, Principal principal) { + try { + System.out.println("UPLOAD ENDPOINT HIT"); + System.out.println("Title: " + form.getTitle()); + System.out.println("Image present: " + (form.getImage() != null && !form.getImage().isEmpty())); + + RecipeDto dto = buildRecipeDto(form, true); + + System.out.println("Image DTO count: " + (dto.getImages() == null ? 0 : dto.getImages().size())); + + String currentUsername = principal.getName(); + RecipeDto saved = recipeService.saveRecipe(dto, currentUsername); + + System.out.println("Saved recipe id: " + saved.getId()); + + return new ResponseEntity<>(saved, HttpStatus.CREATED); + + } catch (Exception e) { + e.printStackTrace(); + return ResponseEntity.status(HttpStatus.BAD_REQUEST) + .body("Recipe creation failed: " + e.getMessage()); + } + } + + @PostMapping(value = "/api/recipes/{id}/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + public ResponseEntity updateRecipeWithUpload( + @PathVariable Integer id, + @ModelAttribute RecipeUploadForm form, + Principal principal) { + try { + System.out.println("UPDATE UPLOAD ENDPOINT HIT"); + System.out.println("Recipe id: " + id); + System.out.println("Title: " + form.getTitle()); + System.out.println("Image present: " + (form.getImage() != null && !form.getImage().isEmpty())); + System.out.println("Remove image: " + form.getRemoveImage()); + + RecipeDto dto = buildRecipeDto(form, false); + + String currentUsername = principal.getName(); + RecipeDto updated = recipeService.updateRecipe(dto, id, currentUsername); + + System.out.println("Updated recipe id: " + updated.getId()); + + return new ResponseEntity<>(updated, HttpStatus.OK); + + } catch (Exception e) { + e.printStackTrace(); + return ResponseEntity.status(HttpStatus.BAD_REQUEST) + .body("Recipe update failed: " + e.getMessage()); + } + } + + 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 = new BigDecimal(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); + System.out.println("Saved file path: " + imageUrl); + } else if (removeImage) { + // Empty list means remove all images on update + dto.setImages(new ArrayList<>()); + } else if (isCreate) { + dto.setImages(new ArrayList<>()); + } else { + // Null on update means keep the existing image as-is + dto.setImages(null); + } + + return dto; + } + + private Integer parseInteger(String value) { + if (value == null || value.isBlank()) { + return null; + } + return Integer.valueOf(value.trim()); + } + + private String getListValue(List list, int index) { + if (list == null || index >= list.size()) { + return null; + } + return list.get(index); + } + + private String safeTrim(String value) { + return value == null ? null : value.trim(); + } + + private String saveUploadedFile(MultipartFile file) throws IOException { + String originalFilename = StringUtils.cleanPath(file.getOriginalFilename()); + String extension = ""; + + int dotIndex = originalFilename.lastIndexOf('.'); + if (dotIndex >= 0) { + extension = originalFilename.substring(dotIndex); + } + + String storedFilename = UUID.randomUUID() + extension; + + Path uploadDir = Paths.get("uploads"); + if (!Files.exists(uploadDir)) { + Files.createDirectories(uploadDir); + } + + Path destination = uploadDir.resolve(storedFilename); + Files.copy(file.getInputStream(), destination, StandardCopyOption.REPLACE_EXISTING); + + return "/uploads/" + storedFilename; + } +} \ No newline at end of file diff --git a/src/main/java/com/example/demo/controller/RecipeUploadForm.java b/src/main/java/com/example/demo/controller/RecipeUploadForm.java new file mode 100644 index 0000000..c0c2a2e --- /dev/null +++ b/src/main/java/com/example/demo/controller/RecipeUploadForm.java @@ -0,0 +1,141 @@ +package com.example.demo.controller; + +import java.util.List; + +import org.springframework.web.multipart.MultipartFile; + +public class RecipeUploadForm { + + private String title; + private String description; + private String prepTimeMinutes; + private String cookTimeMinutes; + private String servings; + private String cost; + + private List ingredientName; + private List ingredientQuantity; + private List ingredientUnit; + private List ingredientNotes; + + private List stepInstruction; + private List tags; + + private MultipartFile image; + private Boolean removeImage; + + public RecipeUploadForm() { + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getPrepTimeMinutes() { + return prepTimeMinutes; + } + + public void setPrepTimeMinutes(String prepTimeMinutes) { + this.prepTimeMinutes = prepTimeMinutes; + } + + public String getCookTimeMinutes() { + return cookTimeMinutes; + } + + public void setCookTimeMinutes(String cookTimeMinutes) { + this.cookTimeMinutes = cookTimeMinutes; + } + + public String getServings() { + return servings; + } + + public void setServings(String servings) { + this.servings = servings; + } + + public String getCost() { + return cost; + } + + public void setCost(String cost) { + this.cost = cost; + } + + 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; + } +} \ 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 db3b44c..a23ae7f 100644 --- a/src/main/java/com/example/demo/controller/SiteController.java +++ b/src/main/java/com/example/demo/controller/SiteController.java @@ -4,11 +4,16 @@ import java.util.List; import com.example.demo.service.RecipeService; import com.example.demo.dto.RecipeDto; + +import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.multipart.MultipartFile; @Controller public class SiteController { @@ -55,6 +60,13 @@ public class SiteController { return "update-recipe"; } + @GetMapping("/update-recipe/{id}") + public String showUpdateRecipePage(@PathVariable Integer id, Model model) { + RecipeDto recipe = recipeService.getRecipeById(id); + model.addAttribute("recipe", recipe); + return "update-recipe"; + } + @GetMapping("/explore") public String explore( @RequestParam(required = false) String q, @@ -70,4 +82,11 @@ public class SiteController { model.addAttribute("tags", tags); return "explore"; } -} \ No newline at end of file + + @PostMapping("/api/recipes/{id}/image") + @ResponseBody + public ResponseEntity updateRecipeImage(@PathVariable Integer id, + @RequestParam("image") MultipartFile image) { + recipeService.updateRecipeImage(id, image); + return ResponseEntity.ok().build(); + }} 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 d729629..44f8585 100644 --- a/src/main/java/com/example/demo/service/Impl/RecipeServiceImpl.java +++ b/src/main/java/com/example/demo/service/Impl/RecipeServiceImpl.java @@ -7,6 +7,7 @@ import java.util.stream.Collectors; import org.springframework.security.access.AccessDeniedException; import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; import com.example.demo.dto.RecipeDto; import com.example.demo.dto.UserDto; @@ -57,6 +58,13 @@ public class RecipeServiceImpl implements RecipeService { this.tagRepository = tagRepository; } + @Override + public void updateRecipeImage(Integer id, MultipartFile image) { + if (image == null || image.isEmpty()) { + return; + } + } + @Override public RecipeDto convertToDto(Recipe recipe) { List ingredientDtos = recipe.getRecipeIngredients().stream() @@ -236,52 +244,55 @@ public class RecipeServiceImpl implements RecipeService { List updatedTags = recipeDto.getTags(); List tagsToRemove = new ArrayList<>(); - for (RecipeIngredient ri : existingRecipe.getRecipeIngredients()) { + if (updatedIngredients != null) { + for (RecipeIngredient ri : existingRecipe.getRecipeIngredients()) { - boolean existsInUpdatedList = false; - for (RecipeIngredientDto dto : updatedIngredients) { - String updatedName = dto.getIngredientName(); - String existingName = ri.getIngredient().getName(); + boolean existsInUpdatedList = false; + for (RecipeIngredientDto dto : updatedIngredients) { + String updatedName = dto.getIngredientName(); + String existingName = ri.getIngredient().getName(); - if (updatedName.equals(existingName)) { - existsInUpdatedList = true; - break; + if (java.util.Objects.equals(updatedName, existingName)) { + existsInUpdatedList = true; + break; + } + } + + if (!existsInUpdatedList) { + ingredientsToRemove.add(ri); } } - if (!existsInUpdatedList) { - ingredientsToRemove.add(ri); - } - } + existingRecipe.getRecipeIngredients().removeAll(ingredientsToRemove); - existingRecipe.getRecipeIngredients().removeAll(ingredientsToRemove); + for (RecipeIngredientDto riDto : updatedIngredients) { - for (RecipeIngredientDto riDto : updatedIngredients) { + RecipeIngredient existingRI = existingRecipe.getRecipeIngredients().stream() + .filter(ri -> java.util.Objects.equals(ri.getIngredient().getName(), riDto.getIngredientName())) + .findFirst() + .orElse(null); - RecipeIngredient existingRI = existingRecipe.getRecipeIngredients().stream() - .filter(ri -> ri.getIngredient().getName().equals(riDto.getIngredientName())).findFirst() - .orElse(null); + if (existingRI != null) { - if (existingRI != null) { - - existingRI.setQuantity(riDto.getQuantity()); - existingRI.setUnit(riDto.getUnit()); - existingRI.setNotes(riDto.getNotes()); - } - - else { - - Ingredient ingredient = ingredientRepository.findByNameIgnoreCase(riDto.getIngredientName()) - .orElseGet(() -> new Ingredient(riDto.getIngredientName())); - - if (ingredient.getId() == null) { - ingredientRepository.save(ingredient); + existingRI.setQuantity(riDto.getQuantity()); + existingRI.setUnit(riDto.getUnit()); + existingRI.setNotes(riDto.getNotes()); } - RecipeIngredient newRI = new RecipeIngredient(existingRecipe, ingredient, riDto.getQuantity(), - riDto.getUnit(), riDto.getNotes()); + else { - existingRecipe.getRecipeIngredients().add(newRI); + Ingredient ingredient = ingredientRepository.findByNameIgnoreCase(riDto.getIngredientName()) + .orElseGet(() -> new Ingredient(riDto.getIngredientName())); + + if (ingredient.getId() == null) { + ingredientRepository.save(ingredient); + } + + RecipeIngredient newRI = new RecipeIngredient(existingRecipe, ingredient, riDto.getQuantity(), + riDto.getUnit(), riDto.getNotes()); + + existingRecipe.getRecipeIngredients().add(newRI); + } } } @@ -314,7 +325,7 @@ public class RecipeServiceImpl implements RecipeService { if (updatedImages != null) { for (Image image : existingRecipe.getImages()) { boolean existsInUpdatedList = updatedImages.stream() - .anyMatch(dto -> dto.getImageUrl().equals(image.getImageUrl())); + .anyMatch(dto -> java.util.Objects.equals(dto.getImageUrl(), image.getImageUrl())); if (!existsInUpdatedList) imagesToRemove.add(image); } @@ -323,7 +334,9 @@ public class RecipeServiceImpl implements RecipeService { for (ImageDto imageDto : updatedImages) { Image existingImage = existingRecipe.getImages().stream() - .filter(img -> img.getImageUrl().equals(imageDto.getImageUrl())).findFirst().orElse(null); + .filter(img -> java.util.Objects.equals(img.getImageUrl(), imageDto.getImageUrl())) + .findFirst() + .orElse(null); if (existingImage != null) { existingImage.setImageUrl(imageDto.getImageUrl()); @@ -336,7 +349,8 @@ public class RecipeServiceImpl implements RecipeService { if (updatedTags != null) { for (Tag tag : existingRecipe.getTags()) { - boolean existsInUpdatedList = updatedTags.stream().anyMatch(dto -> dto.getName().equals(tag.getName())); + boolean existsInUpdatedList = updatedTags.stream() + .anyMatch(dto -> java.util.Objects.equals(dto.getName(), tag.getName())); if (!existsInUpdatedList) tagsToRemove.add(tag); } @@ -345,7 +359,9 @@ public class RecipeServiceImpl implements RecipeService { for (TagDto tagDto : updatedTags) { Tag existingTag = existingRecipe.getTags().stream() - .filter(tag -> tag.getName().equals(tagDto.getName())).findFirst().orElse(null); + .filter(tag -> java.util.Objects.equals(tag.getName(), tagDto.getName())) + .findFirst() + .orElse(null); if (existingTag != null) { existingTag.setName(tagDto.getName()); @@ -356,7 +372,7 @@ public class RecipeServiceImpl implements RecipeService { existingRecipe.getTags().add(newTag); } } - } + } recipeRepository.save(existingRecipe); return convertToDto(existingRecipe); } diff --git a/src/main/java/com/example/demo/service/RecipeService.java b/src/main/java/com/example/demo/service/RecipeService.java index 0882b35..055c34d 100644 --- a/src/main/java/com/example/demo/service/RecipeService.java +++ b/src/main/java/com/example/demo/service/RecipeService.java @@ -3,6 +3,7 @@ package com.example.demo.service; import java.util.List; import org.jspecify.annotations.Nullable; +import org.springframework.web.multipart.MultipartFile; import com.example.demo.dto.RecipeDto; import com.example.demo.entity.Recipe; @@ -23,5 +24,6 @@ public interface RecipeService { void deleteRecipe(Integer Id, String currentUsername); -} + void updateRecipeImage(Integer id, MultipartFile image); +} \ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 8b61b06..b02ea19 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -18,3 +18,8 @@ spring.jpa.open-in-view=false #spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect server.port=8080 + +spring.servlet.multipart.max-file-size=20MB +spring.servlet.multipart.max-request-size=20MB +server.tomcat.max-swallow-size=-1 +server.tomcat.max-part-count=100 \ No newline at end of file diff --git a/src/main/resources/static/css/view-recipe.css b/src/main/resources/static/css/view-recipe.css index a425b1c..34d9afe 100644 --- a/src/main/resources/static/css/view-recipe.css +++ b/src/main/resources/static/css/view-recipe.css @@ -30,7 +30,8 @@ body, html { margin: 0; font-family: 'Mali', cursive; background-color: var(--pale-yellow); - overflow: clip; + overflow-x: hidden; + overflow-y: auto; } /* ========================= diff --git a/src/main/resources/templates/create-recipe.html b/src/main/resources/templates/create-recipe.html index cadeadd..46e4d19 100644 --- a/src/main/resources/templates/create-recipe.html +++ b/src/main/resources/templates/create-recipe.html @@ -73,9 +73,12 @@
+
+ +
$ @@ -153,6 +156,7 @@ function addStep() { function renumberSteps() { stepsContainer.querySelectorAll('.step-bubble').forEach((bubble, i) => { bubble.textContent = i + 1; + bubble.textContent = i + 1; }); } // Cost display @@ -184,6 +188,7 @@ dollars.forEach(dollar => { // ---- Collecting on submit ---- +// Call this wherever you build your POST payload: function getIngredients() { return [...ingredientContainer.querySelectorAll('.dynamic-row')].map(row => { qtyValue = row.querySelector('.ing-qty').value.trim(); // quantity should be a number NOT A STRING @@ -375,4 +380,4 @@ document.getElementById('publish-btn').addEventListener('click', async function( - \ No newline at end of file + diff --git a/src/main/resources/templates/explore.html b/src/main/resources/templates/explore.html index 7c14e6f..7cdc9d7 100644 --- a/src/main/resources/templates/explore.html +++ b/src/main/resources/templates/explore.html @@ -4,7 +4,7 @@ - Explore - Thyme Crunch + Thyme Crunch Home @@ -52,7 +52,7 @@
-
+

There are no recipes that fit this description.

@@ -65,10 +65,11 @@
Recipe Image
-

Cost

+

Cost

+ @@ -234,12 +235,6 @@ window.location.href = '/explore?' + out.toString(); } })(); - - // For cost display - document.querySelectorAll('.card-cost').forEach(el => { - const num = parseInt(el.textContent); - el.textContent = num === 0 ? '' : '$'.repeat(num); -}); diff --git a/src/main/resources/templates/home.html b/src/main/resources/templates/home.html index 744e94d..f675b32 100644 --- a/src/main/resources/templates/home.html +++ b/src/main/resources/templates/home.html @@ -2,7 +2,7 @@ - Home - Thyme Crunch + Thyme Crunch Home @@ -38,7 +38,6 @@
-

You have not saved any recipes.

@@ -51,7 +50,7 @@
Recipe Image
-

Cost

+

Cost

@@ -64,13 +63,5 @@ - - - - \ 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 5c2b4e5..7e7c060 100644 --- a/src/main/resources/templates/my-profile.html +++ b/src/main/resources/templates/my-profile.html @@ -53,10 +53,10 @@

Description

-
- Recipe Image -
-

Cost

+
+ Recipe Image +
+

Cost

Edit @@ -91,12 +91,5 @@ - - \ No newline at end of file diff --git a/src/main/resources/templates/public-profile.html b/src/main/resources/templates/public-profile.html index 060dd50..8065c44 100644 --- a/src/main/resources/templates/public-profile.html +++ b/src/main/resources/templates/public-profile.html @@ -4,16 +4,16 @@ - My Profile - Thyme Crunch - + Profile - Thyme Crunch +
- Violin f-hole shape to the left of header. + Violin f-hole shape to the left of header.

Thyme Crunch

- Violin f-hole shape to the right of header. + Violin f-hole shape to the right of header.
@@ -27,65 +27,50 @@
  • Profile
  • - +
  • - 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.)
    -

    My Recipes

    +

    User's Recipes

    -

    You have not created any recipes yet.

    +

    This user has not created any recipes yet.

    + + + +
    - +
    diff --git a/src/main/resources/templates/update-recipe.html b/src/main/resources/templates/update-recipe.html index 598d09c..d76ca05 100644 --- a/src/main/resources/templates/update-recipe.html +++ b/src/main/resources/templates/update-recipe.html @@ -1,19 +1,19 @@ - - +< + - - - Create Thyme Crunch Recipe - + + + Update Thyme Crunch Recipe +
    - Violin f-hole shape to the left of header. + Violin f-hole shape to the left of header.

    Thyme Crunch

    - Violin f-hole shape to the right of header. + Violin f-hole shape to the right of header.
    @@ -21,7 +21,7 @@
    -
    New Recipe
    +
    Update Recipe
    @@ -34,17 +34,16 @@
    - +
    - +
    - +
    -
    @@ -53,90 +52,107 @@

    Click to upload or drag and drop an image.

    -
    +
    - +
    - -
    + +
    - +
    - -
    + +
    -
    +
    -
    - +
    -
    - -
    + +
    + +
    + + \ No newline at end of file diff --git a/src/main/resources/templates/view-recipe.html b/src/main/resources/templates/view-recipe.html index 6c8e81f..5257c91 100644 --- a/src/main/resources/templates/view-recipe.html +++ b/src/main/resources/templates/view-recipe.html @@ -1,64 +1,138 @@ - + - Thyme Crunch View Recipe + + + Recipe
    - Violin f-hole shape to the left of header. + Left header swirl

    Thyme Crunch

    - Violin f-hole shape to the right of header. + Right header swirl
    + -
    -
    -

    -

    +
    -
    -
    Ingredients
    -
      -
    • -
    -
    +
    +

    Recipe Title

    -
    -
    Instructions
    -
      -
    1. -
    -
    +

    + Author: + + Author Name + +

    + +
    + Recipe Image +
    + +

    + No image uploaded for this recipe. +

    + +
    +

    Description

    +

    Recipe description

    +
    + +
    +

    Prep Time: 0 minutes

    +

    Cook Time: 0 minutes

    +

    Servings: 0

    +

    Cost: 0

    +
    + +
    +

    Ingredients

    +
      +
    • + 1 + cup + Ingredient + (notes) +
    • +
    +

    No ingredients listed.

    +
    + +
    +

    Steps

    +
      +
    1. Step instruction
    2. +
    +

    No steps listed.

    +
    + +
    +

    Tags

    +
    + + Tag + +
    +

    No tags listed.

    +
    + + +
    +
    + +
    +
    +

    Recipe not found

    +

    Back to Explore

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