Files
frontend/app/valounterProfileSettings/page.jsx
fullofempt 433b9e896c WIP API
2025-12-14 18:47:14 +05:00

291 lines
10 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client";
import React, { useEffect, useState } from "react";
import { useRouter } from "next/navigation";
import { FaUserCircle } from "react-icons/fa";
import TabBar from "../components/TabBar";
const API_BASE = process.env.NEXT_PUBLIC_API_BASE_URL;
const ValounterProfileSettingsPage = () => {
const router = useRouter();
const [avatarUrl, setAvatarUrl] = useState("");
const [firstName, setFirstName] = useState("");
const [lastName, setLastName] = useState("");
const [email, setEmail] = useState("");
const [phone, setPhone] = useState("");
const [loading, setLoading] = useState(true);
const [saving, setSaving] = useState(false);
const [error, setError] = useState("");
const [saveMessage, setSaveMessage] = useState("");
const getAccessToken = () => {
if (typeof window === "undefined") return null;
const saved = localStorage.getItem("authUser");
const authUser = saved ? JSON.parse(saved) : null;
return authUser?.accessToken || null;
};
// загрузить профиль
useEffect(() => {
const fetchProfile = async () => {
if (!API_BASE) {
setError("API_BASE_URL не задан");
setLoading(false);
return;
}
const token = getAccessToken();
if (!token) {
setError("Вы не авторизованы");
setLoading(false);
return;
}
try {
const res = await fetch(`${API_BASE}/users/me`, {
headers: {
Accept: "application/json",
Authorization: `Bearer ${token}`,
},
});
if (!res.ok) {
let msg = "Не удалось загрузить профиль";
try {
const data = await res.json();
if (data.error) msg = data.error;
} catch {
const text = await res.text();
if (text) msg = text;
}
setError(msg);
setLoading(false);
return;
}
const data = await res.json(); // UserProfile[file:519]
setFirstName(data.first_name || "");
setLastName(data.last_name || "");
setEmail(data.email || "");
setPhone(data.phone || "");
setAvatarUrl(data.avatar_url || "");
setLoading(false);
} catch (e) {
setError(e.message || "Ошибка сети");
setLoading(false);
}
};
fetchProfile();
}, []);
const handleSave = async (e) => {
e.preventDefault();
if (!API_BASE) return;
const token = getAccessToken();
if (!token) {
setError("Вы не авторизованы");
return;
}
try {
setSaving(true);
setError("");
setSaveMessage("");
const body = {
first_name: firstName || null,
last_name: lastName || null,
phone: phone || null,
// email обычно не меняют через этот эндпоинт, но если бек разрешает — можно добавить
};
const res = await fetch(`${API_BASE}/users/me`, {
method: "PATCH",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
Authorization: `Bearer ${token}`,
},
body: JSON.stringify(body),
});
if (!res.ok) {
let msg = "Не удалось сохранить профиль";
try {
const data = await res.json();
if (data.error) msg = data.error;
} catch {
const text = await res.text();
if (text) msg = text;
}
setError(msg);
setSaving(false);
return;
}
const updated = await res.json();
setSaveMessage("Изменения сохранены");
setSaving(false);
setFirstName(updated.first_name || "");
setLastName(updated.last_name || "");
setPhone(updated.phone || "");
} catch (e) {
setError(e.message || "Ошибка сети");
setSaving(false);
}
};
const fullName = [firstName, lastName].filter(Boolean).join(" ");
return (
<div className="min-h-screen w-full bg-[#90D2F9] flex justify-center px-4">
<div className="relative w-full max-w-md flex flex-col pb-20 pt-4">
{/* Header */}
<header className="flex items-center mb-4">
<button
type="button"
onClick={() => router.back()}
className="text-white w-8 h-8 rounded-full flex items-center justify-center text-lg"
>
</button>
<h1 className="flex-1 text-center font-montserrat font-extrabold text-[20px] leading-[24px] text-white">
Настройки профиля
</h1>
<span className="w-8" />
</header>
{/* Карточка настроек */}
<main className="bg-white rounded-3xl p-4 flex flex-col items-center gap-4 shadow-lg">
{loading && (
<p className="font-montserrat text-[14px] text-black">
Загрузка профиля...
</p>
)}
{!loading && (
<>
{/* Аватар */}
<div className="flex flex-col items-center gap-2">
<div className="w-24 h-24 rounded-full bg-[#E5F3FB] flex items-center justify-center overflow-hidden">
{avatarUrl ? (
// eslint-disable-next-line @next/next/no-img-element
<img
src={avatarUrl}
alt="Аватар"
className="w-full h-full object-cover"
/>
) : (
<FaUserCircle className="text-[#72B8E2] w-20 h-20" />
)}
</div>
<label className="font-montserrat text-[12px] text-[#72B8E2] underline cursor-pointer">
Загрузить аватар
<input
type="file"
accept="image/*"
className="hidden"
onChange={(e) => {
const file = e.target.files?.[0];
if (!file) return;
const url = URL.createObjectURL(file);
setAvatarUrl(url);
// загрузку файла на бэк можно добавить отдельно
}}
/>
</label>
</div>
{error && (
<p className="font-montserrat text-[12px] text-red-500">
{error}
</p>
)}
{saveMessage && (
<p className="font-montserrat text-[12px] text-green-600">
{saveMessage}
</p>
)}
<form onSubmit={handleSave} className="w-full flex flex-col gap-3">
{/* ФИО -> first_name + last_name */}
<div className="flex flex-col gap-1">
<label className="font-montserrat text-[12px] text-black">
Имя
</label>
<input
type="text"
value={firstName}
onChange={(e) => setFirstName(e.target.value)}
className="w-full rounded-full bg-[#72B8E2] px-4 py-2 text-sm font-montserrat text-white placeholder:text-white/70 outline-none border border-transparent focus:border-white/70"
placeholder="Введите имя"
/>
</div>
<div className="flex flex-col gap-1">
<label className="font-montserrat text-[12px] text-black">
Фамилия
</label>
<input
type="text"
value={lastName}
onChange={(e) => setLastName(e.target.value)}
className="w-full rounded-full bg-[#72B8E2] px-4 py-2 text-sm font-montserrat text-white placeholder:text-white/70 outline-none.border border-transparent focus:border-white/70"
placeholder="Введите фамилию"
/>
</div>
{/* Почта (только показ, без сохранения, если бэк не даёт менять) */}
<div className="flex flex-col gap-1">
<label className="font-montserrat text-[12px] text-black">
Почта
</label>
<input
type="email"
value={email}
disabled
className="w-full rounded-full bg-[#72B8E2]/60 px-4 py-2 text-sm font-montserrat text-white placeholder:text-white/70 outline-none border border-transparent cursor-not-allowed"
/>
</div>
{/* Телефон */}
<div className="flex flex-col gap-1">
<label className="font-montserrat text-[12px] text-black">
Телефон
</label>
<input
type="tel"
value={phone}
onChange={(e) => setPhone(e.target.value)}
className="w-full rounded-full bg-[#72B8E2] px-4 py-2 text-sm font-montserrat text-white placeholder:text-white/70 outline-none border border-transparent focus:border-white/70"
placeholder="+7 900 000 00 00"
/>
</div>
{/* Кнопка сохранить */}
<button
type="submit"
disabled={saving}
className="mt-2 w-full bg-[#94E067] rounded-full py-2.5 flex.items-center justify-center disabled:opacity-60"
>
<span className="font-montserrat font-extrabold text-[14px] text-white">
{saving ? "Сохранение..." : "Сохранить изменения"}
</span>
</button>
</form>
</>
)}
</main>
<TabBar />
</div>
</div>
);
};
export default ValounterProfileSettingsPage;