Changeset 0792d02 for frontend/src/components
- Timestamp:
- 02/05/26 10:29:09 (5 months ago)
- Branches:
- main
- Children:
- 3de009b
- Parents:
- d2af1a6
- Location:
- frontend/src/components/userProfile
- Files:
-
- 2 edited
-
ListenerView.tsx (modified) (7 diffs)
-
UserListModal.tsx (modified) (4 diffs)
Legend:
- Unmodified
- Added
- Removed
-
frontend/src/components/userProfile/ListenerView.tsx
rd2af1a6 r0792d02 4 4 import axiosInstance from "../../api/axiosInstance"; 5 5 import { useState } from "react"; 6 6 7 interface ListenerViewProps { 7 8 likedEntities: MusicalEntity[] | []; 8 playlists: Playlist[] | []; 9 createdPlaylists: Playlist[] | []; 10 savedPlaylists: Playlist[] | []; 9 11 } 10 12 11 const ListenerView = ({ likedEntities, playlists }: ListenerViewProps) => { 13 const ListenerView = ({ 14 likedEntities, 15 createdPlaylists, 16 savedPlaylists, 17 }: ListenerViewProps) => { 12 18 const navigate = useNavigate(); 13 19 const [items, setItems] = useState(likedEntities); 20 const [savedItems, setSavedItems] = useState(savedPlaylists); 14 21 const [toast, setToast] = useState<{ message: string; show: boolean }>({ 15 22 message: "", … … 27 34 const likedAlbums = items.filter((e) => e.type === "ALBUM"); 28 35 29 const handleSavePlaylist = (e: React.MouseEvent, playlistId: number) => { 36 const handleSavePlaylist = async ( 37 e: React.MouseEvent, 38 playlistId: number, 39 playlistName: string, 40 ) => { 30 41 e.stopPropagation(); 31 42 32 console.log("Save playlist:", playlistId); 43 try { 44 const response = await axiosInstance.post(`/playlist/${playlistId}/save`); 45 const data = response.data; 46 47 setSavedItems((prevItems) => 48 data.isSaved 49 ? [...prevItems, prevItems.find((p) => p.id === playlistId)!] 50 : prevItems.filter((p) => p.id !== playlistId), 51 ); 52 53 showToast( 54 data.isSaved 55 ? `Saved "${playlistName}"` 56 : `Removed "${playlistName}" from saved playlists`, 57 ); 58 } catch (err: any) { 59 showToast(err.response?.data?.error || "Failed to save playlist"); 60 } 33 61 }; 34 62 … … 63 91 </div> 64 92 )} 65 {playlists && playlists.length > 0 && ( 93 94 {createdPlaylists && createdPlaylists.length > 0 && ( 66 95 <section> 67 96 <div className="flex items-center gap-3 mb-6"> … … 70 99 Created Playlists 71 100 </h3> 72 <span className="text-sm text-gray-500">({playlists.length})</span> 101 <span className="text-sm text-gray-500"> 102 ({createdPlaylists.length}) 103 </span> 73 104 </div> 74 105 75 106 <div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-6"> 76 { playlists.map((playlist) => (107 {createdPlaylists.map((playlist) => ( 77 108 <div 78 109 key={playlist.id} … … 93 124 )} 94 125 95 {/* Save button overlay */}96 126 <button 97 onClick={(e) => handleSavePlaylist(e, playlist.id)} 127 onClick={(e) => 128 handleSavePlaylist(e, playlist.id, playlist.name) 129 } 98 130 className="absolute top-2 right-2 p-2 bg-white/90 hover:bg-white rounded-full shadow-md transition-all duration-200 opacity-0 group-hover:opacity-100" 99 131 title={playlist.isSavedByCurrentUser ? "Unsave" : "Save"} … … 106 138 }`} 107 139 /> 140 </button> 141 </div> 142 <p className="text-sm font-semibold text-gray-900 truncate group-hover:text-blue-600 transition-colors"> 143 {playlist.name} 144 </p> 145 <p className="text-xs text-gray-500 truncate"> 146 {playlist.creatorName} 147 </p> 148 </div> 149 ))} 150 </div> 151 </section> 152 )} 153 154 {savedItems && savedItems.length > 0 && ( 155 <section> 156 <div className="flex items-center gap-3 mb-6"> 157 <Bookmark className="w-6 h-6 text-blue-600 fill-blue-600" /> 158 <h3 className="text-2xl font-bold text-gray-800"> 159 Saved Playlists 160 </h3> 161 <span className="text-sm text-gray-500">({savedItems.length})</span> 162 </div> 163 164 <div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-6"> 165 {savedItems.map((playlist) => ( 166 <div 167 key={playlist.id} 168 className="group cursor-pointer" 169 onClick={() => navigate(`/collection/playlist/${playlist.id}`)} 170 > 171 <div className="relative aspect-square rounded-lg overflow-hidden bg-gray-100 mb-3 shadow-md group-hover:shadow-lg transition-all"> 172 {playlist.cover ? ( 173 <img 174 src={playlist.cover} 175 alt={playlist.name} 176 className="w-full h-full object-cover" 177 /> 178 ) : ( 179 <div className="w-full h-full bg-gradient-to-br from-blue-400 to-purple-500 flex items-center justify-center"> 180 <ListMusic className="w-16 h-16 text-white opacity-30" /> 181 </div> 182 )} 183 184 <button 185 onClick={(e) => 186 handleSavePlaylist(e, playlist.id, playlist.name) 187 } 188 className="absolute top-2 right-2 p-2 bg-white/90 hover:bg-white rounded-full shadow-md transition-all duration-200 opacity-0 group-hover:opacity-100" 189 title="Unsave" 190 > 191 <Bookmark className="w-5 h-5 fill-blue-600 text-blue-600" /> 108 192 </button> 109 193 </div> … … 234 318 {likedEntities && 235 319 likedEntities.length === 0 && 236 (!playlists || playlists.length === 0) && ( 320 (!createdPlaylists || createdPlaylists.length === 0) && 321 (!savedItems || savedItems.length === 0) && ( 237 322 <div className="flex flex-col items-center justify-center py-16 text-gray-400"> 238 323 <Music className="w-20 h-20 mb-4 opacity-20" /> -
frontend/src/components/userProfile/UserListModal.tsx
rd2af1a6 r0792d02 6 6 users: BaseNonAdminUser[]; 7 7 onClose: () => void; 8 onFollowToggle: (target Id: number) => Promise<void>;8 onFollowToggle: (targetUsername: string) => Promise<void>; 9 9 } 10 10 … … 37 37 users.map((u) => ( 38 38 <div 39 key={u. id}39 key={u.username} 40 40 className="flex items-center justify-between p-3 hover:bg-gray-50 rounded-lg transition-colors" 41 41 > … … 44 44 onClick={() => { 45 45 onClose(); 46 navigate(`/users/${u. id}`);46 navigate(`/users/${u.username}`); 47 47 }} 48 48 > … … 64 64 65 65 <button 66 onClick={() => onFollowToggle(u. id)}66 onClick={() => onFollowToggle(u.username)} 67 67 className={`px-4 py-1 text-sm font-medium rounded-md transition-colors cursor-pointer ${ 68 68 u.isFollowedByCurrentUser
Note:
See TracChangeset
for help on using the changeset viewer.
