Changeset 2b08bed for frontend/src/pages


Ignore:
Timestamp:
01/30/26 22:14:36 (5 months ago)
Author:
Dimitar Arsov <dimitararsov04@…>
Branches:
main
Children:
2730163
Parents:
98992cf
Message:

show followers, following and songs in album

Location:
frontend/src/pages
Files:
1 added
1 edited

Legend:

Unmodified
Added
Removed
  • frontend/src/pages/UserDetailView.tsx

    r98992cf r2b08bed  
    44import ArtistView from "../components/userProfile/ArtistView";
    55import ListenerView from "../components/userProfile/ListenerView";
     6import UserListModal from "../components/userProfile/UserListModal";
    67import type {
    78  MusicalEntity,
    89  Playlist,
    910  ArtistContribution,
     11  BaseNonAdminUser,
    1012} from "../utils/types";
    1113
    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 {
     14interface Artist extends BaseNonAdminUser {
    2215  userType: "ARTIST";
    2316  contributions: ArtistContribution[];
    2417}
    25 interface Listener extends BaseUser {
     18interface Listener extends BaseNonAdminUser {
    2619  userType: "LISTENER";
    2720  likedEntities: MusicalEntity[];
     
    3326const UserDetail = () => {
    3427  // user refers to the selected user NOT to the user from context
     28  const baseURL = import.meta.env.VITE_API_BASE_URL;
    3529  const { userId } = useParams();
    3630  const navigate = useNavigate();
    3731  const [user, setUser] = useState<UserProfile | null>(null);
    3832  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);
    3937  const [isFollowing, setIsFollowing] = useState(false);
    4038
     
    4745        `/users/follow/${userId}`,
    4846      );
     47      setUser(response.data);
    4948    } catch (err: any) {
    5049      console.error(err.response?.data?.error);
    5150    } finally {
    5251      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);
    53104    }
    54105  };
     
    60111        const response = await axiosInstance.get(`/users/${userId}`);
    61112        console.log(response.data);
    62 
    63113        setUser(response.data);
    64114      } catch (err: any) {
     
    84134  return (
    85135    <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      )}
    86143      <button
    87144        onClick={() => navigate(-1)}
     
    94151        <div className="flex items-start gap-6 mb-8">
    95152          <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              )}
    98163            </div>
    99164          </div>
     
    106171
    107172            <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              >
    109179                <span className="text-2xl font-bold">{user.followers}</span>
    110180                <span className="text-sm text-gray-500">Followers</span>
    111181              </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              >
    113188                <span className="text-2xl font-bold">{user.following}</span>
    114189                <span className="text-sm text-gray-500">Following</span>
     
    144219          />
    145220        )}
     221
     222        {showModal && (
     223          <UserListModal
     224            title={modalTitle}
     225            users={modalUsers}
     226            onClose={() => setShowModal(false)}
     227            onFollowToggle={handleFollowInModal}
     228          />
     229        )}
    146230      </div>
    147231    </div>
Note: See TracChangeset for help on using the changeset viewer.