From 89bbc975ce67d9b71af33ef25d75206cf8f120b4 Mon Sep 17 00:00:00 2001 From: degradin Date: Wed, 7 May 2025 13:37:53 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A1=D1=82=D1=80=D0=B0=D0=BD=D0=B8=D1=86?= =?UTF-8?q?=D0=B0=20=D0=BF=D1=80=D0=BE=D1=84=D0=B8=D0=BB=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Страница учетной записи --- src/pages/ProfilePage.jsx | 465 ++++++++++---------------------------- 1 file changed, 124 insertions(+), 341 deletions(-) diff --git a/src/pages/ProfilePage.jsx b/src/pages/ProfilePage.jsx index bc50331..0a7ce16 100644 --- a/src/pages/ProfilePage.jsx +++ b/src/pages/ProfilePage.jsx @@ -1,6 +1,7 @@ import { useState, useEffect } from "react"; -import { useParams, Link } from "react-router-dom"; +import { useParams, useNavigate } from "react-router-dom"; import { useAuth } from "../contexts/AuthContext"; +import { getUserProfile, getUserReviews } from "../services/supabase"; import { FiEdit, FiSettings, @@ -14,374 +15,156 @@ import ReviewCard from "../components/reviews/ReviewCard"; import RatingChart from "../components/reviews/RatingChart"; function ProfilePage() { - const { id } = useParams(); - const { currentUser, userProfile, logout } = useAuth(); + const { userId } = useParams(); + const navigate = useNavigate(); + const { currentUser, userProfile } = useAuth(); + const [profile, setProfile] = useState(null); + const [reviews, setReviews] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); - const [isCurrentUser, setIsCurrentUser] = useState(false); - const [userData, setUserData] = useState(null); - const [activeTab, setActiveTab] = useState("reviews"); - const [userReviews, setUserReviews] = useState([]); - - // Check if this is the current user's profile useEffect(() => { - if (currentUser && id === currentUser.uid) { - setIsCurrentUser(true); - setUserData(userProfile); - } else { - setIsCurrentUser(false); - // In a real app, fetch the user data for the profile being viewed - // For now, we'll use mock data - setUserData({ - username: "Degradin", - email: "degradin@campfiregg.ru", - bio: "CEO of CampFireGG.", - profilePicture: - "https://staff.campfiregg.ru/assets/avatars/gdVPZMyCy9StMDMy.gif", - createdAt: "2025-01-15T00:00:00.000Z", - reviewCount: 28, - isCritic: true, - }); - } - }, [currentUser, id, userProfile]); + const loadProfile = async () => { + try { + setLoading(true); + setError(null); + + // Если userId не указан, показываем профиль текущего пользователя + const targetUserId = userId || currentUser?.id; + if (!targetUserId) { + navigate('/login'); + return; + } - // Mock loading reviews - useEffect(() => { - // In a real app, fetch user reviews from the database - // For now, we'll use mock data - const mockReviews = [ - { - id: "1", - user: { - id: id, - username: userData?.username || "User", - profilePicture: userData?.profilePicture, - isCritic: userData?.isCritic || false, - }, - mediaId: "123", - mediaType: "movie", - mediaTitle: "The Last of Us", - mediaPoster: - "https://kinopoisk-ru.clstorage.net/1L67Lp105/810449redVB/ZukoaopfTiUSOnVYJJxkXkBCJLdbT63V4ifXLUdXiOvBUJT00q4PPtjWH9FFjkFNvMIv_JU9XtLv_pkyuD8UF7Osabtd2tgwV_qmziZcZBlH1_OkCM6_RCVxE3vRx4TmTdoPOVoixLZBSc4BVEkVmqkSP-pDL4lBKztFqCiuHg_kIK4mS4GJ5xwqOBon0e1e1gilv4NDYd31K1FnDBKGrwXfBPPQrMTxiUnJxRVQ35uk2bHhSKOA2SkyA6c1qldGKiFr5UNOhmBbq7FZZhsh21mK4f_MBidRt2GTY4lYAeADGg__U2RIfA5HT06X0JMZZ9E4qUsuAxrjvUNl9W8XQWfsoaTOQsfvVig_0mQXppyZxeB5hIsp1fyunOVB2kGkiJtCdVygC3kBhMdEFJ7flCgTN62K5UCR7vbBLzauFAArrmnvA4pGr1NtuN3j0CbanUzttwTHrdwzLVQqzZ8CZ4VWx_MZp8z1QoQATtVfWhQn2P9pQ29JE2Q3Bq92rpjK7-Eh6k1NTmWca3edbdQuWV9DaLfNz6Gbsukd70GewuJGEU333-IDeocHSIARUxFd4dizKopngh5rfMChdGiZwmempO9MActs1yp_UaeV5FIWAS76joOh1HPo3m5G1wjiBlzK8BUshzvPS05HlJpa0eOQeSoK64EbpfgAYvBnV8Ev4Oquxs4O6JSkPVPlUS8YmcNv9MPHJ5a4JpAkTVqKaIDQhrDRqov8QAeGDdAYlNnrWvhgQioC3mG3gu96IVtOpuVipcrODeGSrfhcr9mhHdEIIH5IyCWU9aOQY4URy62ImQ021-bCdE-CDohcUZTVYZP3LYwuxp-puAYq--FQjyokoONCAkvg3ak3maWcrN1ej-F5QYwuFHtl0KRPn8JnABuM-NssDr0JiYHJmJ8bkuhc-yiAoU9R6bBHYXaq1wnk76UsxIKIIh1gupDm0WVYlkymfA3LJxx07VgvytUOKksYzbvS7Mu6AQdFj1pYFh3oETglROdNnC53weF97BhMou2rbc9HCO-Tbb6T4pylkV1NoX5Nx2EdOySUIkiTBuqOmo38EuyMPEtCCsASV92Vq5v4L81mCBenc0bq-2ZcAm3kauyHQkZkVesw0KPeKxSTQGEyxcfgUrihFCUBG0Ogzl9FNBxrjPWDz0AO0BaTESYUNaHBIIvW7zuB4nnmXcjiYaGvTw7Bb1zvd5nun-ReWw3v84OHJ5gxq52lDthC6cAfibAQJ4e7h4bIBBEYUpoh0j6lymuLESq_xCV6LFGO5CKtrQbICGWc6XAbrh_mkNOAI3wOi-9Zf-EdrQDdj-rM3of6FWUAd08AT42ZENXQb9A4LAchz5fksAPju2ifjiNhpKKPSoCmkGew0qKbIVIai2vyAMxuWTDl0OYNVcvngJqO8h1kBbcAhEZAk1AeWetYOuAFJcoQY3yI6vxmWEXqp2Orz4iGqRwg_xrtFiCSmwDvt0yLJ13_LFnjhBFL6Ezfgr5XbY-1QAAKAJjbnN2u2j1sCi1N22t1gi00KBSI6K_r54zFgCRTqzEdpNBvnBlFoTlLgmwZdO8cJY8Xz2OMEUp-UK3KvE-Jx0YUV90c4hrzbMVlD9_u9AtveGGXD62s62eAAovqlq0w3GoYrpJQhCezhYym0r9pEOIFGIrqhFMHdVMtRbHHAcGI3xveXqtVPi_K5sbSIbOBJPgrWQ3s5-5qAIEG7hJsfZTuECdWGork-QqLKVOxqVsrwJ9FpEXXBj_dqET_yQnHghPTndIs0vKjDGoP3KE9zy90YZ_OrK8pKk0Njy-VbPCWLBtt21mPLrHBBO2QfaTWqwTbT2rIW4s1F20DdsvOxk_VWBQTIxC3owfjxt_lO0_psehYwWUkoedCCMZkVq-7lukVINZbDWA4i88iGn8g3mhNVcDtiJPNtFwrCzGAgwZN2NeSkKObOeoKbwqfJjUDLzokEY9jIWFtDwcI4pqgehmq2OMZnglvdMwNrpl_KFxjhBqCIgxew3rYYAwzwAmHzFIaV5QnG7HjjapJ1uS3iyLzYxXAp6UpbwZGjeGfYrle7RHhUpMHrrkEBaadMuQQJALfwaJAXkzzl63DuspBi0xbERecK5j2pAMhQt5j9YdtsaeWxCgmLWuHSUmhWqD1WaOZ4ViTQmY6BY1q2vtgnmUMWYZigNfG8JekRbkOC0JKk5zZWuHVcyMPI8cW7_cKInAkFsmmZGzti05L6B6icVAsmWEQGwtgeQxNodNxIBfmTNdBZUhYBb4bIkt9zghOhtoblBKjGvOjzaOKUe_7z6O-px8G7-RuLsAAh2md4XpVoZek3FkHrTcGgOaReuxZp83YQWCEF8I822uE-8eEyIiYmVEaL1-6aMmpxxmkMoBvNWNcj-egpWCEh8CpkOQ52SpdrZZSCuOyjAuvkvXsFOwNGoOshpvJPFluA75GAEMAX59cnanc_eQEK4HeoncJ5LDuHgXjZ6WnBMxLbJBhMljpl25U30VnOcLLr1p9LhjsydgPbc7XDnOdpI-7CQvCjZzY2thuUPrlwiKD0a06Cu9yLFaPLGTr5EiFxOFYonuUqR9hGV5Ka3TER2GeuSDWLQnVwWjBEQxzkahLt06Pw4VUURebKBf7K4QqyZus-0RsNibfCeAuIirCBggpWiu-HCvR4RxVSGC3hoBp2_ghmCKC2YGqhlBH9l3uwvbOzgbFkBgbGy9Sfa3JJ4kZJLeHqvHrE4lrbi3vCwiP7VeruBKhVSaeFo0ldk2A79ox59lnjRHHYs_aib_a78j7gkwOhR0Z1B1q0bikgCHBGSP1QiK7aBTHaOxsqAVMgS4VrLETItGsHJWKZz7MgiGbN-XRrcTVh6vN1kq_GmtEPIuIRkQbEZ_dYFSyIkctzhFhtUzt_aHUAudlparHD0vtH2o6FuTTJ93QSyi-ScytW_KtUOTJ2gesBtvCfdqrA7cJQY-B1Jzd0mjYfukN6sAaa0", - content: "Клубничка имба.", - ratings: { - story: 9, - visuals: 10, - performance: 8, - soundtrack: 10, - enjoyment: 9, - }, - likes: 42, - comments: [], - createdAt: "2025-05-20T14:30:00.000Z", - spoiler: false, - }, - { - id: "2", - user: { - id: id, - username: userData?.username || "User", - profilePicture: userData?.profilePicture, - isCritic: userData?.isCritic || false, - }, - mediaId: "456", - mediaType: "tv", - mediaTitle: "Breaking Bad", - mediaPoster: - "https://kinopoisk-ru.clstorage.net/1L67Lp105/810449redVB/ZukoaopfTiUSOnVYJJxkXkBCJLdbT63V4ifXLUdXiOvBUJT00q4PPtjWHZCFj0FO-VB7fVQ-X8cuLV6nbKsGUeY6fb1ODBjxRakyjfFJ8dnFgPEm3t24ROVjAHuQnEThzdrIfV1kBzXAh44BQhhVG2lV-CvFqZgWKvHFt_yhHgZqKiBrA8hObtUi8VirlSOdmsHoOYWDIZS_IRymRZ4NqcjRAvfTZED9wIBKQFvSVBai2_MgSaAAFCO3yqr46FfIa-_jY8zHySlUq7IZYVmmEhiFqHbAxa3W9eMYIw8ZQq8N2g50G-jNes4GwcLbntbVqlK7I8Qjw96s9whqPahQhaYlou7AhkYk1yU5G23e4dJXhC4-TcspnTrsnuuNWQYkjJeEe9-ryHUNjstBldJbmmhdv-IPYEjT53VCqfyrEcnk7ekjCAcGIZKiNpyqXqfW3ongvgFPIFK5JFFnyBrFKUESAj7YbEQ2DUnBgFRX25WjlXsrhWnKWSaywun8J9sNq6Xsbc8PzO6UbXYSrlYkFNEM4HcJQ2fTfW2U68naQOoGmAb4naPL844BD0RYHJJTo9k44o2ix1ZtdMzrOibThewg6WyDisWl1WW62O-cL12TSq00RUMoEnyuFeyAEECqSx8BMlMnirvCB8jBkVceUesTOGIBocKY6bQPbzXo0M2qbqjmTE9B5lsnuJorV2XSUQujeQHFatmy4F_nDZLKLMdTQfbVK000js-ChROZFlkhknIqwCIHXKY2guVz5F9Er-aj6kxMSOBS4rfQo9lgmppEKfIOx2iUN-AYL0bTDySO2MfwmWLFOwtHBwlamRrT6J0_5MTmRpSpskdks--eyG4k5WKACkmvVi1y2ubUoBUdhS77SszoGvGg3-XI2sWsDFkJ_FOuDH0AyEbNlBZdWieROuhLb4jUofMM6fhoGAalISrkAMmLb5-ge5mrHSXd3sIrtw3F4BV5KFRgghaI5w8YSvWTawt1jgwAD1Mb0lpqUjcsDW1FE2Z1jOK-p11Apy9r78VJRuGSpHid5t3sFt_FKfcBg-FasORZ4sQeSCKNWA01kyyNP8tDT0dc0FoZ4JI9pYwozppi8odkNOMTj67u6qfAB80nFCo73KRTadjSBWV_AQKu2TVkXqtMWwJiSJDFOxQsRPdGCYGFHZ7elG9fs6nKqw_SKjWP5rTi2QArJalvjIDGLhBtcpHlnCMQn4ukOUHFZFA_7d6kj1pLbAhcQTdYJwrzD4GLRBNfVZOpVLeigavIF65wSOV-7p8GaCmrJkpJzO4WavDRZZ7tmBJHK7RNDaUedW3WqoqXSGDJUgsyGqDGO4kGAswb2B_dq1I-b8vvTtmht44kOiCfwSsgpK_IwQ_imKo53eFZL1EZD6W6CoylUXGgnacC00UsjVsDOhulBnQND8_GWxOWWSNQ8m3P6slebTyHYzTnVAjt563vCscAbt_l8ZJsWO_Qkovg9k3FoZ64KZguRlNK4MhXT3ASLwQ0iUOPzdCREhyhV35ix20CVmQ2QKt6q5kK5W_hrEfBjSFUK_bbqhfhUtfFbvFEjuUVe2xeJUDXwSAGn4930moNOwCOyUFc0NNQYZl-rY8vBtPlvwLnMygeT-ZvYaCAykPkUio3FWLW7xsWQ-Q_SkQu3v1gma9PkkgoRNKEdFLlALOIiAeKENORGS5UPaIM5gscojVJZ3nmHA6tamwgA0dHYJNnf5FqXytRGICusE3Lr9A9K1BqyF0G6cDTzvrX5E69gI4NRtiQHZ6pmLFkgC8FnCx7QuszIN9O5a0sbY_OhueT6n1TYRWmEh1K5nvCD2wcMKbQroxXyGRMXsQwEqPHv0ePwIBTGdyRa92xbwnmBtgq-4QuuufQh24l4WKKh80kUKF9lm9YqxCfBG8xCcDmHrSuE-cC2E8khBhFe1SrgPQKT8KN3J9fEeBT-GKFKkYbJLdCpXaunoFr5WsvhUlL6F9g8tWim2TVmwsjdstMZR68LBguTZqAoEkWi_8frIK0gM5DBxFaW5Vg2_HlQGkP2aY_T2wxqtFF761pJsTMSO2do7WSa5kv2JXK7r7DRGFTcGBfqIjZAOxJmQKw0mMLvsjCww4aGlOZ45y2a8tiB17kMwAu9SnVymypbafLCAgoX--y3OOZJdjQAm2_S4gmmvTuHqYOnsAswBMBsNvlCHqCC8XGl9SVU64ZMWfJ58_S5r5P73ap2EQu6OurzApBbF1ru1PjGW1QmQQutotDLxC0Z53mgFnH5E_QTzxd68y6gQcJjxCZ3RFhmbGlSaqI0up7ziH1oBcNruoo4ILGwO8eYL7e7dyhEpXJYLxGBG0beCncZ49ZwigAF838FCRKsw2BB82SXNWdJNB6oUPnwJkjNAKqMeOeBeohZqQFgQDiGyMyVSfV6xmYh-U2zU1ulHhkl6dNmw4qjBzNfhGjDzKJCo8KlFFSG6eX9mzBoQefZr2JL7yhFAEtIaEkTgrF4p4os5btFimU1wNueA1Nphy6aJdjjxfPYsDbgrrbLwp9gosCydPXF9wrkPeqyKMIkCu-gu1-6Z7OLm_iaAeFSCpdYX_WZRlkFdgPI36Bg2LYtKZWo4LZym0G2YK21-sGOgaKCgFaGlSabJE57MDpQpHq8AGpdGAYAmSmLOKESYAo1KT3VKuZYR7aBOA8RosnmbXoWSiOmQgqR5IHepFiR7pHT0rFExbUnSkXv6HNqcAZpjPHbrmsmIkkqekris5EJVSi-d4vXuNdH0Eh90YNJlBzqRwnRt_AY81cTv2QaEr2xUcKSBLZ0tiq0rboy-HAHuT2TyQ6q9aKpuiuJc7Ah2dTq_hdq9Rh3hgDaXZEw2dWcaHWboKfCWHBn049FOSN_wEPy04akhLSL9gwL8fuyFyk-IBi82sTBS8hrOeNCkRtlSD9m6lfoJvZTOnzCk-nkzkgn2ONHw6qzBeM_dSjBn3Ixg6Bl9Ad2qMU-2UA4MNWY8", - content: - "Одно из величайших телевизионных шоу, когда-либо созданных. Развитие персонажа Уолтера Уайта не имеет себе равных, показывая его превращение из мягкосердечного учителя химии в безжалостного наркобарона. Брайан Крэнстон демонстрирует потрясающую игру, которую поддерживает не менее впечатляющий актерский состав. Сценарий неизменно превосходен, с плотным сюжетом и значимыми дугами персонажей.", - ratings: { - story: 10, - visuals: 8, - performance: 10, - soundtrack: 7, - enjoyment: 10, - }, - likes: 87, - comments: [{ id: "c1", user: "TVFan", content: "Completely agree!" }], - createdAt: "2025-06-12T09:15:00.000Z", - spoiler: true, - }, - ]; + const profileData = await getUserProfile(targetUserId); + setProfile(profileData); - setUserReviews(mockReviews); - }, [id, userData]); + const reviewsData = await getUserReviews(targetUserId); + setReviews(reviewsData); + } catch (err) { + setError("Ошибка при загрузке профиля"); + console.error("Error loading profile:", err); + } finally { + setLoading(false); + } + }; - if (!userData) { + loadProfile(); + }, [userId, currentUser, navigate]); + + if (loading) { return ( -
-
+
+
+
+
+
+
+
+
); } - // Calculate average ratings from reviews - const calculateAverageRatings = () => { - if (!userReviews.length) return null; + if (error) { + return ( +
+
+

