Changeset 2b08bed for frontend/src/pages
- Timestamp:
- 01/30/26 22:14:36 (5 months ago)
- Branches:
- main
- Children:
- 2730163
- Parents:
- 98992cf
- Location:
- frontend/src/pages
- Files:
-
- 1 added
- 1 edited
-
MusicalCollection.tsx (added)
-
UserDetailView.tsx (modified) (8 diffs)
Legend:
- Unmodified
- Added
- Removed
-
frontend/src/pages/UserDetailView.tsx
r98992cf r2b08bed 4 4 import ArtistView from "../components/userProfile/ArtistView"; 5 5 import ListenerView from "../components/userProfile/ListenerView"; 6 import UserListModal from "../components/userProfile/UserListModal"; 6 7 import type { 7 8 MusicalEntity, 8 9 Playlist, 9 10 ArtistContribution, 11 BaseNonAdminUser, 10 12 } from "../utils/types"; 11 13 12 interface BaseUser { 13 id: number; 14 fullName: string; 15 userType: string; 16 followers: number; 17 following: number; 18 isFollowedByCurrentUser: boolean; 19 } 20 21 interface Artist extends BaseUser { 14 interface Artist extends BaseNonAdminUser { 22 15 userType: "ARTIST"; 23 16 contributions: ArtistContribution[]; 24 17 } 25 interface Listener extends Base User {18 interface Listener extends BaseNonAdminUser { 26 19 userType: "LISTENER"; 27 20 likedEntities: MusicalEntity[]; … … 33 26 const UserDetail = () => { 34 27 // user refers to the selected user NOT to the user from context 28 const baseURL = import.meta.env.VITE_API_BASE_URL; 35 29 const { userId } = useParams(); 36 30 const navigate = useNavigate(); 37 31 const [user, setUser] = useState<UserProfile | null>(null); 38 32 const [error, setError] = useState<string | null>(null); 33 const [showModal, setShowModal] = useState(false); 34 const [modalTitle, setModalTitle] = useState(""); 35 const [modalUsers, setModalUsers] = useState<any[]>([]); 36 const [isLoadingModal, setIsLoadingModal] = useState(false); 39 37 const [isFollowing, setIsFollowing] = useState(false); 40 38 … … 47 45 `/users/follow/${userId}`, 48 46 ); 47 setUser(response.data); 49 48 } catch (err: any) { 50 49 console.error(err.response?.data?.error); 51 50 } finally { 52 51 setIsFollowing(false); 52 } 53 }; 54 55 const displayFollowers = async () => { 56 setIsLoadingModal(true); 57 try { 58 const response = await axiosInstance.get(`/users/followers/${userId}`); 59 setModalUsers(response.data); 60 setModalTitle("Followers"); 61 setShowModal(true); 62 } catch (err) { 63 console.error("Failed to fetch followers"); 64 } finally { 65 setIsLoadingModal(false); 66 } 67 }; 68 const displayFollowing = async () => { 69 setIsLoadingModal(true); 70 try { 71 const response = await axiosInstance.get(`/users/following/${userId}`); 72 setModalUsers(response.data); 73 setModalTitle("Following"); 74 setShowModal(true); 75 } catch (err) { 76 console.error("Failed to fetch following users"); 77 } finally { 78 setIsLoadingModal(false); 79 } 80 }; 81 82 const handleFollowInModal = async (targetId: number) => { 83 try { 84 await axiosInstance.post(`/users/follow/${targetId}`); 85 setModalUsers((prevUsers) => 86 prevUsers.map((u) => { 87 if (u.id === targetId) { 88 const isNowFollowing = !u.isFollowedByCurrentUser; 89 return { 90 ...u, 91 isFollowedByCurrentUser: isNowFollowing, 92 }; 93 } 94 return u; 95 }), 96 ); 97 98 if (user && user.id === targetId) { 99 const response = await axiosInstance.get(`/users/${targetId}`); 100 setUser(response.data); 101 } 102 } catch (err) { 103 console.error("Failed to toggle follow in modal", err); 53 104 } 54 105 }; … … 60 111 const response = await axiosInstance.get(`/users/${userId}`); 61 112 console.log(response.data); 62 63 113 setUser(response.data); 64 114 } catch (err: any) { … … 84 134 return ( 85 135 <div className="container mx-auto p-6"> 136 {isLoadingModal && ( 137 <div className="fixed inset-0 z-40 bg-black/30 backdrop-blur-sm flex items-center justify-center"> 138 <div className="flex items-center gap-3"> 139 <div className="w-6 h-6 border-3 border-blue-500 border-t-transparent rounded-full animate-spin"></div> 140 </div> 141 </div> 142 )} 86 143 <button 87 144 onClick={() => navigate(-1)} … … 94 151 <div className="flex items-start gap-6 mb-8"> 95 152 <div className="shrink-0"> 96 <div className="w-32 h-32 rounded-full bg-linear-to-br from-blue-400 to-purple-500 flex items-center justify-center text-white text-4xl font-bold shadow-lg"> 97 {user.fullName.charAt(0).toUpperCase()} 153 <div className="w-32 h-32 rounded-full bg-linear-to-br from-blue-400 to-purple-500 flex items-center justify-center text-white text-4xl font-bold shadow-lg overflow-hidden"> 154 {user.profilePhoto ? ( 155 <img 156 src={`${baseURL}/${user.profilePhoto}`} 157 alt={user.fullName} 158 className="w-full h-full object-cover" 159 /> 160 ) : ( 161 user.fullName.charAt(0).toUpperCase() 162 )} 98 163 </div> 99 164 </div> … … 106 171 107 172 <div className="flex gap-6 mb-4 text-gray-700"> 108 <div className="flex flex-col"> 173 <div 174 className={`flex flex-col ${user.userType == "LISTENER" ? "cursor-pointer" : "cursor-default"}`} 175 onClick={ 176 user.userType === "LISTENER" ? displayFollowers : undefined 177 } 178 > 109 179 <span className="text-2xl font-bold">{user.followers}</span> 110 180 <span className="text-sm text-gray-500">Followers</span> 111 181 </div> 112 <div className="flex flex-col"> 182 <div 183 className={`flex flex-col ${user.userType == "LISTENER" ? "cursor-pointer" : "cursor-default"}`} 184 onClick={ 185 user.userType === "LISTENER" ? displayFollowing : undefined 186 } 187 > 113 188 <span className="text-2xl font-bold">{user.following}</span> 114 189 <span className="text-sm text-gray-500">Following</span> … … 144 219 /> 145 220 )} 221 222 {showModal && ( 223 <UserListModal 224 title={modalTitle} 225 users={modalUsers} 226 onClose={() => setShowModal(false)} 227 onFollowToggle={handleFollowInModal} 228 /> 229 )} 146 230 </div> 147 231 </div>
Note:
See TracChangeset
for help on using the changeset viewer.
