Страница профиля

Страница учетной записи
This commit is contained in:
degradin 2025-05-07 13:37:53 +03:00
parent 561a3426e0
commit 89bbc975ce

View File

@ -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 (
<div className="pt-20 flex justify-center items-center h-screen">
<div className="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-campfire-amber"></div>
<div className="pt-20 container-custom py-12">
<div className="animate-pulse">
<div className="h-32 bg-campfire-charcoal rounded-lg mb-6"></div>
<div className="space-y-4">
<div className="h-4 bg-campfire-charcoal rounded w-1/4"></div>
<div className="h-4 bg-campfire-charcoal rounded w-1/2"></div>
</div>
</div>
</div>
);
}
// Calculate average ratings from reviews
const calculateAverageRatings = () => {
if (!userReviews.length) return null;
if (error) {
return (
<div className="pt-20 container-custom py-12">
<div className="bg-status-error bg-opacity-20 text-status-error p-6 rounded-lg">
<h2 className="text-xl font-bold mb-2">Ошибка</h2>
<p>{error}</p>
</div>
</div>
);
}
const totals = {
story: 0,
visuals: 0,
performance: 0,
soundtrack: 0,
enjoyment: 0,
};
if (!profile) {
return (
<div className="pt-20 container-custom py-12">
<div className="bg-status-error bg-opacity-20 text-status-error p-6 rounded-lg">
<h2 className="text-xl font-bold mb-2">Профиль не найден</h2>
<p>Пользователь с таким ID не существует.</p>
</div>
</div>
);
}
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 (
<div className="pt-20">
{/* Profile Header */}
<div className="bg-campfire-charcoal py-8">
<div className="container-custom">
<div className="flex flex-col md:flex-row items-center md:items-start gap-8">
{/* Profile Picture */}
<div className="relative">
<div className="w-32 h-32 rounded-full overflow-hidden bg-campfire-dark">
{userData.profilePicture ? (
<img
src={userData.profilePicture}
alt={userData.username}
className="w-full h-full object-cover"
/>
) : (
<div className="w-full h-full flex items-center justify-center text-campfire-ash">
{userData.username.substring(0, 1).toUpperCase()}
</div>
)}
</div>
<div className="pt-20 container-custom py-12">
<div className="bg-campfire-charcoal rounded-lg overflow-hidden">
{/* Заголовок профиля */}
<div className="relative h-48 bg-gradient-to-r from-campfire-amber to-campfire-ember">
{profile.profile_picture && (
<img
src={profile.profile_picture}
alt={profile.username}
className="absolute -bottom-16 left-8 w-32 h-32 rounded-full border-4 border-campfire-charcoal object-cover"
/>
)}
</div>
{isCurrentUser && (
<button className="absolute bottom-0 right-0 bg-campfire-amber text-campfire-dark p-2 rounded-full">
<FiEdit size={16} />
</button>
)}
</div>
{/* Profile Info */}
<div className="flex-1 text-center md:text-left">
<div className="flex flex-col md:flex-row md:items-center gap-4">
<h1 className="text-3xl font-bold">
{userData.username}
{userData.isCritic && (
<span className="ml-2 inline-block px-2 py-0.5 text-xs font-medium bg-campfire-amber text-campfire-dark rounded-full">
Резидент
</span>
)}
</h1>
{isCurrentUser && (
<div className="flex gap-2">
<Link to="/settings" className="btn-secondary text-sm">
<FiSettings className="mr-1" /> Настройки
</Link>
<button onClick={logout} className="btn-secondary text-sm">
<FiLogOut className="mr-1" /> Выйти
</button>
</div>
)}
</div>
<p className="text-campfire-ash my-4">
{userData.bio ||
`${userData.username} еще не написал ничего о себе.}`}
<div className="pt-20 px-8 pb-8">
<div className="flex justify-between items-start mb-6">
<div>
<h1 className="text-3xl font-bold mb-2">{profile.username}</h1>
<p className="text-campfire-ash">
{profile.is_critic ? "Критик" : "Пользователь"}
</p>
<div className="flex flex-wrap justify-center md:justify-start gap-4 text-sm text-campfire-ash">
<div className="flex items-center">
<FiCalendar className="mr-1" />
<span>Участник с {joinDate}</span>
</div>
<div className="flex items-center">
<FiStar className="mr-1" />
<span>{userData.reviewCount} Рецензий</span>
</div>
</div>
</div>
{isOwnProfile && (
<button
onClick={() => navigate('/settings')}
className="btn-secondary"
>
Редактировать профиль
</button>
)}
</div>
</div>
</div>
{/* Tabs Navigation */}
<div className="bg-campfire-dark">
<div className="container-custom">
<div className="flex overflow-x-auto space-x-8">
<button
onClick={() => setActiveTab("reviews")}
className={`py-4 font-medium ${
activeTab === "reviews"
? "text-campfire-amber border-b-2 border-campfire-amber"
: "text-campfire-ash hover:text-campfire-light"
}`}
>
Рецензии
</button>
<button
onClick={() => setActiveTab("stats")}
className={`py-4 font-medium ${
activeTab === "stats"
? "text-campfire-amber border-b-2 border-campfire-amber"
: "text-campfire-ash hover:text-campfire-light"
}`}
>
Статистика
</button>
<button
onClick={() => setActiveTab("favorites")}
className={`py-4 font-medium ${
activeTab === "favorites"
? "text-campfire-amber border-b-2 border-campfire-amber"
: "text-campfire-ash hover:text-campfire-light"
}`}
>
Избранное
</button>
</div>
</div>
</div>
{profile.bio && (
<div className="mb-8">
<h2 className="text-xl font-bold mb-2">О себе</h2>
<p className="text-campfire-light">{profile.bio}</p>
</div>
)}
{/* Tab Content */}
<div className="container-custom py-8">
{/* Reviews Tab */}
{activeTab === "reviews" && (
{/* Отзывы пользователя */}
<div>
<h2 className="text-2xl font-bold mb-6">
Рецензии{" "}
<span className="text-campfire-ash">({userReviews.length})</span>
</h2>
{userReviews.length > 0 ? (
<div className="space-y-8">
{userReviews.map((review) => (
<div key={review.id} className="mb-6">
<div className="mb-2 flex items-center">
<div className="w-8 h-12 mr-2">
{review.mediaPoster ? (
<img
src={review.mediaPoster}
alt={review.mediaTitle}
className="w-full h-full object-cover rounded"
/>
) : (
<div className="w-full h-full bg-campfire-charcoal rounded"></div>
)}
<h2 className="text-2xl font-bold mb-4">Отзывы</h2>
{reviews.length === 0 ? (
<p className="text-campfire-ash">Пока нет отзывов</p>
) : (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{reviews.map((review) => (
<div key={review.id} className="card p-4">
<div className="flex items-center mb-4">
<img
src={review.media.poster_url}
alt={review.media.title}
className="w-16 h-24 object-cover rounded-lg mr-4"
/>
<div>
<h3 className="font-bold">{review.media.title}</h3>
<p className="text-sm text-campfire-ash">
{review.media.type === 'movie' ? 'Фильм' :
review.media.type === 'series' ? 'Сериал' : 'Игра'}
</p>
</div>
<Link
to={`/media/${review.mediaId}?type=${review.mediaType}`}
className="text-campfire-amber hover:text-campfire-ember"
>
{review.mediaTitle}
</Link>
<span className="mx-2 text-campfire-ash"></span>
<span className="text-campfire-ash">
{review.mediaType === "movie" ? (
<FiFilm className="inline" />
) : (
<FiTv className="inline" />
)}
</span>
</div>
<ReviewCard review={review} isDetailed={true} />
<p className="text-sm mb-4 line-clamp-3">{review.content}</p>
<button
onClick={() => navigate(`/media/${review.media_id}`)}
className="btn-secondary w-full"
>
Читать полностью
</button>
</div>
))}
</div>
) : (
<div className="bg-campfire-charcoal rounded-lg p-8 text-center">
<p className="text-campfire-ash">No reviews yet.</p>
</div>
)}
</div>
)}
{/* Stats Tab */}
{activeTab === "stats" && (
<div>
<h2 className="text-2xl font-bold mb-6">Статистика оценок</h2>
{averageRatings ? (
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
<div className="bg-campfire-charcoal rounded-lg p-6">
<h3 className="text-xl font-bold mb-4 text-center">
Средние оценки
</h3>
<RatingChart ratings={averageRatings} showLegend={true} />
</div>
<div className="bg-campfire-charcoal rounded-lg p-6">
<h3 className="text-xl font-bold mb-4">Разбивка оценок</h3>
<div className="space-y-4">
{Object.entries(averageRatings).map(([category, value]) => (
<div key={category}>
<div className="flex justify-between items-center mb-1">
<span className="capitalize">{category}</span>
<span className="font-medium">
{value.toFixed(1)}/10
</span>
</div>
<div className="w-full h-2 bg-campfire-dark rounded-full">
<div
className="h-full bg-campfire-amber rounded-full"
style={{ width: `${(value / 10) * 100}%` }}
></div>
</div>
</div>
))}
</div>
</div>
<div className="bg-campfire-charcoal rounded-lg p-6 md:col-span-2">
<h3 className="text-xl font-bold mb-4">Активность</h3>
<div className="flex justify-center items-center h-48 text-campfire-ash">
График активности скоро появится...
</div>
</div>
</div>
) : (
<div className="bg-campfire-charcoal rounded-lg p-8 text-center">
<p className="text-campfire-ash">
Статистика недоступна. Пишите рецензии, чтобы увидеть свою
статистику.
</p>
</div>
)}
</div>
)}
{/* Favorites Tab */}
{activeTab === "favorites" && (
<div>
<h2 className="text-2xl font-bold mb-6">Favorites</h2>
<div className="bg-campfire-charcoal rounded-lg p-8 text-center">
<p className="text-campfire-ash">
{isCurrentUser
? "Вы еще не добавили ни одного избранного. Отметьте медиа как избранное, чтобы видеть их здесь."
: `${userData.username} еще не добавил ничего в избранное.`}
</p>
</div>
</div>
)}
</div>
</div>
</div>
);