Ошибка

+

{error}

+
+
+ ); + } - const totals = { - story: 0, - visuals: 0, - performance: 0, - soundtrack: 0, - enjoyment: 0, - }; + if (!profile) { + return ( +
+
+

Профиль не найден

+

Пользователь с таким ID не существует.

+
+
+ ); + } - userReviews.forEach((review) => { - Object.keys(totals).forEach((key) => { - totals[key] += review.ratings[key]; - }); - }); - - const averages = {}; - Object.keys(totals).forEach((key) => { - averages[key] = totals[key] / userReviews.length; - }); - - return averages; - }; - - const averageRatings = calculateAverageRatings(); - - // Format join date - const joinDate = new Date(userData.createdAt).toLocaleDateString("ru-RU", { - year: "numeric", - month: "long", - day: "numeric", - }); + const isOwnProfile = currentUser?.id === profile.id; return ( -
- {/* Profile Header */} -
-
-
- {/* Profile Picture */} -
-
- {userData.profilePicture ? ( - {userData.username} - ) : ( -
- {userData.username.substring(0, 1).toUpperCase()} -
- )} -
+
+
+ {/* Заголовок профиля */} +
+ {profile.profile_picture && ( + {profile.username} + )} +
- {isCurrentUser && ( - - )} -
- - {/* Profile Info */} -
-
-

