Ignore:
Timestamp:
02/15/26 00:39:55 (5 months ago)
Author:
Dimitar Arsov <dimitararsov04@…>
Branches:
main
Children:
c8baad1
Parents:
85512ff
Message:

add song to playlist

File:
1 edited

Legend:

Unmodified
Added
Removed
  • frontend/src/components/userProfile/ArtistView.tsx

    r85512ff rce45c7a  
    88
    99interface ArtistViewProps {
    10         contributions: ArtistContribution[];
     10  contributions: ArtistContribution[];
    1111}
    1212
    1313const 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  );
    1619
    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");
    1922
    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  };
    2932
    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;
    3437
    35                         setItems((prevItems) =>
    36                                 prevItems.map((item) =>
    37                                         item.id === data.entityId
    38                                                 ? { ...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  };
    4952
    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                                                         <div
    61                                                                 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                                                                         <img
    67                                                                                 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                  />
    7679
    77                                                                         <button
    78                                                                                 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                                                                                 <svg
    86                                                                                         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>
    97100
    98                                                                         <div className="absolute bottom-0 left-0 right-0 bg-linear-to-t from-black/80 to-transparent p-3">
    99                                                                                 <span
    100                                                                                         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      )}
    114117
    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      )}
    140145
    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  );
    150155};
    151156
Note: See TracChangeset for help on using the changeset viewer.