mirror of
https://gitlab.com/etc404/software-engineering-project.git
synced 2026-05-10 20:52:58 +00:00
Remove saved recipe functionality
This commit is contained in:
@@ -71,8 +71,16 @@ public class SiteController {
|
|||||||
@GetMapping("/recipes/{id}")
|
@GetMapping("/recipes/{id}")
|
||||||
public String viewRecipe(@PathVariable Integer id, Model model, Principal principal) {
|
public String viewRecipe(@PathVariable Integer id, Model model, Principal principal) {
|
||||||
RecipeDto recipe = recipeService.getRecipeById(id);
|
RecipeDto recipe = recipeService.getRecipeById(id);
|
||||||
|
|
||||||
|
boolean saved = false;
|
||||||
|
if (principal != null) {
|
||||||
|
saved = userService.isFavorite(principal.getName(), id);
|
||||||
|
}
|
||||||
|
|
||||||
model.addAttribute("recipe", recipe);
|
model.addAttribute("recipe", recipe);
|
||||||
model.addAttribute("guest", principal == null);
|
model.addAttribute("guest", principal == null);
|
||||||
|
model.addAttribute("saved", saved);
|
||||||
|
|
||||||
return "view-recipe";
|
return "view-recipe";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,9 @@ import com.example.demo.repository.RecipeRepo;
|
|||||||
import com.example.demo.repository.UserRepo;
|
import com.example.demo.repository.UserRepo;
|
||||||
import com.example.demo.service.RecipeService;
|
import com.example.demo.service.RecipeService;
|
||||||
import com.example.demo.service.UserService;
|
import com.example.demo.service.UserService;
|
||||||
|
import com.example.demo.entity.Favorite;
|
||||||
|
import com.example.demo.entity.FavoriteId;
|
||||||
|
import com.example.demo.repository.FavoriteRepo;
|
||||||
|
|
||||||
import jakarta.transaction.Transactional;
|
import jakarta.transaction.Transactional;
|
||||||
|
|
||||||
@@ -27,14 +30,16 @@ public class UserServiceImpl implements UserService {
|
|||||||
private RecipeRepo recipeRepository;
|
private RecipeRepo recipeRepository;
|
||||||
private PasswordEncoder passwordEncoder;
|
private PasswordEncoder passwordEncoder;
|
||||||
private RecipeService recipeService;
|
private RecipeService recipeService;
|
||||||
|
private FavoriteRepo favoriteRepository;
|
||||||
|
|
||||||
public UserServiceImpl(UserRepo userRepository, RecipeRepo recipeRepository,
|
public UserServiceImpl(UserRepo userRepository, RecipeRepo recipeRepository,
|
||||||
PasswordEncoder passwordEncoder, RecipeService recipeService) {
|
PasswordEncoder passwordEncoder, RecipeService recipeService, FavoriteRepo favoriteRepository) {
|
||||||
super();
|
super();
|
||||||
this.userRepository = userRepository;
|
this.userRepository = userRepository;
|
||||||
this.recipeRepository = recipeRepository;
|
this.recipeRepository = recipeRepository;
|
||||||
this.passwordEncoder = passwordEncoder;
|
this.passwordEncoder = passwordEncoder;
|
||||||
this.recipeService = recipeService;
|
this.recipeService = recipeService;
|
||||||
|
this.favoriteRepository = favoriteRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -80,11 +85,14 @@ public class UserServiceImpl implements UserService {
|
|||||||
User existingUser = userRepository.findById(userId)
|
User existingUser = userRepository.findById(userId)
|
||||||
.orElseThrow(() -> new NotFoundException("User", "id", userId));
|
.orElseThrow(() -> new NotFoundException("User", "id", userId));
|
||||||
|
|
||||||
Recipe existingRecipe = recipeRepository.findById(recipeId)
|
recipeRepository.findById(recipeId)
|
||||||
.orElseThrow(() -> new NotFoundException("Recipe", "id", recipeId));
|
.orElseThrow(() -> new NotFoundException("Recipe", "id", recipeId));
|
||||||
|
|
||||||
existingUser.getFavRecipes().add(existingRecipe);
|
FavoriteId favoriteId = new FavoriteId(userId, recipeId);
|
||||||
userRepository.save(existingUser);
|
|
||||||
|
if (!favoriteRepository.existsById(favoriteId)) {
|
||||||
|
favoriteRepository.save(new Favorite(userId, recipeId));
|
||||||
|
}
|
||||||
|
|
||||||
return convertToDto(existingUser);
|
return convertToDto(existingUser);
|
||||||
}
|
}
|
||||||
@@ -112,14 +120,17 @@ public class UserServiceImpl implements UserService {
|
|||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
public void deleteFavorite(Integer userId, Integer recipeId) {
|
public void deleteFavorite(Integer userId, Integer recipeId) {
|
||||||
User existingUser = userRepository.findById(userId)
|
userRepository.findById(userId)
|
||||||
.orElseThrow(() -> new NotFoundException("User", "id", userId));
|
.orElseThrow(() -> new NotFoundException("User", "id", userId));
|
||||||
|
|
||||||
Recipe existingRecipe = recipeRepository.findById(recipeId)
|
recipeRepository.findById(recipeId)
|
||||||
.orElseThrow(() -> new NotFoundException("Recipe", "id", recipeId));
|
.orElseThrow(() -> new NotFoundException("Recipe", "id", recipeId));
|
||||||
|
|
||||||
existingUser.getFavRecipes().remove(existingRecipe);
|
FavoriteId favoriteId = new FavoriteId(userId, recipeId);
|
||||||
userRepository.save(existingUser);
|
|
||||||
|
if (favoriteRepository.existsById(favoriteId)) {
|
||||||
|
favoriteRepository.deleteById(favoriteId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -235,4 +246,13 @@ public class UserServiceImpl implements UserService {
|
|||||||
|
|
||||||
return favoriteDtos;
|
return favoriteDtos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public boolean isFavorite(String username, Integer recipeId) {
|
||||||
|
User user = userRepository.findByUsername(username)
|
||||||
|
.orElseThrow(() -> new NotFoundException("User", "username", username));
|
||||||
|
|
||||||
|
return favoriteRepository.existsById(new FavoriteId(user.getId(), recipeId));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -42,4 +42,6 @@ public interface UserService {
|
|||||||
ProfileDto updateProfile(String username, UpdateProfileDto dto);
|
ProfileDto updateProfile(String username, UpdateProfileDto dto);
|
||||||
|
|
||||||
List<RecipeDto> getFavoriteRecipesByUsername(String username);
|
List<RecipeDto> getFavoriteRecipesByUsername(String username);
|
||||||
|
|
||||||
|
boolean isFavorite(String username, Integer recipeId);
|
||||||
}
|
}
|
||||||
@@ -119,7 +119,13 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<button type="button" class="save-btn" th:data-recipe-id="${recipe.id}">Save Recipe</button>
|
<button type="button"
|
||||||
|
class="save-btn"
|
||||||
|
th:data-recipe-id="${recipe.id}"
|
||||||
|
th:data-saved="${saved}"
|
||||||
|
th:text="${saved} ? 'Remove Saved Recipe' : 'Save Recipe'">
|
||||||
|
Save Recipe
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div th:unless="${recipe != null}" class="recipe-not-found">
|
<div th:unless="${recipe != null}" class="recipe-not-found">
|
||||||
@@ -131,7 +137,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
async function getCurrentUser() {
|
async function getCurrentUser() {
|
||||||
const response = await fetch('/api/users/me', {
|
const response = await fetch('/api/users/me', {
|
||||||
credentials: 'same-origin'
|
credentials: 'same-origin'
|
||||||
});
|
});
|
||||||
@@ -142,9 +148,9 @@ async function getCurrentUser() {
|
|||||||
|
|
||||||
const user = await response.json();
|
const user = await response.json();
|
||||||
return user || null;
|
return user || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function saveRecipe(recipeId, button) {
|
async function toggleSaveRecipe(recipeId, button) {
|
||||||
const csrfHeader = document.querySelector('meta[name="_csrf_header"]')?.content;
|
const csrfHeader = document.querySelector('meta[name="_csrf_header"]')?.content;
|
||||||
const csrfToken = document.querySelector('meta[name="_csrf"]')?.content;
|
const csrfToken = document.querySelector('meta[name="_csrf"]')?.content;
|
||||||
|
|
||||||
@@ -155,8 +161,10 @@ async function saveRecipe(recipeId, button) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isSaved = button.dataset.saved === 'true';
|
||||||
|
|
||||||
const response = await fetch(`/api/users/${user.id}/favorites/${recipeId}`, {
|
const response = await fetch(`/api/users/${user.id}/favorites/${recipeId}`, {
|
||||||
method: 'POST',
|
method: isSaved ? 'DELETE' : 'POST',
|
||||||
credentials: 'same-origin',
|
credentials: 'same-origin',
|
||||||
headers: {
|
headers: {
|
||||||
...(csrfHeader && csrfToken ? { [csrfHeader]: csrfToken } : {})
|
...(csrfHeader && csrfToken ? { [csrfHeader]: csrfToken } : {})
|
||||||
@@ -165,21 +173,21 @@ async function saveRecipe(recipeId, button) {
|
|||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const text = await response.text();
|
const text = await response.text();
|
||||||
alert(text || 'Could not save recipe.');
|
alert(text || 'Could not update saved recipe.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
button.textContent = 'Saved';
|
button.dataset.saved = isSaved ? 'false' : 'true';
|
||||||
button.disabled = true;
|
button.textContent = isSaved ? 'Save Recipe' : 'Un-save Recipe';
|
||||||
}
|
}
|
||||||
|
|
||||||
document.querySelectorAll('.save-btn').forEach(button => {
|
document.querySelectorAll('.save-btn').forEach(button => {
|
||||||
button.addEventListener('click', async () => {
|
button.addEventListener('click', async () => {
|
||||||
const recipeId = button.dataset.recipeId;
|
const recipeId = button.dataset.recipeId;
|
||||||
await saveRecipe(recipeId, button);
|
await toggleSaveRecipe(recipeId, button);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
Reference in New Issue
Block a user