- {userData.username} - {userData.isCritic && ( - - Резидент - - )} -

- - {isCurrentUser && ( -
- - Настройки - - -
- )} -
- -

- {userData.bio || - `${userData.username} еще не написал ничего о себе.}`} +

+
+
+

{profile.username}

+

+ {profile.is_critic ? "Критик" : "Пользователь"}

- -
-
- - Участник с {joinDate} -
-
- - {userData.reviewCount} Рецензий -
-
+ {isOwnProfile && ( + + )}
-
-
- {/* Tabs Navigation */} -
-
-
- - - -
-
-
+ {profile.bio && ( +
+

О себе

+

{profile.bio}

+
+ )} - {/* Tab Content */} -
- {/* Reviews Tab */} - {activeTab === "reviews" && ( + {/* Отзывы пользователя */}
-

- Рецензии{" "} - ({userReviews.length}) -

- - {userReviews.length > 0 ? ( -
- {userReviews.map((review) => ( -
-
-
- {review.mediaPoster ? ( - {review.mediaTitle} - ) : ( -
- )} +

Отзывы

+ {reviews.length === 0 ? ( +

Пока нет отзывов

+ ) : ( +
+ {reviews.map((review) => ( +
+
+ {review.media.title} +
+

{review.media.title}

+

+ {review.media.type === 'movie' ? 'Фильм' : + review.media.type === 'series' ? 'Сериал' : 'Игра'} +

- - {review.mediaTitle} - - - - {review.mediaType === "movie" ? ( - - ) : ( - - )} -
- +

{review.content}

+
))}
- ) : ( -
-

No reviews yet.

-
)}
- )} - - {/* Stats Tab */} - {activeTab === "stats" && ( -
-

Статистика оценок

- - {averageRatings ? ( -
-
-

- Средние оценки -

- -
- -
-

Разбивка оценок

-
- {Object.entries(averageRatings).map(([category, value]) => ( -
-
- {category} - - {value.toFixed(1)}/10 - -
-
-
-
-
- ))} -
-
- -
-

Активность

-
- График активности скоро появится... -
-
-
- ) : ( -
-

- Статистика недоступна. Пишите рецензии, чтобы увидеть свою - статистику. -

-
- )} -
- )} - - {/* Favorites Tab */} - {activeTab === "favorites" && ( -
-

Favorites

- -
-

- {isCurrentUser - ? "Вы еще не добавили ни одного избранного. Отметьте медиа как избранное, чтобы видеть их здесь." - : `${userData.username} еще не добавил ничего в избранное.`} -

-
-
- )} +
);