Changeset ce45c7a for frontend/src/components/userProfile/ArtistView.tsx
- Timestamp:
- 02/15/26 00:39:55 (5 months ago)
- Branches:
- main
- Children:
- c8baad1
- Parents:
- 85512ff
- File:
-
- 1 edited
-
frontend/src/components/userProfile/ArtistView.tsx (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
frontend/src/components/userProfile/ArtistView.tsx
r85512ff rce45c7a 8 8 9 9 interface ArtistViewProps { 10 contributions: ArtistContribution[];10 contributions: ArtistContribution[]; 11 11 } 12 12 13 13 const ArtistView = ({ contributions }: ArtistViewProps) => { 14 const navigate = useNavigate(); 15 const [items, setItems] = useState(contributions); 14 const navigate = useNavigate(); 15 const [items, setItems] = useState(contributions); 16 const [openDropdownSongId, setOpenDropdownSongId] = useState<number | null>( 17 null, 18 ); 16 19 17 const albums = items.filter((c) => c.entityType === "ALBUM");18 const songs = items.filter((c) => c.entityType === "SONG");20 const albums = items.filter((c) => c.entityType === "ALBUM"); 21 const songs = items.filter((c) => c.entityType === "SONG"); 19 22 20 const getRoleColor = (role: string) => {21 const colors: { [key: string]: string } = {22 COMPOSER: "bg-purple-500/20 text-purple-300",23 PERFORMER: "bg-blue-500/20 text-blue-300",24 PRODUCER: "bg-green-500/20 text-green-300",25 MAIN_VOCAL: "bg-pink-500/20 text-pink-300",26 };27 return colors[role] || "bg-white/10 text-gray-300";28 };23 const getRoleColor = (role: string) => { 24 const colors: { [key: string]: string } = { 25 COMPOSER: "bg-purple-500/20 text-purple-300", 26 PERFORMER: "bg-blue-500/20 text-blue-300", 27 PRODUCER: "bg-green-500/20 text-green-300", 28 MAIN_VOCAL: "bg-pink-500/20 text-pink-300", 29 }; 30 return colors[role] || "bg-white/10 text-gray-300"; 31 }; 29 32 30 const handleLike = async (id: number, title: string) => {31 try {32 const response = await axiosInstance.post(`/musical-entity/${id}/like`);33 const data = response.data;33 const handleLike = async (id: number, title: string) => { 34 try { 35 const response = await axiosInstance.post(`/musical-entity/${id}/like`); 36 const data = response.data; 34 37 35 setItems((prevItems) =>36 prevItems.map((item) =>37 item.id === data.entityId38 ? { ...item, isLikedByCurrentUser: data.isLiked }39 : item,40 ),41 );42 toast.success(43 data.isLiked ? `Liked "${title}"` : `Removed "${title}" from likes`,44 );45 } catch (err: any) {46 toast.error(err.response?.data?.error || "Failed to like the item");47 }48 };38 setItems((prevItems) => 39 prevItems.map((item) => 40 item.id === data.entityId 41 ? { ...item, isLikedByCurrentUser: data.isLiked } 42 : item, 43 ), 44 ); 45 toast.success( 46 data.isLiked ? `Liked "${title}"` : `Removed "${title}" from likes`, 47 ); 48 } catch (err: any) { 49 toast.error(err.response?.data?.error || "Failed to like the item"); 50 } 51 }; 49 52 50 return (51 <div className="mt-8">52 {albums.length > 0 && (53 <div className="mb-12">54 <div className="flex items-center gap-3 mb-6">55 <Disc3 className="w-6 h-6 text-[#1db954]" />56 <h2 className="text-2xl font-bold text-white">Albums</h2>57 </div>58 <div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-6">59 {albums.map((album) => (60 <div61 key={album.id}62 className="group cursor-pointer"63 onClick={() => navigate(`/collection/album/${album.id}`)}64 >65 <div className="relative aspect-square rounded-lg mb-3 overflow-hidden shadow-md group-hover:shadow-xl transition-all duration-300 bg-[#181818]">66 <img67 src={68 album.cover ? `${baseURL}/${album.cover}` : "/favicon.png"69 }70 alt={album.title}71 className="w-full h-full object-cover"72 onError={(e) => {73 (e.target as HTMLImageElement).src = "/favicon.png";74 }}75 />53 return ( 54 <div className="mt-8"> 55 {albums.length > 0 && ( 56 <div className="mb-12"> 57 <div className="flex items-center gap-3 mb-6"> 58 <Disc3 className="w-6 h-6 text-[#1db954]" /> 59 <h2 className="text-2xl font-bold text-white">Albums</h2> 60 </div> 61 <div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-6"> 62 {albums.map((album) => ( 63 <div 64 key={album.id} 65 className="group cursor-pointer" 66 onClick={() => navigate(`/collection/album/${album.id}`)} 67 > 68 <div className="relative aspect-square rounded-lg mb-3 overflow-hidden shadow-md group-hover:shadow-xl transition-all duration-300 bg-[#181818]"> 69 <img 70 src={ 71 album.cover ? `${baseURL}/${album.cover}` : "/favicon.png" 72 } 73 alt={album.title} 74 className="w-full h-full object-cover" 75 onError={(e) => { 76 (e.target as HTMLImageElement).src = "/favicon.png"; 77 }} 78 /> 76 79 77 <button78 className="absolute top-2 right-2 p-2 bg-black/70 hover:bg-black/90 rounded-full shadow-md transition-all duration-200 opacity-0 group-hover:opacity-100 cursor-pointer"79 title={album.isLikedByCurrentUser ? "Unlike" : "Like"}80 onClick={(e) => {81 e.stopPropagation();82 handleLike(album.id, album.title);83 }}84 >85 <svg86 className="w-5 h-5"87 fill={album.isLikedByCurrentUser ? "#ef4444" : "none"}88 stroke={89 album.isLikedByCurrentUser ? "#ef4444" : "#9ca3af"90 }91 strokeWidth="2"92 viewBox="0 0 24 24"93 >94 <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" />95 </svg>96 </button>80 <button 81 className="absolute top-2 right-2 p-2 bg-black/70 hover:bg-black/90 rounded-full shadow-md transition-all duration-200 opacity-0 group-hover:opacity-100 cursor-pointer" 82 title={album.isLikedByCurrentUser ? "Unlike" : "Like"} 83 onClick={(e) => { 84 e.stopPropagation(); 85 handleLike(album.id, album.title); 86 }} 87 > 88 <svg 89 className="w-5 h-5" 90 fill={album.isLikedByCurrentUser ? "#ef4444" : "none"} 91 stroke={ 92 album.isLikedByCurrentUser ? "#ef4444" : "#9ca3af" 93 } 94 strokeWidth="2" 95 viewBox="0 0 24 24" 96 > 97 <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" /> 98 </svg> 99 </button> 97 100 98 <div className="absolute bottom-0 left-0 right-0 bg-linear-to-t from-black/80 to-transparent p-3">99 <span100 className={`text-xs px-2 py-1 rounded-full font-medium ${getRoleColor(album.role)}`}101 >102 {album.role.replace("_", " ")}103 </span>104 </div>105 </div>106 <h3 className="font-semibold text-sm line-clamp-2 text-white group-hover:text-[#1db954] transition-colors">107 {album.title}108 </h3>109 </div>110 ))}111 </div>112 </div>113 )}101 <div className="absolute bottom-0 left-0 right-0 bg-linear-to-t from-black/80 to-transparent p-3"> 102 <span 103 className={`text-xs px-2 py-1 rounded-full font-medium ${getRoleColor(album.role)}`} 104 > 105 {album.role.replace("_", " ")} 106 </span> 107 </div> 108 </div> 109 <h3 className="font-semibold text-sm line-clamp-2 text-white group-hover:text-[#1db954] transition-colors"> 110 {album.title} 111 </h3> 112 </div> 113 ))} 114 </div> 115 </div> 116 )} 114 117 115 {songs.length > 0 && ( 116 <div className="mb-12"> 117 <div className="flex items-center gap-3 mb-6"> 118 <Music className="w-6 h-6 text-[#1db954]" /> 119 <h2 className="text-2xl font-bold text-white">Songs</h2> 120 </div> 121 <div className="space-y-1"> 122 {songs.map((song) => ( 123 <SongItem 124 key={song.id} 125 song={{ 126 id: song.id, 127 title: song.title, 128 cover: song.cover, 129 genre: song.genre, 130 link: song.link, 131 isLikedByCurrentUser: song.isLikedByCurrentUser, 132 }} 133 role={song.role} 134 onLikeToggle={() => handleLike(song.id, song.title)} 135 /> 136 ))} 137 </div> 138 </div> 139 )} 118 {songs.length > 0 && ( 119 <div className="mb-12"> 120 <div className="flex items-center gap-3 mb-6"> 121 <Music className="w-6 h-6 text-[#1db954]" /> 122 <h2 className="text-2xl font-bold text-white">Songs</h2> 123 </div> 124 <div className="space-y-1"> 125 {songs.map((song) => ( 126 <SongItem 127 key={song.id} 128 song={{ 129 id: song.id, 130 title: song.title, 131 cover: song.cover, 132 genre: song.genre, 133 link: song.link, 134 isLikedByCurrentUser: song.isLikedByCurrentUser, 135 }} 136 role={song.role} 137 onLikeToggle={() => handleLike(song.id, song.title)} 138 isDropdownOpen={openDropdownSongId === song.id} 139 onDropdownToggle={setOpenDropdownSongId} 140 /> 141 ))} 142 </div> 143 </div> 144 )} 140 145 141 {contributions.length === 0 && (142 <div className="flex flex-col items-center justify-center py-16 text-gray-500">143 <Music className="w-20 h-20 mb-4 opacity-20" />144 <p className="text-lg font-medium">No contributions yet</p>145 <p className="text-sm mt-2">Start creating music to see it here</p>146 </div>147 )}148 </div>149 );146 {contributions.length === 0 && ( 147 <div className="flex flex-col items-center justify-center py-16 text-gray-500"> 148 <Music className="w-20 h-20 mb-4 opacity-20" /> 149 <p className="text-lg font-medium">No contributions yet</p> 150 <p className="text-sm mt-2">Start creating music to see it here</p> 151 </div> 152 )} 153 </div> 154 ); 150 155 }; 151 156
Note:
See TracChangeset
for help on using the changeset viewer.
