feat: добавление Добавление функции смены аватарки

Добавлено окно с аватаркой по нажатию на которое добавлена функция загрузки нового аватара и исправлены некоторые недочёты.
This commit is contained in:
genzof
2026-02-22 21:58:18 +03:00
parent a2ce9043e7
commit 8da52af390
2 changed files with 185 additions and 4 deletions

View File

@@ -22,15 +22,17 @@ const Profile = () => {
const [showDescriptionModal, setShowDescriptionModal] = useState(false); const [showDescriptionModal, setShowDescriptionModal] = useState(false);
const [profileDescription, setProfileDescription] = useState(''); const [profileDescription, setProfileDescription] = useState('');
const [descriptionError, setDescriptionError] = useState(''); const [descriptionError, setDescriptionError] = useState('');
const [avatarUrl, setAvatarUrl] = useState(null);
const [isUploading, setIsUploading] = useState(false);
useEffect(() => { useEffect(() => {
const checkSession = async () => { const checkSession = async () => {
try { try {
const response = await axios.get('/api/users/me'); const response = await axios.get('/api/users/me');
setUser(response.data.display_name); setUser(response.data.display_name);
setUserName(response.data.username) setUserName(response.data.username);
setAvatarUrl(response.data.avatar_url || null);
} catch (err) { } catch (err) {
// Если нет сессии - редирект на логин
setError('Вы не авторизованы'); setError('Вы не авторизованы');
setTimeout(() => { setTimeout(() => {
navigate('/login'); navigate('/login');
@@ -49,6 +51,38 @@ const Profile = () => {
setError('Ошибка'); setError('Ошибка');
} }
}; };
const uploadAvatar = async (event) => {
const file = event.target.files[0];
if (!file) return;
setIsUploading(true);
setError('');
try {
const formData = new FormData();
formData.append('new_avatar', file);
await axios.put(
'/api/users/change_avatar',
formData,
{
withCredentials: true,
headers: {
'Content-Type': 'multipart/form-data'
}
}
);
window.location.reload();
} catch (err) {
setError(err.response?.data?.detail || 'Ошибка загрузки аватара');
} finally {
setIsUploading(false);
}
};
const NewDisplayName = async (e) => { const NewDisplayName = async (e) => {
e.preventDefault(); e.preventDefault();
setError(''); setError('');
@@ -213,6 +247,38 @@ const Profile = () => {
} }
{user && ( {user && (
<div className="user-info"> <div className="user-info">
<div className="avatar-wrapper">
<label className="avatar-upload-label">
<input
type="file"
accept="image/*"
onChange={uploadAvatar}
disabled={isUploading}
className="avatar-file-input"
/>
<div className="avatar-container">
{avatarUrl ? (
<img
src={avatarUrl}
alt="Аватар пользователя"
className="avatar-image"
/>
) : (
<div className="avatar-placeholder">
{user?.charAt(0).toUpperCase()}
</div>
)}
{/*Overlay с плюсом (появляется при наведении)*/}
<div className="avatar-overlay">
<div className="plus-icon">+</div>
</div>
</div>
</label>
{isUploading && <div className="uploading-spinner">Загружается...</div>}
</div>
<p><strong>Привет, {user}! Добро пожаловать в личный кабинет.</strong></p> <p><strong>Привет, {user}! Добро пожаловать в личный кабинет.</strong></p>
<div className="text-block">Здесь ты сможешь управлять данными своей учётной записи.</div> <div className="text-block">Здесь ты сможешь управлять данными своей учётной записи.</div>
<p>Информация об учётной записи</p> <p>Информация об учётной записи</p>

View File

@@ -235,7 +235,7 @@ button {
.frame { .frame {
background-color: #33404d; background-color: #33404d;
margin: 10px 0px; margin: 10px 0px;
width: 30%; width: 344px;
padding: 6px; padding: 6px;
border-radius: 6px; border-radius: 6px;
font-size: 16px; font-size: 16px;
@@ -314,7 +314,8 @@ button:disabled {
.buttonName button{ .buttonName button{
background-color: #007bff; background-color: #007bff;
margin: 10px 5px; margin: 10px 5px;
width: 5%; width: 50px;
min-width: 50px;
padding: 6px; padding: 6px;
border-radius: 6px; border-radius: 6px;
font-size: 20px; font-size: 20px;
@@ -362,4 +363,118 @@ h3 {
.margin-top-large { .margin-top-large {
margin-top: 10em; margin-top: 10em;
}
.avatar-wrapper {
position: relative;
display: inline-block;
float: right;
margin: 30px
}
.avatar-upload-label {
display: block;
cursor: pointer;
}
.avatar-file-input {
position: absolute;
width: 100%;
height: 100%;
opacity: 0;
z-index: 2;
cursor: pointer;
}
.avatar-container {
position: relative;
width: 200px;
height: 200px;
border-radius: 50%;
overflow: hidden;
background-color: #e0e0e0;
transition: all 0.3s ease;
z-index: 1;
}
.avatar-image {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.3s ease;
}
.avatar-placeholder {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
font-size: 40px;
color: #323332;
text-transform: uppercase;
transition: background-color 0.3s ease;
}
/*Overlay — серый фон с плюсом*/
.avatar-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(200, 200, 200, 0.8); /* Светло‑серый фон */
display: flex;
justify-content: center;
align-items: center;
opacity: 0;
visibility: hidden;
transition: opacity 0.3s ease, visibility 0.3s ease;
border-radius: 50%;
z-index: 3;
}
/* Плюс в центре */
.rounded-plus {
display: flex;
justify-content: center;
align-items: center;
width: 80px; /* Диаметр круга */
height: 80px;
background-color: #333333; /* Тёмно‑серый фон плюса */
color: white; /* Белый знак "+" */
font-size: 96px; /* Размер символа "+" */
font-weight: bold;
border-radius: 50%; /* Делаем круглым */
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); /* Лёгкая тень */
transition: background-color 0.3s ease, transform 0.3s ease;
}
/* Эффект при наведении на плюс (можно убрать, если не нужно) */
.avatar-upload-label:hover .rounded-plus {
background-color: #323332; /* Темнее при наведении */
transform: scale(1.08); /* Лёгкое увеличение */
}
/* Эффект при наведении */
.avatar-upload-label:hover .avatar-overlay {
opacity: 1;
visibility: visible;
}
.avatar-upload-label:hover .avatar-container {
transform: scale(1.05);
}
.avatar-upload-label:hover .avatar-placeholder,
.avatar-upload-label:hover .avatar-image {
filter: brightness(0.9);
}
/* Индикатор загрузки */
.uploading-spinner {
margin-top: 10px;
font-size: 12px;
color: #6c757d;
text-align: center;
} }