source: frontend/src/components/userProfile/ArtistView.tsx@ 615dcee

main
Last change on this file since 615dcee was 615dcee, checked in by Dimitar Arsov <dimitararsov04@…>, 5 months ago

hide actions from unauthenticated users

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