diff --git a/modules/frontend/src/App.tsx b/modules/frontend/src/App.tsx index de7101c..f8ebdf5 100644 --- a/modules/frontend/src/App.tsx +++ b/modules/frontend/src/App.tsx @@ -5,6 +5,7 @@ import UserPage from "./pages/UserPage/UserPage"; import TitlesPage from "./pages/TitlesPage/TitlesPage"; import TitlePage from "./pages/TitlePage/TitlePage"; import { LoginPage } from "./pages/LoginPage/LoginPage"; +import { SettingsPage } from "./pages/SettingsPage/SettingsPage"; import { Header } from "./components/Header/Header"; // import { OpenAPI } from "./api"; @@ -46,6 +47,12 @@ const App: React.FC = () => { element={userId ? : } /> + {/*settings*/} + : } + /> + {/* titles */} } /> } /> diff --git a/modules/frontend/src/components/Header/Header.tsx b/modules/frontend/src/components/Header/Header.tsx index 36cbd5a..9c3b39a 100644 --- a/modules/frontend/src/components/Header/Header.tsx +++ b/modules/frontend/src/components/Header/Header.tsx @@ -32,6 +32,8 @@ export const Header: React.FC = () => { localStorage.removeItem("user_id"); localStorage.removeItem("user_name"); setUsername(null); + setDropdownOpen(false); + setMenuOpen(false); navigate("/login"); } catch (err) { console.error(err); @@ -74,14 +76,38 @@ export const Header: React.FC = () => {
{dropdownOpen && ( -
- setDropdownOpen(false)}>Profile - +
+ setDropdownOpen(false)} + > + Profile + + + {/* КНОПКА SETTINGS */} + setDropdownOpen(false)} + > + Settings + + +
+ +
)}
@@ -107,11 +133,16 @@ export const Header: React.FC = () => { setMenuOpen(false)}>Titles setMenuOpen(false)}>Users setMenuOpen(false)}>About + {username ? ( - <> - setMenuOpen(false)}>Profile - - +
+ setMenuOpen(false)}>Profile + + {/* SETTINGS (Mobile) */} + setMenuOpen(false)}>Settings + + +
) : ( setMenuOpen(false)}>Login )} diff --git a/modules/frontend/src/pages/SettingsPage/SettingsPage.tsx b/modules/frontend/src/pages/SettingsPage/SettingsPage.tsx index 16c7e9e..d3b6fc6 100644 --- a/modules/frontend/src/pages/SettingsPage/SettingsPage.tsx +++ b/modules/frontend/src/pages/SettingsPage/SettingsPage.tsx @@ -1,154 +1,212 @@ -// import React, { useEffect, useState } from "react"; -// import { updateUser, getUsersId } from "../../api"; -// import { useNavigate } from "react-router-dom"; +import React, { useEffect, useState, useRef } from "react"; +import { updateUser, getUsersId, postMediaUpload } from "../../api"; +import { useNavigate } from "react-router-dom"; +import { useCookies } from 'react-cookie'; -// export const SettingsPage: React.FC = () => { -// const navigate = useNavigate(); +export const SettingsPage: React.FC = () => { + const [cookies] = useCookies(['xsrf_token']); + const xsrfToken = cookies['xsrf_token'] || null; -// const userId = Number(localStorage.getItem("user_id")); -// const initialNickname = localStorage.getItem("user_name") || ""; -// const [mail, setMail] = useState(""); -// const [nickname, setNickname] = useState(initialNickname); -// const [dispName, setDispName] = useState(""); -// const [userDesc, setUserDesc] = useState(""); -// const [avatarId, setAvatarId] = useState(null); + const navigate = useNavigate(); + const fileInputRef = useRef(null); -// const [loading, setLoading] = useState(false); -// const [success, setSuccess] = useState(null); -// const [error, setError] = useState(null); + const userId = Number(localStorage.getItem("user_id")); + + // Состояния для полей формы + const [mail, setMail] = useState(""); + const [nickname, setNickname] = useState(""); + const [dispName, setDispName] = useState(""); + const [userDesc, setUserDesc] = useState(""); + const [avatarId, setAvatarId] = useState(null); + const [avatarUrl, setAvatarUrl] = useState(null); -// useEffect(() => { -// const fetch = async () => { -// const res = await getUsersId({ -// path: { user_id: String(userId) }, -// }); + const [loading, setLoading] = useState(false); + const [uploading, setUploading] = useState(false); + const [success, setSuccess] = useState(null); + const [error, setError] = useState(null); -// setProfile(res.data); -// }; + // Загружаем текущие данные пользователя при входе + useEffect(() => { + const fetchUserData = async () => { + try { + const res = await getUsersId({ + path: { user_id: String(userId) }, + }); + if (res.data) { + setMail(res.data.mail || ""); + setNickname(res.data.nickname || ""); + setDispName(res.data.disp_name || ""); + setUserDesc(res.data.user_desc || ""); + setAvatarId(res.data.image?.id ?? null); + setAvatarUrl(res.data.image?.image_path ?? null); + } + } catch (err) { + console.error("Failed to fetch user:", err); + } + }; + fetchUserData(); + }, [userId]); -// fetch(); -// }, [userId]); + // Обработка загрузки файла + const handleFileChange = async (e: React.ChangeEvent) => { + const file = e.target.files?.[0]; + if (!file) return; -// const saveSettings = async (e: React.FormEvent) => { -// e.preventDefault(); -// setLoading(true); -// setSuccess(null); -// setError(null); + setUploading(true); + setError(null); -// try { -// const res = await updateUser({ -// path: { user_id: userId }, -// body: { -// ...(mail ? { mail } : {}), -// ...(nickname ? { nickname } : {}), -// ...(dispName ? { disp_name: dispName } : {}), -// ...(userDesc ? { user_desc: userDesc } : {}), -// ...(avatarId !== undefined ? { avatar_id: avatarId } : {}), -// } -// }); + const formData = new FormData(); + formData.append("file", file); -// // Обновляем локальное отображение username -// if (nickname) { -// localStorage.setItem("user_name", nickname); -// window.dispatchEvent(new Event("storage")); // чтобы Header обновился -// } + try { + const res = await postMediaUpload({ + body: formData, + // Сбрасываем заголовок, чтобы браузер сам поставил multipart/form-data + boundary + headers: { "Content-Type": undefined as any }, + }); -// setSuccess("Settings updated!"); -// setTimeout(() => navigate("/profile"), 800); + if (res.data && res.data.id) { + setAvatarId(res.data.id); + setAvatarUrl(res.data.image_path ?? null); // Для мгновенного превью + setSuccess("Image uploaded!"); + } + } catch (err: any) { + setError("Failed to upload image"); + } finally { + setUploading(false); + } + }; -// } catch (err: any) { -// console.error(err); -// setError(err?.message || "Failed to update settings"); -// } finally { -// setLoading(false); -// } -// }; + const saveSettings = async (e: React.FormEvent) => { + e.preventDefault(); + setLoading(true); + setSuccess(null); + setError(null); -// return ( -//
-//

