diff --git a/backend/pms/src/main/java/com/mini/pms/configuration/SecurityConfig.java b/backend/pms/src/main/java/com/mini/pms/configuration/SecurityConfig.java index 4914c24..4858ee0 100644 --- a/backend/pms/src/main/java/com/mini/pms/configuration/SecurityConfig.java +++ b/backend/pms/src/main/java/com/mini/pms/configuration/SecurityConfig.java @@ -1,10 +1,10 @@ package com.mini.pms.configuration; import lombok.RequiredArgsConstructor; - import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.jpa.repository.config.EnableJpaAuditing; +import org.springframework.http.HttpMethod; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.ProviderManager; import org.springframework.security.authentication.dao.DaoAuthenticationProvider; @@ -29,12 +29,12 @@ public class SecurityConfig { private final JwtFilter jwtFilter; private static final String[] STATIC_RESOURCES = { - "/images/**", - "/js/**", - "/webjars/**", - "/swagger-resources/", - "/swagger-ui/**", - "/v3/api-docs/**" + "/images/**", + "/js/**", + "/webjars/**", + "/swagger-resources/", + "/swagger-ui/**", + "/v3/api-docs/**" }; @Bean @@ -59,13 +59,17 @@ public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws authorize .requestMatchers( contextPath + "/public", - contextPath + "/auth/**", - contextPath + "/files/**", - contextPath + "/properties/**", - contextPath + "/properties" - ) + contextPath + "/auth/**" + ) + .permitAll() + .requestMatchers(HttpMethod.GET, + contextPath + "/properties/**", + contextPath + "/properties", + contextPath + "/files/**" + ) .permitAll() + .requestMatchers(contextPath + "/admins/**") .hasAuthority("Admin") .requestMatchers(contextPath + "/owners/**") diff --git a/backend/pms/src/main/java/com/mini/pms/customexception/ExceptionControllerAdvice.java b/backend/pms/src/main/java/com/mini/pms/customexception/ExceptionControllerAdvice.java index 53ba0b1..5dc70b9 100644 --- a/backend/pms/src/main/java/com/mini/pms/customexception/ExceptionControllerAdvice.java +++ b/backend/pms/src/main/java/com/mini/pms/customexception/ExceptionControllerAdvice.java @@ -3,7 +3,8 @@ import jakarta.servlet.ServletException; import jakarta.validation.ValidationException; import lombok.NonNull; -import lombok.extern.log4j.Log4j2; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatusCode; @@ -23,20 +24,22 @@ import java.util.Map; @ControllerAdvice -@Log4j2 public class ExceptionControllerAdvice extends ResponseEntityExceptionHandler { + private static final Logger logger = LogManager.getLogger(ExceptionControllerAdvice.class); + @ExceptionHandler({RuntimeException.class, ServletException.class}) public ResponseEntity handleException(Exception e) { - e.printStackTrace(); - log.error(e); - if (e instanceof PlatformException) { - var platform = (PlatformException) e; + e.fillInStackTrace(); + logger.error(e.fillInStackTrace()); + if (e instanceof PlatformException platform) { return createResponseEntity(platform.getMessage(), platform.getHttpStatusCode()); } else if (e instanceof AccessDeniedException) { return createResponseEntity("Access Denied", HttpStatus.FORBIDDEN); } else if (e instanceof NoResourceFoundException) { return createResponseEntity("Resource Not Found", HttpStatus.NOT_FOUND); + } else if(e instanceof ValidationException) { + return createResponseEntity(e.getMessage(), HttpStatus.BAD_REQUEST); } return createResponseEntity("General Error", HttpStatus.INTERNAL_SERVER_ERROR); } @@ -68,9 +71,4 @@ protected ResponseEntity handleMethodArgumentNotValid(MethodArgumentNotV }); return new ResponseEntity<>(errors, HttpStatus.BAD_REQUEST); } - - @ExceptionHandler(ValidationException.class) - public ResponseEntity handleValidationException(ValidationException exception) { - return createResponseEntity(exception.getMessage(), HttpStatus.BAD_REQUEST); - } } diff --git a/backend/pms/src/main/java/com/mini/pms/customexception/MemberNotFoundException.java b/backend/pms/src/main/java/com/mini/pms/customexception/MemberNotFoundException.java deleted file mode 100644 index 1e82eaf..0000000 --- a/backend/pms/src/main/java/com/mini/pms/customexception/MemberNotFoundException.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.mini.pms.customexception; - -public class MemberNotFoundException extends RuntimeException { - public MemberNotFoundException(String message) { - super(message); - } -} - diff --git a/backend/pms/src/main/java/com/mini/pms/customexception/PropertyNotFoundException.java b/backend/pms/src/main/java/com/mini/pms/customexception/PropertyNotFoundException.java deleted file mode 100644 index fc80f5e..0000000 --- a/backend/pms/src/main/java/com/mini/pms/customexception/PropertyNotFoundException.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.mini.pms.customexception; - -public class PropertyNotFoundException extends RuntimeException { - public PropertyNotFoundException(String message) { - super(message); - } -} diff --git a/backend/pms/src/main/java/com/mini/pms/repo/FavoriteRepo.java b/backend/pms/src/main/java/com/mini/pms/repo/FavoriteRepo.java index d9cb4c0..a420b2c 100644 --- a/backend/pms/src/main/java/com/mini/pms/repo/FavoriteRepo.java +++ b/backend/pms/src/main/java/com/mini/pms/repo/FavoriteRepo.java @@ -1,15 +1,14 @@ package com.mini.pms.repo; -import java.util.List; - import com.mini.pms.entity.Favorite; +import com.mini.pms.entity.Member; +import com.mini.pms.entity.Property; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; - -import com.mini.pms.entity.Member; -import com.mini.pms.entity.Property; +import java.util.List; +import java.util.Optional; public interface FavoriteRepo extends JpaRepository { @Query("SELECT p FROM Favorite f JOIN f.member u JOIN f.property p WHERE u.id = :memberId") @@ -24,5 +23,7 @@ public interface FavoriteRepo extends JpaRepository { List findByMemberId(long memberId); + Optional findByMemberAndProperty(Member member, Property property); + } \ No newline at end of file diff --git a/backend/pms/src/main/java/com/mini/pms/service/impl/FavoriteServiceImpl.java b/backend/pms/src/main/java/com/mini/pms/service/impl/FavoriteServiceImpl.java index d502f07..f43aaae 100644 --- a/backend/pms/src/main/java/com/mini/pms/service/impl/FavoriteServiceImpl.java +++ b/backend/pms/src/main/java/com/mini/pms/service/impl/FavoriteServiceImpl.java @@ -1,7 +1,6 @@ package com.mini.pms.service.impl; -import com.mini.pms.customexception.MemberNotFoundException; -import com.mini.pms.customexception.PropertyNotFoundException; +import com.mini.pms.customexception.PlatformException; import com.mini.pms.entity.Favorite; import com.mini.pms.entity.Member; import com.mini.pms.entity.Property; @@ -11,32 +10,34 @@ import com.mini.pms.service.PropertyService; import jakarta.transaction.Transactional; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; import java.util.List; import java.util.stream.Collectors; @Service -public class FavoriteServiceImpl implements FavoriteService{ +public class FavoriteServiceImpl implements FavoriteService { @Autowired private FavoriteRepo favoriteRepo; @Autowired private MemberService memberService; @Autowired private PropertyService propertyService; + @Override public void addFavorite(long propertyId, long memberId) { // Retrieve the Member and Property entities using the IDs provided in the DTO Member member = memberService.findById(memberId); Property property = propertyService.findById(propertyId); - // Check if the member exists - if (member == null) { - throw new MemberNotFoundException("Member with ID " + memberId + " not found."); - } - // Check if the property exists - if (property == null) { - throw new PropertyNotFoundException("Property with ID " + propertyId + " not found."); + + var existingFav = favoriteRepo.findByMemberAndProperty(member, property); + + if (existingFav.isPresent()) { + throw new PlatformException("Favorite already exist", HttpStatus.BAD_REQUEST); } + + // At this point, both member and property exist // Create a new Favorite entity and set its member and property Favorite favorite = new Favorite(); @@ -71,6 +72,7 @@ public List getFavoriteByMemberId(long memberId) { public List getFavoritePropertyId(long propertyId) { return favoriteRepo.findFavoritesUsersByPropertyId(propertyId); } + @Override public List findFavoritesByMemberId(long memberId) { List favorites = favoriteRepo.findByMemberId(memberId); @@ -79,10 +81,10 @@ public List findFavoritesByMemberId(long memberId) { .collect(Collectors.toList()); } -// @Override -// public void unFavorite(long favoriteId) { -// favoriteRepo.deleteById(favoriteId); -// } + public Favorite findFavoriteByMemberAndProperty(Member member, Property property) { + return favoriteRepo.findByMemberAndProperty(member, property) + .orElseThrow(() -> new PlatformException("Not Found", HttpStatus.NOT_FOUND)); + } + - } diff --git a/frontend/src/components/Property.js b/frontend/src/components/Property.js index 0886ed9..ede1599 100644 --- a/frontend/src/components/Property.js +++ b/frontend/src/components/Property.js @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useState } from "react"; import { Card, Button, Badge } from "react-bootstrap"; import { MdFavoriteBorder, MdOutlineFavorite } from "react-icons/md"; import { Link } from "react-router-dom"; @@ -19,13 +19,18 @@ const Property = ({ const addFavorite = async (id) => { api.post(`favorites/${id}`).then((res) => { console.log(res); + setIsFav(true); }); }; const removeFavorite = (id) => { api.delete(`favorites/${id}`).then((res) => { console.log(res); + setIsFav(false); }); }; + + const [isFav, setIsFav] = useState(favorite); + return ( @@ -50,14 +55,15 @@ const Property = ({ }>{offerStatus} + { viewOffer && (