source: frontend/src/pages/UserDetailView.tsx@ 6de2873

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

implement following a user

  • Property mode set to 100644
File size: 4.6 KB
Line 
1import { useEffect, useState } from "react";
2import { useNavigate, useParams } from "react-router-dom";
3import axiosInstance from "../api/axiosInstance";
4import ArtistView from "../components/userProfile/ArtistView";
5import ListenerView from "../components/userProfile/ListenerView";
6import type {
7 ArtistContributionDTO,
8 MusicalEntityDTO,
9 Playlist,
10} from "../utils/types";
11
12interface User {
13 id: number;
14 username: string;
15 fullName: string;
16 userType: string;
17 followers: number;
18 following: number;
19 isFollowedByCurrentUser: boolean;
20
21 musicalEntities?: {
22 contributions: ArtistContributionDTO[];
23 };
24
25 likes?: {
26 likedEntities: MusicalEntityDTO[];
27 };
28
29 createdPlaylists?: Playlist[];
30}
31
32const UserDetail = () => {
33 // user refers to the selected user NOT to the user from context
34 const { userId } = useParams();
35 const navigate = useNavigate();
36 const [user, setUser] = useState<User | null>(null);
37 const [error, setError] = useState<string | null>(null);
38 const [isFollowing, setIsFollowing] = useState(false);
39
40 const handleFollow = async () => {
41 if (!user) return;
42
43 setIsFollowing(true);
44 try {
45 const response = await axiosInstance.post<User>(
46 `/users/follow/${userId}`,
47 );
48 setUser(response.data);
49 } catch (err: any) {
50 console.error(err.response?.data?.error);
51 } finally {
52 setIsFollowing(false);
53 }
54 };
55
56 useEffect(() => {
57 const fetchUser = async () => {
58 setError(null);
59 try {
60 const response = await axiosInstance.get(`/users/${userId}`);
61
62 setUser(response.data);
63 } catch (err: any) {
64 const errorMessage =
65 err.response?.data?.error || "Failed to fetch user";
66 setError(errorMessage);
67 }
68 };
69 fetchUser();
70 }, [userId]);
71
72 if (error) {
73 return (
74 <div className="p-6 bg-red-50 border border-red-200 text-red-700 rounded-lg">
75 <h2 className="font-bold">Error</h2>
76 <p>{error}</p>
77 </div>
78 );
79 }
80
81 if (!user) return <div className="p-6">Loading...</div>;
82
83 return (
84 <div className="container mx-auto p-6">
85 <button
86 onClick={() => navigate(-1)}
87 className="mb-4 px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600 transition-colors duration-200"
88 >
89 ← Back
90 </button>
91
92 <div className="bg-white shadow-lg rounded-lg p-8">
93 <div className="flex items-start gap-6 mb-8">
94 <div className="shrink-0">
95 <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">
96 {user.fullName.charAt(0).toUpperCase()}
97 </div>
98 </div>
99
100 <div className="flex-1">
101 <h1 className="text-4xl font-bold mb-2">{user.fullName}</h1>
102 <span className="inline-block px-3 py-1 bg-blue-100 text-blue-800 rounded-full text-sm font-medium mb-4">
103 {user.userType}
104 </span>
105
106 <div className="flex gap-6 mb-4 text-gray-700">
107 <div className="flex flex-col">
108 <span className="text-2xl font-bold">{user.followers}</span>
109 <span className="text-sm text-gray-500">Followers</span>
110 </div>
111 <div className="flex flex-col">
112 <span className="text-2xl font-bold">{user.following}</span>
113 <span className="text-sm text-gray-500">Following</span>
114 </div>
115 </div>
116
117 <button
118 onClick={handleFollow}
119 disabled={isFollowing}
120 className={`
121 px-6 py-2 font-semibold rounded-lg shadow-md
122 transition-colors duration-200
123 ${
124 isFollowing
125 ? "bg-gray-400 text-gray-200 cursor-not-allowed"
126 : user.isFollowedByCurrentUser
127 ? "bg-gray-200 text-gray-700 hover:bg-gray-300 cursor-pointer"
128 : "bg-blue-500 text-white hover:bg-blue-600 cursor-pointer"
129 }
130 `}
131 >
132 {user.isFollowedByCurrentUser ? "Unfollow" : "Follow"}
133 </button>
134 </div>
135 </div>
136
137 {user.userType === "ARTIST" && user.musicalEntities?.contributions && (
138 <ArtistView contributions={user.musicalEntities.contributions} />
139 )}
140
141 {user.userType === "LISTENER" && user.likes?.likedEntities && (
142 <ListenerView
143 likedEntities={user.likes.likedEntities}
144 playlists={user.createdPlaylists}
145 />
146 )}
147 </div>
148 </div>
149 );
150};
151
152export default UserDetail;
Note: See TracBrowser for help on using the repository browser.