Changeset 6de2873
- Timestamp:
- 01/27/26 17:30:07 (6 months ago)
- Branches:
- main
- Children:
- 98992cf
- Parents:
- c1d2f07
- Files:
-
- 2 added
- 6 edited
-
finkwave/src/main/java/com/ukim/finki/develop/finkwave/controller/GlobalExceptionHandler.java (modified) (2 diffs)
-
finkwave/src/main/java/com/ukim/finki/develop/finkwave/controller/NonAdminUserController.java (modified) (3 diffs)
-
finkwave/src/main/java/com/ukim/finki/develop/finkwave/exceptions/FollowException.java (added)
-
finkwave/src/main/java/com/ukim/finki/develop/finkwave/model/Follow.java (modified) (3 diffs)
-
finkwave/src/main/java/com/ukim/finki/develop/finkwave/model/FollowId.java (modified) (2 diffs)
-
finkwave/src/main/java/com/ukim/finki/develop/finkwave/service/FollowService.java (added)
-
finkwave/src/main/java/com/ukim/finki/develop/finkwave/service/NonAdminUserService.java (modified) (4 diffs)
-
frontend/src/pages/UserDetailView.tsx (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
finkwave/src/main/java/com/ukim/finki/develop/finkwave/controller/GlobalExceptionHandler.java
rc1d2f07 r6de2873 3 3 import java.util.Map; 4 4 5 import com.ukim.finki.develop.finkwave.exceptions.FollowException; 6 import com.ukim.finki.develop.finkwave.exceptions.UserNotFoundException; 5 7 import org.slf4j.Logger; 6 8 import org.slf4j.LoggerFactory; … … 33 35 } 34 36 37 @ExceptionHandler(UserNotFoundException.class) 38 public ResponseEntity<Map<String, String>> handleUserNotFound(UserNotFoundException ex) { 39 return ResponseEntity 40 .status(HttpStatus.BAD_REQUEST) 41 .body(Map.of("error", ex.getMessage())); 42 } 43 44 @ExceptionHandler(FollowException.class) 45 public ResponseEntity<Map<String, String>> handleSelfFollow(FollowException ex) { 46 return ResponseEntity 47 .status(HttpStatus.BAD_REQUEST) 48 .body(Map.of("error", ex.getMessage())); 49 } 50 51 52 35 53 @ExceptionHandler(ResponseStatusException.class) 36 54 public ResponseEntity<Map<String, String>> handleResponseStatusException(ResponseStatusException ex) { -
finkwave/src/main/java/com/ukim/finki/develop/finkwave/controller/NonAdminUserController.java
rc1d2f07 r6de2873 2 2 3 3 import com.ukim.finki.develop.finkwave.model.dto.NonAdminUserDTO; 4 import com.ukim.finki.develop.finkwave.service.FollowService; 4 5 import com.ukim.finki.develop.finkwave.service.NonAdminUserService; 5 6 import lombok.RequiredArgsConstructor; … … 17 18 18 19 private final NonAdminUserService nonAdminUserService; 20 private final FollowService followService; 19 21 20 22 @GetMapping("/all") … … 32 34 return ResponseEntity.ok(nonAdminUserService.searchUsers(name)); 33 35 } 36 37 @PostMapping("/follow/{id}") 38 public HttpEntity<NonAdminUserDTO>followUser(@PathVariable Long id){ 39 followService.toggleFollow(id); 40 41 return ResponseEntity.ok(nonAdminUserService.getById(id)); 42 } 34 43 } -
finkwave/src/main/java/com/ukim/finki/develop/finkwave/model/Follow.java
rc1d2f07 r6de2873 3 3 import jakarta.persistence.*; 4 4 import lombok.Getter; 5 import lombok.NoArgsConstructor; 5 6 import lombok.Setter; 6 7 import org.hibernate.annotations.OnDelete; … … 10 11 @Setter 11 12 @Entity 13 @NoArgsConstructor 12 14 @Table(name = "follows", schema = "project") 13 15 public class Follow { … … 27 29 private NonAdminUser followee; 28 30 31 public Follow(NonAdminUser follower,NonAdminUser followee){ 32 this.follower=follower; 33 this.followee=followee; 34 this.id=new FollowId(follower.getId(),followee.getId()); 35 } 36 29 37 } -
finkwave/src/main/java/com/ukim/finki/develop/finkwave/model/FollowId.java
rc1d2f07 r6de2873 3 3 import jakarta.persistence.Column; 4 4 import jakarta.persistence.Embeddable; 5 import lombok.AllArgsConstructor; 5 6 import lombok.Getter; 7 import lombok.NoArgsConstructor; 6 8 import lombok.Setter; 7 9 import org.hibernate.Hibernate; … … 13 15 @Setter 14 16 @Embeddable 17 @NoArgsConstructor 18 @AllArgsConstructor 15 19 public class FollowId implements Serializable { 16 20 private static final long serialVersionUID = -915670330063692538L; -
finkwave/src/main/java/com/ukim/finki/develop/finkwave/service/NonAdminUserService.java
rc1d2f07 r6de2873 1 1 package com.ukim.finki.develop.finkwave.service; 2 2 3 import com.ukim.finki.develop.finkwave.exceptions.UserNotFoundException; 3 4 import com.ukim.finki.develop.finkwave.model.*; 4 5 import com.ukim.finki.develop.finkwave.model.dto.*; … … 66 67 private NonAdminUserDTO getArtistProfile(Long artistId, Long followers,Long following) { 67 68 Artist artist = artistRepository.findByIdWithUser(artistId) 68 .orElseThrow(() -> new RuntimeException("Artist not found with id: " + artistId));69 .orElseThrow(()->new UserNotFoundException("Artist not found with id: " + artistId)); 69 70 70 71 … … 77 78 private NonAdminUserDTO getListenerProfile(Long listenerId, Long followers,Long following) { 78 79 Listener listener = listenerRepository.findByIdWithUser(listenerId) 79 .orElseThrow(() -> new RuntimeException("Listener not found with id: " + listenerId));80 .orElseThrow(()->new UserNotFoundException("Listener not found with id: " + listenerId)); 80 81 81 82 … … 97 98 List<NonAdminUser>nonAdminUsers=nonAdminUserRepository.searchByName(name); 98 99 if (nonAdminUsers.isEmpty()){ 99 throw new RuntimeException("No user found");100 throw new UserNotFoundException("No users matched the criteria"); 100 101 } 101 102 -
frontend/src/pages/UserDetailView.tsx
rc1d2f07 r6de2873 5 5 import ListenerView from "../components/userProfile/ListenerView"; 6 6 import type { 7 ArtistContributionDTO,8 MusicalEntityDTO,9 Playlist,7 ArtistContributionDTO, 8 MusicalEntityDTO, 9 Playlist, 10 10 } from "../utils/types"; 11 11 12 12 interface User { 13 id: number;14 username: string;15 fullName: string;16 userType: string;17 followers: number;18 following: number;19 isFollowedByCurrentUser: boolean;13 id: number; 14 username: string; 15 fullName: string; 16 userType: string; 17 followers: number; 18 following: number; 19 isFollowedByCurrentUser: boolean; 20 20 21 musicalEntities?: {22 contributions: ArtistContributionDTO[];23 };21 musicalEntities?: { 22 contributions: ArtistContributionDTO[]; 23 }; 24 24 25 likes?: {26 likedEntities: MusicalEntityDTO[];27 };25 likes?: { 26 likedEntities: MusicalEntityDTO[]; 27 }; 28 28 29 createdPlaylists?: Playlist[];29 createdPlaylists?: Playlist[]; 30 30 } 31 31 32 32 const UserDetail = () => { 33 // user refers to the selected user NOT to the user from context 34 const { userId } = useParams(); 35 const navigate = useNavigate(); 36 const [user, setUser] = useState<User | null>(null); 37 const [error, setError] = useState<string | null>(null); 33 // user refers to the selected user NOT to the user from context 34 const { userId } = useParams(); 35 const navigate = useNavigate(); 36 const [user, setUser] = useState<User | null>(null); 37 const [error, setError] = useState<string | null>(null); 38 const [isFollowing, setIsFollowing] = useState(false); 38 39 39 useEffect(() => { 40 const fetchUser = async () => { 41 setError(null); 42 try { 43 const response = await axiosInstance.get(`/users/${userId}`); 40 const handleFollow = async () => { 41 if (!user) return; 44 42 45 setUser(response.data); 46 } catch (err: any) { 47 const errorMessage = 48 err.response?.data?.error || "Failed to fetch user"; 49 setError(errorMessage); 50 } 51 }; 52 fetchUser(); 53 }, [userId]); 43 setIsFollowing(true); 44 try { 45 const response = await axiosInstance.post<User>( 46 `/users/follow/${userId}`, 47 ); 48 setUser(response.data); 49 } catch (err: any) { 50 console.error(err.response?.data?.error); 51 } finally { 52 setIsFollowing(false); 53 } 54 }; 54 55 55 if (error) { 56 return ( 57 <div className="p-6 bg-red-50 border border-red-200 text-red-700 rounded-lg"> 58 <h2 className="font-bold">Error</h2> 59 <p>{error}</p> 60 </div> 61 ); 62 } 56 useEffect(() => { 57 const fetchUser = async () => { 58 setError(null); 59 try { 60 const response = await axiosInstance.get(`/users/${userId}`); 63 61 64 if (!user) return <div className="p-6">Loading...</div>; 62 setUser(response.data); 63 } catch (err: any) { 64 const errorMessage = 65 err.response?.data?.error || "Failed to fetch user"; 66 setError(errorMessage); 67 } 68 }; 69 fetchUser(); 70 }, [userId]); 65 71 66 return ( 67 <div className="container mx-auto p-6"> 68 <button 69 onClick={() => navigate(-1)} 70 className="mb-4 px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600" 71 >72 ← Back 73 </button> 72 if (error) { 73 return ( 74 <div className="p-6 bg-red-50 border border-red-200 text-red-700 rounded-lg"> 75 <h2 className="font-bold">Error</h2> 76 <p>{error}</p> 77 </div> 78 ); 79 } 74 80 75 <div className="bg-white shadow-lg rounded-lg p-8"> 76 <div className="flex items-start gap-6 mb-8"> 77 <div className="shrink-0"> 78 <div className="w-32 h-32 rounded-full bg-linear-to-br from-blue-400 to-purple-500 flex items-center justify-center text-white text-4xl font-bold shadow-lg"> 79 {user.fullName.charAt(0).toUpperCase()} 80 </div> 81 </div> 81 if (!user) return <div className="p-6">Loading...</div>; 82 82 83 <div className="flex-1"> 84 <h1 className="text-4xl font-bold mb-2">{user.fullName}</h1> 85 <span className="inline-block px-3 py-1 bg-blue-100 text-blue-800 rounded-full text-sm font-medium mb-4"> 86 {user.userType} 87 </span> 83 return ( 84 <div className="container mx-auto p-6"> 85 <button 86 onClick={() => navigate(-1)} 87 className="mb-4 px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600 transition-colors duration-200" 88 > 89 ← Back 90 </button> 88 91 89 <div className="flex gap-6 mb-4 text-gray-700"> 90 <div className="flex flex-col"> 91 <span className="text-2xl font-bold">{user.followers}</span> 92 <span className="text-sm text-gray-500">Followers</span> 93 </div> 94 <div className="flex flex-col"> 95 <span className="text-2xl font-bold">{user.following}</span> 96 <span className="text-sm text-gray-500">Following</span> 97 </div> 98 </div> 92 <div className="bg-white shadow-lg rounded-lg p-8"> 93 <div className="flex items-start gap-6 mb-8"> 94 <div className="shrink-0"> 95 <div className="w-32 h-32 rounded-full bg-linear-to-br from-blue-400 to-purple-500 flex items-center justify-center text-white text-4xl font-bold shadow-lg"> 96 {user.fullName.charAt(0).toUpperCase()} 97 </div> 98 </div> 99 99 100 <button className="px-6 py-2 bg-blue-500 hover:bg-blue-600 text-white font-semibold rounded-lg shadow-md transition-colors duration-200 cursor-pointer">101 {user.isFollowedByCurrentUser ? "Unfollow" : "Follow"} 102 </button>103 </div> 104 </div>100 <div className="flex-1"> 101 <h1 className="text-4xl font-bold mb-2">{user.fullName}</h1> 102 <span className="inline-block px-3 py-1 bg-blue-100 text-blue-800 rounded-full text-sm font-medium mb-4"> 103 {user.userType} 104 </span> 105 105 106 {user.userType === "ARTIST" && user.musicalEntities?.contributions && ( 107 <ArtistView contributions={user.musicalEntities.contributions} /> 108 )} 106 <div className="flex gap-6 mb-4 text-gray-700"> 107 <div className="flex flex-col"> 108 <span className="text-2xl font-bold">{user.followers}</span> 109 <span className="text-sm text-gray-500">Followers</span> 110 </div> 111 <div className="flex flex-col"> 112 <span className="text-2xl font-bold">{user.following}</span> 113 <span className="text-sm text-gray-500">Following</span> 114 </div> 115 </div> 109 116 110 {user.userType === "LISTENER" && user.likes?.likedEntities && ( 111 <ListenerView 112 likedEntities={user.likes.likedEntities} 113 playlists={user.createdPlaylists} 114 /> 115 )} 116 </div> 117 </div> 118 ); 117 <button 118 onClick={handleFollow} 119 disabled={isFollowing} 120 className={` 121 px-6 py-2 font-semibold rounded-lg shadow-md 122 transition-colors duration-200 123 ${ 124 isFollowing 125 ? "bg-gray-400 text-gray-200 cursor-not-allowed" 126 : user.isFollowedByCurrentUser 127 ? "bg-gray-200 text-gray-700 hover:bg-gray-300 cursor-pointer" 128 : "bg-blue-500 text-white hover:bg-blue-600 cursor-pointer" 129 } 130 `} 131 > 132 {user.isFollowedByCurrentUser ? "Unfollow" : "Follow"} 133 </button> 134 </div> 135 </div> 136 137 {user.userType === "ARTIST" && user.musicalEntities?.contributions && ( 138 <ArtistView contributions={user.musicalEntities.contributions} /> 139 )} 140 141 {user.userType === "LISTENER" && user.likes?.likedEntities && ( 142 <ListenerView 143 likedEntities={user.likes.likedEntities} 144 playlists={user.createdPlaylists} 145 /> 146 )} 147 </div> 148 </div> 149 ); 119 150 }; 120 151
Note:
See TracChangeset
for help on using the changeset viewer.
