Changeset 2730163
- Timestamp:
- 01/31/26 22:34:40 (5 months ago)
- Branches:
- main
- Children:
- d2af1a6
- Parents:
- 2b08bed
- Files:
-
- 6 added
- 18 edited
- 1 moved
-
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) (1 diff)
-
finkwave/src/main/java/com/ukim/finki/develop/finkwave/controller/PlaylistController.java (added)
-
finkwave/src/main/java/com/ukim/finki/develop/finkwave/exceptions/AlbumNotFoundException.java (added)
-
finkwave/src/main/java/com/ukim/finki/develop/finkwave/exceptions/PlaylistNotFoundException.java (added)
-
finkwave/src/main/java/com/ukim/finki/develop/finkwave/model/dto/ArtistContributionDto.java (modified) (1 diff)
-
finkwave/src/main/java/com/ukim/finki/develop/finkwave/model/dto/PlaylistDto.java (modified) (2 diffs)
-
finkwave/src/main/java/com/ukim/finki/develop/finkwave/repository/ArtistContributionRepository.java (modified) (1 diff)
-
finkwave/src/main/java/com/ukim/finki/develop/finkwave/repository/FollowRepository.java (modified) (1 diff)
-
finkwave/src/main/java/com/ukim/finki/develop/finkwave/repository/NonAdminUserRepository.java (modified) (1 diff)
-
finkwave/src/main/java/com/ukim/finki/develop/finkwave/repository/SongRepository.java (modified) (1 diff)
-
finkwave/src/main/java/com/ukim/finki/develop/finkwave/service/AlbumService.java (modified) (2 diffs)
-
finkwave/src/main/java/com/ukim/finki/develop/finkwave/service/ArtistService.java (modified) (1 diff)
-
finkwave/src/main/java/com/ukim/finki/develop/finkwave/service/FollowService.java (modified) (7 diffs)
-
finkwave/src/main/java/com/ukim/finki/develop/finkwave/service/LikeService.java (added)
-
finkwave/src/main/java/com/ukim/finki/develop/finkwave/service/ListenerService.java (modified) (1 diff)
-
finkwave/src/main/java/com/ukim/finki/develop/finkwave/service/NonAdminUserService.java (modified) (1 diff)
-
finkwave/src/main/java/com/ukim/finki/develop/finkwave/service/PlaylistService.java (added)
-
frontend/src/App.tsx (modified) (2 diffs)
-
frontend/src/components/userProfile/ListenerView.tsx (modified) (3 diffs)
-
frontend/src/components/userProfile/UserListModal.tsx (modified) (1 diff)
-
frontend/src/pages/MusicalCollection.tsx (modified) (6 diffs)
-
frontend/src/pages/UserDetail.tsx (moved) (moved from frontend/src/pages/UserDetailView.tsx ) (8 diffs)
-
frontend/src/utils/error.ts (added)
-
frontend/src/utils/types.ts (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
-
finkwave/src/main/java/com/ukim/finki/develop/finkwave/controller/GlobalExceptionHandler.java
r2b08bed r2730163 3 3 import java.util.Map; 4 4 5 import com.ukim.finki.develop.finkwave.exceptions.AlbumNotFoundException; 5 6 import com.ukim.finki.develop.finkwave.exceptions.FollowException; 6 7 import com.ukim.finki.develop.finkwave.exceptions.UserNotFoundException; … … 49 50 } 50 51 52 @ExceptionHandler(AlbumNotFoundException.class) 53 public ResponseEntity<Map<String, String>> handleAlbumNotFound(AlbumNotFoundException ex) { 54 return ResponseEntity 55 .status(HttpStatus.BAD_REQUEST) 56 .body(Map.of("error", ex.getMessage())); 57 } 58 51 59 52 60 -
finkwave/src/main/java/com/ukim/finki/develop/finkwave/controller/NonAdminUserController.java
r2b08bed r2730163 35 35 } 36 36 37 @GetMapping("/ followers/{id}")37 @GetMapping("/{id}/followers") 38 38 public HttpEntity<List<NonAdminUserDto>>getFollowersForUser(@PathVariable Long id){ 39 39 return ResponseEntity.ok(followService.getFollowersForUser(id)); 40 40 } 41 41 42 @GetMapping("/ following/{id}")42 @GetMapping("/{id}/following") 43 43 public HttpEntity<List<NonAdminUserDto>>getFollowingForUser(@PathVariable Long id){ 44 44 return ResponseEntity.ok(followService.getFollowingForUser(id)); 45 45 } 46 46 47 @PostMapping("/ follow/{id}")47 @PostMapping("/{id}/follow") 48 48 public HttpEntity<NonAdminUserDto>followUser(@PathVariable Long id){ 49 49 followService.toggleFollow(id); -
finkwave/src/main/java/com/ukim/finki/develop/finkwave/model/dto/ArtistContributionDto.java
r2b08bed r2730163 14 14 private String role; 15 15 private String entityType; 16 private Boolean isLikedByCurrentUser; 16 17 } -
finkwave/src/main/java/com/ukim/finki/develop/finkwave/model/dto/PlaylistDto.java
r2b08bed r2730163 4 4 import lombok.Getter; 5 5 import lombok.Setter; 6 7 import java.util.List; 6 8 7 9 @AllArgsConstructor … … 14 16 private String creatorName; 15 17 18 private List<MusicalEntityDto>songsInPlaylist; 19 16 20 } -
finkwave/src/main/java/com/ukim/finki/develop/finkwave/repository/ArtistContributionRepository.java
r2b08bed r2730163 17 17 "ac.musicalEntity.title," + 18 18 "ac.musicalEntity.genre," + 19 "ac.role, "+ 20 "CASE WHEN s.id IS NOT NULL THEN 'SONG' "+ 21 "WHEN a.id IS NOT NULL THEN 'ALBUM' "+ 22 "ELSE 'Unknown' END) "+ 23 "FROM ArtistContribution ac "+ 24 "LEFT JOIN Song s ON s.musicalEntities.id=ac.musicalEntity.id "+ 25 "LEFT JOIN Album a ON a.musicalEntities.id=ac.musicalEntity.id "+ 26 "WHERE ac.artist.id=:artistId") 27 List<ArtistContributionDto>findContributionsByArtistId(@Param("artistId")Long artistId); 19 "ac.role, " + 20 "CASE WHEN s.id IS NOT NULL THEN 'SONG' " + 21 "WHEN a.id IS NOT NULL THEN 'ALBUM' " + 22 "ELSE 'Unknown' END," + 23 "(CASE WHEN l.id IS NOT NULL THEN true ELSE false END)) " + 24 "FROM ArtistContribution ac " + 25 "LEFT JOIN Song s ON s.musicalEntities.id=ac.musicalEntity.id " + 26 "LEFT JOIN Album a ON a.musicalEntities.id=ac.musicalEntity.id " + 27 "LEFT JOIN Like l ON l.musicalEntity.id=ac.musicalEntity.id AND l.listener.id=:currentUserId " + 28 "WHERE ac.artist.id=:artistId") 29 List<ArtistContributionDto>findContributionsByArtistId(@Param("currentUserId")Long currentUserId,@Param("artistId")Long artistId); 28 30 } -
finkwave/src/main/java/com/ukim/finki/develop/finkwave/repository/FollowRepository.java
r2b08bed r2730163 25 25 boolean isFollowing(@Param("currentUserId") Long currentUSerId,@Param("selectedUserId")Long selectedUserId); 26 26 27 List<Follow>getFollowsByFollowee_Id(Long id);28 27 29 28 List<Follow>getFollowsByFollower_Id(Long id); 30 29 30 @Query("SELECT f FROM Follow f " + 31 "JOIN FETCH f.follower nu " + 32 "JOIN FETCH nu.user " + 33 "WHERE f.followee.id = :id") 34 List<Follow> findFollowersWithProfile(@Param("id") Long id); 35 36 @Query("SELECT f FROM Follow f " + 37 "JOIN FETCH f.followee nu " + 38 "JOIN FETCH nu.user " + 39 "WHERE f.follower.id = :id") 40 List<Follow> findFollowingWithProfile(@Param("id") Long id); 41 31 42 } -
finkwave/src/main/java/com/ukim/finki/develop/finkwave/repository/NonAdminUserRepository.java
r2b08bed r2730163 16 16 17 17 @Query("SELECT u FROM NonAdminUser u " + 18 "WHERE LOWER(u.user.fullName) LIKE LOWER(CONCAT('%', :name, '%') )"+19 "OR LOWER(u.user.username) LIKE LOWER(CONCAT('%', :name, '%' )) ")18 "WHERE u.user.fullName ILIKE CONCAT('%', :name, '%' ) "+ 19 "OR u.user.username ILIKE CONCAT('%', :name, '%' ) ") 20 20 List<NonAdminUser> searchByName(String name); 21 21 -
finkwave/src/main/java/com/ukim/finki/develop/finkwave/repository/SongRepository.java
r2b08bed r2730163 15 15 16 16 17 @Query("SELECT NEW com.ukim.finki.develop.finkwave.model.dto.MusicalEntityDto( "+17 @Query("SELECT NEW com.ukim.finki.develop.finkwave.model.dto.MusicalEntityDto(" + 18 18 "s.id, " + 19 19 "me.title, " + 20 20 "me.genre, " + 21 "'SONG', " +21 "'SONG', " + 22 22 "u.fullName, " + 23 "(CASE WHEN l.id IS NOT NULL THEN true ELSE false END)) "+ 24 "FROM Song s "+ 25 "JOIN MusicalEntity me on me.id=s.musicalEntities.id "+ 26 "JOIN User u on u.id=me.releasedBy.id "+ 27 "LEFT JOIN Like l on l.musicalEntity.id=s.id AND l.listener.id=:currentUserId "+ 28 "WHERE s.album.id=:albumId" 29 23 "(CASE WHEN :currentUserId IS NOT NULL AND l.id IS NOT NULL THEN true ELSE false END)) " + 24 "FROM Song s " + 25 "JOIN s.musicalEntities me " + 26 "JOIN me.releasedBy a " + 27 "JOIN a.nonAdminUser nau " + 28 "JOIN nau.user u "+ 29 "LEFT JOIN Like l ON l.musicalEntity.id = s.id AND l.listener.id = :currentUserId " + 30 "WHERE s.album.id = :albumId" 30 31 ) 31 32 List<MusicalEntityDto>findSongsByAlbum(@Param("albumId") Long albumId, @Param("currentUserId")Long currentUserId); 33 34 35 @Query("SELECT NEW com.ukim.finki.develop.finkwave.model.dto.MusicalEntityDto(" + 36 "s.id, " + 37 "me.title, " + 38 "me.genre, " + 39 "'SONG', " + 40 "u.fullName, " + 41 "(CASE WHEN :currentUserId IS NOT NULL AND l.id IS NOT NULL THEN true ELSE false END)) " + 42 "FROM Song s "+ 43 "JOIN s.musicalEntities me "+ 44 "JOIN me.releasedBy a "+ 45 "JOIN a.nonAdminUser nau "+ 46 "JOIN nau.user u "+ 47 "JOIN PlaylistSong ps ON ps.song.id=s.id "+ 48 "LEFT JOIN Like l ON l.musicalEntity.id=s.id AND l.listener.id=:currentUserId "+ 49 "WHERE ps.playlist.id=:playlistId") 50 List<MusicalEntityDto>findSongsByPlaylistId(@Param("playlistId")Long playlistId, @Param("currentUserId")Long currentUserId); 32 51 } -
finkwave/src/main/java/com/ukim/finki/develop/finkwave/service/AlbumService.java
r2b08bed r2730163 1 1 package com.ukim.finki.develop.finkwave.service; 2 2 3 import com.ukim.finki.develop.finkwave.exceptions.AlbumNotFoundException; 3 4 import com.ukim.finki.develop.finkwave.model.Album; 4 5 import com.ukim.finki.develop.finkwave.model.MusicalEntity; … … 23 24 public MusicalEntityDto getAlbum(Long id){ 24 25 if (!albumRepository.existsById(id)){ 25 throw new RuntimeException("Album not found");26 throw new AlbumNotFoundException(id); 26 27 } 27 28 Long currentUserId=authService.getCurrentUserID(); 28 29 29 Album album=albumRepository.findById(id).orElseThrow(()->new RuntimeException("Album not found"));30 Album album=albumRepository.findById(id).orElseThrow(()->new AlbumNotFoundException(id)); 30 31 AlbumDto dto=new AlbumDto(album.getId(), 31 32 album.getMusicalEntities().getTitle(), -
finkwave/src/main/java/com/ukim/finki/develop/finkwave/service/ArtistService.java
r2b08bed r2730163 16 16 17 17 private final ArtistContributionRepository artistContributionRepository; 18 private final AuthService authService; 18 19 19 20 20 21 @Transactional(readOnly=true) 21 22 public List<ArtistContributionDto> getArtistContributions(Long artistId){ 22 return artistContributionRepository.findContributionsByArtistId(artistId); 23 Long currentUserId=authService.getCurrentUserID(); 24 return artistContributionRepository.findContributionsByArtistId(currentUserId,artistId); 23 25 } 24 26 -
finkwave/src/main/java/com/ukim/finki/develop/finkwave/service/FollowService.java
r2b08bed r2730163 16 16 import java.util.ArrayList; 17 17 import java.util.List; 18 import java.util.Set; 18 19 import java.util.stream.Collectors; 19 20 … … 29 30 30 31 32 private boolean isFollowedByCurrentUser(Long currentUserId,Long otherUserId){ 33 Set<Long> currentUserFollows = followRepository.getFollowsByFollower_Id(currentUserId) 34 .stream() 35 .map(f -> f.getFollowee().getId()) 36 .collect(Collectors.toSet()); 37 return currentUserFollows.contains(otherUserId); 38 } 31 39 32 40 public List<NonAdminUserDto>getFollowersForUser(Long id){ … … 34 42 throw new FollowException("Cannot view for artist"); 35 43 } 36 List<Follow> followers=followRepository.getFollowsByFollowee_Id(id);44 List<Follow> followers = followRepository.findFollowersWithProfile(id); 37 45 Long currentUserId=authService.getCurrentUserID(); 46 47 38 48 return followers.stream() 39 .map(f->{ 40 NonAdminUserDto dto=mapper.toDto(f.getFollower(),null,null,null); 41 dto.setIsFollowedByCurrentUser(followRepository.isFollowing(currentUserId,f.getFollower().getId())); 42 return dto; 49 .map(f -> { 50 NonAdminUser follower = f.getFollower(); 51 NonAdminUserDto dto = mapper.toDto(follower, null, null, null); 52 53 dto.setIsFollowedByCurrentUser(isFollowedByCurrentUser(currentUserId,follower.getId())); 54 return dto; 43 55 }) 44 56 .toList(); … … 49 61 throw new FollowException("Cannot view for artist"); 50 62 } 51 List<Follow>follow ers=followRepository.getFollowsByFollower_Id(id);63 List<Follow>followings=followRepository.getFollowsByFollower_Id(id); 52 64 Long currentUserId=authService.getCurrentUserID(); 53 return follow ers.stream()65 return followings.stream() 54 66 .map(f->{ 67 NonAdminUser followee=f.getFollowee(); 55 68 NonAdminUserDto dto=mapper.toDto(f.getFollowee(),null,null,null); 56 dto.setIsFollowedByCurrentUser( followRepository.isFollowing(currentUserId,f.getFollowee().getId()));69 dto.setIsFollowedByCurrentUser(isFollowedByCurrentUser(currentUserId,followee.getId())); 57 70 return dto; 58 71 }) … … 62 75 63 76 64 public booleantoggleFollow(Long id){77 public void toggleFollow(Long id){ 65 78 Long currentUserId=authService.getCurrentUserID(); 66 79 … … 74 87 if (followRepository.existsById(followId)){ 75 88 followRepository.deleteById(followId); 76 return false; 89 77 90 }else{ 78 91 NonAdminUser follower = nonAdminUserRepository.findById(currentUserId) … … 82 95 83 96 followRepository.save(new Follow(follower, followee)); 84 return true; 97 85 98 } 86 99 -
finkwave/src/main/java/com/ukim/finki/develop/finkwave/service/ListenerService.java
r2b08bed r2730163 13 13 @Service 14 14 @AllArgsConstructor 15 @Transactional(readOnly = true) 15 16 public class ListenerService { 16 private final Like Repository likeRepository;17 private final Playlist Repository playlistRepository;18 @Transactional(readOnly = true) 17 private final LikeService likeService; 18 private final PlaylistService playlistService; 19 19 20 public List<MusicalEntityDto> getLikedEntities(Long listenerId) { 20 return like Repository.findLikedEntitiesWithTypeByListenerId(listenerId);21 return likeService.findLikedEntitiesWithTypeByListenerId(listenerId); 21 22 22 23 23 24 } 24 25 25 @Transactional(readOnly = true) 26 26 27 public List<Playlist>getPlaylistsCreatedByUser(Long listenerId){ 27 return playlist Repository.findByCreatorId(listenerId);28 return playlistService.findByCreatorId(listenerId); 28 29 } 29 30 } -
finkwave/src/main/java/com/ukim/finki/develop/finkwave/service/NonAdminUserService.java
r2b08bed r2730163 82 82 p.getName(), 83 83 p.getCover(), 84 p.getCreatedBy().getNonAdminUser().getUser().getFullName() 84 p.getCreatedBy().getNonAdminUser().getUser().getFullName(), 85 null 85 86 )).toList(); 86 87 -
frontend/src/App.tsx
r2b08bed r2730163 7 7 import Nav from "./pages/Nav"; 8 8 import Register from "./pages/Register"; 9 import UserDetail View from "./pages/UserDetailView";9 import UserDetail from "./pages/UserDetail"; 10 10 import MusicalCollection from "./pages/MusicalCollection"; 11 11 … … 52 52 { 53 53 path: "/users/:userId", 54 element: <UserDetail View/>,54 element: <UserDetail />, 55 55 }, 56 56 { -
frontend/src/components/userProfile/ListenerView.tsx
r2b08bed r2730163 20 20 <div className="flex items-center gap-2 mb-6 pb-2 border-b-2 border-gray-200"> 21 21 <ListMusic className="w-6 h-6 text-gray-700" /> 22 <h3 className="text-2xl font-bold text-gray-800">My Playlists</h3> 22 <h3 className="text-2xl font-bold text-gray-800"> 23 Created Playlists 24 </h3> 23 25 <span className="text-sm text-gray-400 ml-1"> 24 26 ({playlists.length}) … … 31 33 key={playlist.id} 32 34 className="group cursor-pointer" 33 onClick={() => navigate(`/ playlists/${playlist.id}`)}35 onClick={() => navigate(`/collection/playlist/${playlist.id}`)} 34 36 > 35 37 <div className="aspect-square rounded-md overflow-hidden bg-gray-100 mb-2 relative shadow-sm group-hover:shadow-lg transition-all"> … … 105 107 key={album.id} 106 108 className="group cursor-pointer" 107 onClick={() => navigate(`/ musical-entity/${album.id}`)}109 onClick={() => navigate(`/collection/album/${album.id}`)} 108 110 > 109 111 <div className="aspect-square rounded-lg overflow-hidden bg-gradient-to-br from-blue-100 to-indigo-100 mb-3 flex items-center justify-center shadow-sm group-hover:shadow-md transition-all"> -
frontend/src/components/userProfile/UserListModal.tsx
r2b08bed r2730163 25 25 <button 26 26 onClick={onClose} 27 className="text-gray-500 hover:text-black text-2xl "27 className="text-gray-500 hover:text-black text-2xl cursor-pointer" 28 28 > 29 29 × -
frontend/src/pages/MusicalCollection.tsx
r2b08bed r2730163 2 2 import { useNavigate, useParams } from "react-router-dom"; 3 3 import axiosInstance from "../api/axiosInstance"; 4 5 interface Song { 4 import type { Song, Album, Playlist } from "../utils/types"; 5 import { handleError } from "../utils/error"; 6 interface CollectionView { 6 7 id: number; 7 8 title: string; 8 genre: string; 9 type: "SONG"; 10 releasedBy: string; 11 isLikedByCurrentUser?: boolean; 12 } 13 14 interface MusicalEntity { 15 id: number; 16 title: string; 17 genre: string; 9 genre?: string; 18 10 type: string; 19 11 releasedBy: string; 20 12 isLikedByCurrentUser?: boolean; 21 songs ?: Song[];13 songs: Song[]; 22 14 } 23 15 … … 25 17 const { type, id } = useParams(); 26 18 const navigate = useNavigate(); 27 const [collection, setCollection] = useState< MusicalEntity| null>(null);19 const [collection, setCollection] = useState<CollectionView | null>(null); 28 20 const [isLoading, setIsLoading] = useState(true); 29 21 const [error, setError] = useState<string | null>(null); 22 23 const normalizeCollection = ( 24 data: Album | Playlist, 25 type: string, 26 ): CollectionView => { 27 if (type === "album") { 28 const album = data as Album; 29 return { 30 id: album.id, 31 title: album.title, 32 genre: album.genre, 33 type: album.type, 34 releasedBy: album.releasedBy, 35 isLikedByCurrentUser: album.isLikedByCurrentUser, 36 songs: album.songs, 37 }; 38 } else { 39 const playlist = data as Playlist; 40 return { 41 id: playlist.id, 42 title: playlist.name, 43 genre: undefined, 44 type: "PLAYLIST", 45 releasedBy: playlist.creatorName, 46 isLikedByCurrentUser: undefined, 47 songs: playlist.songsInPlaylist, 48 }; 49 } 50 }; 30 51 31 52 useEffect(() => { … … 37 58 type === "album" ? `/albums/${id}` : `/playlists/${id}`; 38 59 const response = await axiosInstance.get(endpoint); 39 console.log(response.data); 40 setCollection(response.data); 60 61 const normalized = normalizeCollection(response.data, type!); 62 setCollection(normalized); 41 63 } catch (err: any) { 42 setError( err.response?.data?.error || "Failed to load collection");64 setError(handleError(err)); 43 65 } finally { 44 66 setIsLoading(false); … … 67 89 <button 68 90 onClick={() => navigate(-1)} 69 className="mb-4 px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600 transition-colors duration-200 "91 className="mb-4 px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600 transition-colors duration-200 cursor-pointer" 70 92 > 71 93 ← Back … … 88 110 <div className="flex items-center gap-3 text-gray-700 mb-4"> 89 111 <span className="font-semibold">{collection.releasedBy}</span> 90 <span>•</span> 91 <span className="text-gray-600">{collection.genre}</span> 112 {collection.genre && ( 113 <> 114 <span>•</span> 115 <span className="text-gray-600">{collection.genre}</span> 116 </> 117 )} 92 118 {collection.songs && ( 93 119 <> … … 101 127 </div> 102 128 103 <button 104 className="flex items-center gap-2 px-4 py-2 bg-gray-100 hover:bg-gray-200 rounded-lg transition-colors duration-200" 105 aria-label={collection.isLikedByCurrentUser ? "Unlike" : "Like"} 106 > 107 <svg 108 className="w-5 h-5" 109 fill={collection.isLikedByCurrentUser ? "#ef4444" : "none"} 110 stroke={collection.isLikedByCurrentUser ? "#ef4444" : "#6b7280"} 111 strokeWidth="2" 112 viewBox="0 0 24 24" 129 {type === "album" && ( 130 <button 131 className="flex items-center gap-2 px-4 py-2 bg-gray-100 hover:bg-gray-200 rounded-lg transition-colors duration-200" 132 aria-label={collection.isLikedByCurrentUser ? "Unlike" : "Like"} 113 133 > 114 <path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z" /> 115 </svg> 116 <span className="text-sm font-medium text-gray-700"> 117 {collection.isLikedByCurrentUser ? "Liked" : "Like"} 118 </span> 119 </button> 134 <svg 135 className="w-5 h-5" 136 fill={collection.isLikedByCurrentUser ? "#ef4444" : "none"} 137 stroke={ 138 collection.isLikedByCurrentUser ? "#ef4444" : "#6b7280" 139 } 140 strokeWidth="2" 141 viewBox="0 0 24 24" 142 > 143 <path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z" /> 144 </svg> 145 <span className="text-sm font-medium text-gray-700"> 146 {collection.isLikedByCurrentUser ? "Liked" : "Like"} 147 </span> 148 </button> 149 )} 120 150 </div> 121 151 </div> -
frontend/src/pages/UserDetail.tsx
r2b08bed r2730163 5 5 import ListenerView from "../components/userProfile/ListenerView"; 6 6 import UserListModal from "../components/userProfile/UserListModal"; 7 import { handleError } from "../utils/error"; 7 8 import type { 8 9 MusicalEntity, … … 43 44 try { 44 45 const response = await axiosInstance.post<UserProfile>( 45 `/users/ follow/${userId}`,46 `/users/${userId}/follow`, 46 47 ); 47 48 setUser(response.data); 48 49 } catch (err: any) { 49 console.error(err.response?.data?.error);50 setError(handleError(err)); 50 51 } finally { 51 52 setIsFollowing(false); … … 56 57 setIsLoadingModal(true); 57 58 try { 58 const response = await axiosInstance.get(`/users/ followers/${userId}`);59 const response = await axiosInstance.get(`/users/${userId}/followers`); 59 60 setModalUsers(response.data); 60 61 setModalTitle("Followers"); 61 62 setShowModal(true); 62 63 } catch (err) { 63 console.error("Failed to fetch followers");64 setError(handleError(err)); 64 65 } finally { 65 66 setIsLoadingModal(false); … … 69 70 setIsLoadingModal(true); 70 71 try { 71 const response = await axiosInstance.get(`/users/ following/${userId}`);72 const response = await axiosInstance.get(`/users/${userId}/following`); 72 73 setModalUsers(response.data); 73 74 setModalTitle("Following"); 74 75 setShowModal(true); 75 } catch (err ) {76 console.error("Failed to fetch following users");76 } catch (err: any) { 77 setError(handleError(err)); 77 78 } finally { 78 79 setIsLoadingModal(false); … … 82 83 const handleFollowInModal = async (targetId: number) => { 83 84 try { 84 await axiosInstance.post(`/users/ follow/${targetId}`);85 await axiosInstance.post(`/users/${targetId}/follow`); 85 86 setModalUsers((prevUsers) => 86 87 prevUsers.map((u) => { … … 96 97 ); 97 98 98 if (user && user.id === targetId) {99 const response = await axiosInstance.get(`/users/${targetId}`);100 setUser(response.data);101 }102 } catch (err ) {103 console.error("Failed to toggle follow in modal", err);99 // if (user && user.id === targetId) { 100 // const response = await axiosInstance.get(`/users/${targetId}`); 101 // setUser(response.data); 102 // } 103 } catch (err: any) { 104 setError(handleError(err)); 104 105 } 105 106 }; … … 110 111 try { 111 112 const response = await axiosInstance.get(`/users/${userId}`); 112 console.log(response.data);113 113 setUser(response.data); 114 114 } catch (err: any) { 115 const errorMessage = 116 err.response?.data?.error || "Failed to fetch user"; 117 setError(errorMessage); 115 setError(handleError(err)); 118 116 } 119 117 }; … … 143 141 <button 144 142 onClick={() => navigate(-1)} 145 className="mb-4 px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600 transition-colors duration-200 "143 className="mb-4 px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600 transition-colors duration-200 cursor-pointer" 146 144 > 147 145 ← Back -
frontend/src/utils/types.ts
r2b08bed r2730163 13 13 role: string; 14 14 entityType: string; 15 isLikedByCurrentUser: boolean; 15 16 } 17 16 18 export interface MusicalEntity { 17 19 id: number; … … 20 22 type: string; 21 23 releasedBy: string; 24 isLikedByCurrentUser?: boolean; 25 } 26 27 export interface Song extends MusicalEntity { 28 type: "SONG"; 29 } 30 31 export interface Album extends MusicalEntity { 32 type: "ALBUM"; 33 songs: Song[]; 22 34 } 23 35 … … 27 39 cover: string; 28 40 creatorName: string; 41 songsInPlaylist: Song[]; 29 42 } 30 43
Note:
See TracChangeset
for help on using the changeset viewer.