User Settings

+ try { + await updateUser({ + path: { user_id: userId }, + body: { + mail: mail || undefined, + nickname: nickname || undefined, + disp_name: dispName || undefined, + user_desc: userDesc || undefined, + avatar_id: avatarId, // Может быть числом или null для удаления + }, + headers: { "X-XSRF-TOKEN": xsrfToken }, + }); -// {success &&
{success}
} -// {error &&
{error}
} + localStorage.setItem("user_name", nickname); + window.dispatchEvent(new Event("storage")); -//
-// {/* Email */} -//
-// -// setMail(e.target.value)} -// placeholder="example@mail.com" -// className="w-full px-4 py-2 border rounded-lg" -// /> -//
+ setSuccess("Settings updated successfully!"); + setTimeout(() => navigate("/profile"), 1000); + } catch (err: any) { + setError(err?.message || "Failed to update settings"); + } finally { + setLoading(false); + } + }; -// {/* Nickname */} -//
-// -// setNickname(e.target.value)} -// className="w-full px-4 py-2 border rounded-lg" -// /> -//
+ return ( +
+

User Settings

-// {/* Display name */} -//
-// -// setDispName(e.target.value)} -// placeholder="Shown name" -// className="w-full px-4 py-2 border rounded-lg" -// /> -//
+ {success &&
{success}
} + {error &&
{error}
} -// {/* Bio */} -//
-// -//