source: frontend/src/pages/MusicalCollection.tsx@ 2b08bed

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

show followers, following and songs in album

  • Property mode set to 100644
File size: 6.1 KB
Line 
1import { useEffect, useState } from "react";
2import { useNavigate, useParams } from "react-router-dom";
3import axiosInstance from "../api/axiosInstance";
4
5interface Song {
6 id: number;
7 title: string;
8 genre: string;
9 type: "SONG";
10 releasedBy: string;
11 isLikedByCurrentUser?: boolean;
12}
13
14interface MusicalEntity {
15 id: number;
16 title: string;
17 genre: string;
18 type: string;
19 releasedBy: string;
20 isLikedByCurrentUser?: boolean;
21 songs?: Song[];
22}
23
24const MusicalCollection = () => {
25 const { type, id } = useParams();
26 const navigate = useNavigate();
27 const [collection, setCollection] = useState<MusicalEntity | null>(null);
28 const [isLoading, setIsLoading] = useState(true);
29 const [error, setError] = useState<string | null>(null);
30
31 useEffect(() => {
32 const fetchData = async () => {
33 setIsLoading(true);
34 setError(null);
35 try {
36 const endpoint =
37 type === "album" ? `/albums/${id}` : `/playlists/${id}`;
38 const response = await axiosInstance.get(endpoint);
39 console.log(response.data);
40 setCollection(response.data);
41 } catch (err: any) {
42 setError(err.response?.data?.error || "Failed to load collection");
43 } finally {
44 setIsLoading(false);
45 }
46 };
47 fetchData();
48 }, [id, type]);
49
50 if (isLoading) {
51 return <div className="p-6">Loading...</div>;
52 }
53
54 if (error) {
55 return (
56 <div className="p-6 bg-red-50 border border-red-200 text-red-700 rounded-lg">
57 <h2 className="font-bold">Error</h2>
58 <p>{error}</p>
59 </div>
60 );
61 }
62
63 if (!collection) return null;
64
65 return (
66 <div className="container mx-auto p-6">
67 <button
68 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"
70 >
71 ← Back
72 </button>
73
74 <div className="bg-white shadow-lg rounded-lg p-8">
75 <div className="flex items-start gap-6 mb-8">
76 <div className="shrink-0">
77 <div className="w-40 h-40 rounded-lg bg-gradient-to-br from-blue-400 to-purple-500 flex items-center justify-center text-white text-5xl font-bold shadow-lg">
78 {collection.title.charAt(0).toUpperCase()}
79 </div>
80 </div>
81
82 <div className="flex-1">
83 <span className="inline-block px-3 py-1 bg-blue-100 text-blue-800 rounded-full text-sm font-medium mb-2">
84 {collection.type}
85 </span>
86 <h1 className="text-4xl font-bold mb-3">{collection.title}</h1>
87
88 <div className="flex items-center gap-3 text-gray-700 mb-4">
89 <span className="font-semibold">{collection.releasedBy}</span>
90 <span>•</span>
91 <span className="text-gray-600">{collection.genre}</span>
92 {collection.songs && (
93 <>
94 <span>•</span>
95 <span className="text-gray-600">
96 {collection.songs.length} song
97 {collection.songs.length !== 1 ? "s" : ""}
98 </span>
99 </>
100 )}
101 </div>
102
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"
113 >
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>
120 </div>
121 </div>
122
123 <div className="border-t pt-6">
124 <h2 className="text-2xl font-bold mb-4 text-gray-800">Songs</h2>
125
126 {collection.songs && collection.songs.length > 0 ? (
127 <div className="space-y-2">
128 {collection.songs.map((song, index) => (
129 <div
130 key={song.id}
131 className="flex items-center gap-4 p-3 rounded-lg hover:bg-gray-50 transition-colors duration-150"
132 >
133 <span className="text-gray-500 font-medium w-8 text-center">
134 {index + 1}
135 </span>
136
137 <div className="flex-1 min-w-0">
138 <p className="font-semibold text-gray-900 truncate">
139 {song.title}
140 </p>
141 <p className="text-sm text-gray-600 truncate">
142 {song.releasedBy}
143 </p>
144 </div>
145
146 <span className="text-sm text-gray-600 px-3 py-1 bg-gray-100 rounded-full">
147 {song.genre}
148 </span>
149
150 <button
151 className="p-2 hover:bg-gray-100 rounded-full transition-colors duration-200"
152 aria-label={song.isLikedByCurrentUser ? "Unlike" : "Like"}
153 >
154 <svg
155 className="w-5 h-5"
156 fill={song.isLikedByCurrentUser ? "#ef4444" : "none"}
157 stroke={song.isLikedByCurrentUser ? "#ef4444" : "#6b7280"}
158 strokeWidth="2"
159 viewBox="0 0 24 24"
160 >
161 <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" />
162 </svg>
163 </button>
164 </div>
165 ))}
166 </div>
167 ) : (
168 <p className="text-center text-gray-500 py-8">No songs available</p>
169 )}
170 </div>
171 </div>
172 </div>
173 );
174};
175
176export default MusicalCollection;
Note: See TracBrowser for help on using the repository browser.