feat: добавление Добавление функции смены аватарки
Добавлено окно с аватаркой по нажатию на которое добавлена функция загрузки нового аватара и исправлены некоторые недочёты.
This commit is contained in:
@@ -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>
|
||||||
|
|||||||
119
src/css/App.css
119
src/css/App.css
@@ -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;
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user