VolonteurMainPage
This commit is contained in:
109
app/ProfilePage/page.jsx
Normal file
109
app/ProfilePage/page.jsx
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
import { useRouter } from "next/navigation";
|
||||||
|
import { FaUserCircle, FaStar } from "react-icons/fa";
|
||||||
|
import TabBar from "../components/TabBar";
|
||||||
|
|
||||||
|
const ProfilePage = () => {
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const fullName = "Иванов Александр Сергеевич";
|
||||||
|
const birthDate = "12.03.1990";
|
||||||
|
const rating = 4.8;
|
||||||
|
|
||||||
|
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">
|
||||||
|
{/* Аватар */}
|
||||||
|
<FaUserCircle className="text-[#72B8E2] w-20 h-20" />
|
||||||
|
|
||||||
|
{/* ФИО и рейтинг */}
|
||||||
|
<div className="text-center space-y-1">
|
||||||
|
{/* <p className="font-montserrat font-extrabold text-[16px] text-black">
|
||||||
|
ФИО
|
||||||
|
</p> */}
|
||||||
|
<p className="font-montserrat font-bold text-[20px] text-black">
|
||||||
|
{fullName}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{/* Рейтинг + звезды */}
|
||||||
|
<div className="mt-2 flex items-center justify-center gap-2">
|
||||||
|
<span className="font-montserrat font-semibold text-[14px] text-black">
|
||||||
|
Рейтинг: {rating.toFixed(1)}
|
||||||
|
</span>
|
||||||
|
<div className="flex gap-1">
|
||||||
|
{[1, 2, 3, 4, 5].map((star) => (
|
||||||
|
<FaStar
|
||||||
|
key={star}
|
||||||
|
size={18}
|
||||||
|
className={
|
||||||
|
star <= Math.round(rating)
|
||||||
|
? "text-[#F6E168] fill-[#F6E168]"
|
||||||
|
: "text-[#F6E168] fill-[#F6E168]/30"
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Контакты и день рождения */}
|
||||||
|
<div className="w-full bg-[#72B8E2] rounded-2xl p-3 text-white space-y-1">
|
||||||
|
<p className="font-montserrat text-[12px]">
|
||||||
|
Дата рождения: {birthDate}
|
||||||
|
</p>
|
||||||
|
<p className="font-montserrat text-[12px]">
|
||||||
|
Почта: example@mail.com
|
||||||
|
</p>
|
||||||
|
<p className="font-montserrat text-[12px]">
|
||||||
|
Телефон: +7 (900) 000-00-00
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Кнопки */}
|
||||||
|
<div className="w-full flex flex-col gap-2 mt-2">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => router.push("/profileSettings")}
|
||||||
|
className="w-full bg-[#E0B267] rounded-full py-2 flex items-center justify-center"
|
||||||
|
>
|
||||||
|
<span className="font-montserrat font-extrabold text-[14px] text-white">
|
||||||
|
Редактировать профиль
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="w-full bg-[#E07567] rounded-full py-2 flex items-center justify-center"
|
||||||
|
>
|
||||||
|
<span className="font-montserrat font-extrabold text-[14px] text-white">
|
||||||
|
Выйти из аккаунта
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<TabBar />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ProfilePage;
|
||||||
@@ -43,7 +43,7 @@ const RequestDetailsModal = ({ request, onClose }) => {
|
|||||||
|
|
||||||
{/* Белая карточка как на макете */}
|
{/* Белая карточка как на макете */}
|
||||||
<div className="flex-1 flex items-start justify-center">
|
<div className="flex-1 flex items-start justify-center">
|
||||||
<div className="w-full max-w-[360px] bg-white rounded-3xl p-4 flex flex-col gap-4 shadow-lg">
|
<div className="w-full max-w-[360px] bg-white rounded-2xl p-4 flex flex-col gap-4 shadow-lg">
|
||||||
{/* Статус + срок (берём цвет и текст из заявки) */}
|
{/* Статус + срок (берём цвет и текст из заявки) */}
|
||||||
<div className="flex items-start justify-between">
|
<div className="flex items-start justify-between">
|
||||||
<span
|
<span
|
||||||
@@ -87,7 +87,7 @@ const RequestDetailsModal = ({ request, onClose }) => {
|
|||||||
{isRejected && (
|
{isRejected && (
|
||||||
<>
|
<>
|
||||||
{request.rejectReason && (
|
{request.rejectReason && (
|
||||||
<div className="bg-[#FF8282] rounded-3xl p-3">
|
<div className="bg-[#FF8282] rounded-2xl p-3">
|
||||||
<p className="font-montserrat font-bold text-[12px] text-white mb-1">
|
<p className="font-montserrat font-bold text-[12px] text-white mb-1">
|
||||||
Причина отказа
|
Причина отказа
|
||||||
</p>
|
</p>
|
||||||
@@ -147,7 +147,7 @@ const RequestDetailsModal = ({ request, onClose }) => {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={handleSubmit}
|
onClick={handleSubmit}
|
||||||
className="mt-4 w-full max-w-[360px] mx-auto bg-[#94E067] rounded-full py-3 flex items-center justify-center"
|
className="mt-4 w-full max-w-[360px] mx-auto bg-[#94E067] rounded-2xl py-3 flex items-center justify-center"
|
||||||
>
|
>
|
||||||
<span className="font-montserrat font-extrabold text-[16px] text-white">
|
<span className="font-montserrat font-extrabold text-[16px] text-white">
|
||||||
{isRejected ? "Отправить комментарий" : "Оставить отзыв"}
|
{isRejected ? "Отправить комментарий" : "Оставить отзыв"}
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ const TabBar = () => {
|
|||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
onClick={() => router.push("/ProfilePage")}
|
||||||
className="flex flex-col items-center gap-1 text-[#90D2F9]"
|
className="flex flex-col items-center gap-1 text-[#90D2F9]"
|
||||||
>
|
>
|
||||||
<FaCog size={20} />
|
<FaCog size={20} />
|
||||||
|
|||||||
102
app/components/acceptPopUp.jsx
Normal file
102
app/components/acceptPopUp.jsx
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
import { FaTimesCircle } from "react-icons/fa";
|
||||||
|
|
||||||
|
const AcceptPopup = ({ request, isOpen, onClose, onAccept }) => {
|
||||||
|
if (!isOpen || !request) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="fixed inset-0 z-50 flex items-center justify-center">
|
||||||
|
{/* затемнение */}
|
||||||
|
<div
|
||||||
|
className="absolute inset-0 bg-black/40"
|
||||||
|
onClick={onClose}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* карточка на всю страницу */}
|
||||||
|
<div className="relative z-10 w-full h-250px bg-white rounded-2xl px-4 pt-4 pb-6 flex flex-col">
|
||||||
|
{/* крестик */}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={onClose}
|
||||||
|
className="absolute top-4 right-4 text-[#FF9494]"
|
||||||
|
>
|
||||||
|
<FaTimesCircle className="w-5 h-5" />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{/* Заголовок */}
|
||||||
|
<h2 className="font-montserrat font-extrabold text-[20px] leading-[22px] text-[#90D2F9] mb-1">
|
||||||
|
Задача
|
||||||
|
</h2>
|
||||||
|
<p className="text-[20px] leading-[14px] mt-5 font-montserrat mb-5">
|
||||||
|
{request.title}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{/* Сумма и время */}
|
||||||
|
<div className="flex items-center gap-3 mb-3">
|
||||||
|
<div className="w-full h-[40px] bg-[#90D2F9] rounded-full flex flex-col items-center justify-center">
|
||||||
|
<span className="text-[12px] leading-[11px] text-white font-semibold mb-2">
|
||||||
|
Сумма
|
||||||
|
</span>
|
||||||
|
<span className="text-[15px] leading-[13px] text-white font-semibold">
|
||||||
|
{request.amount || "2000 ₽"}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="w-full h-[40px] bg-[#90D2F9] rounded-full flex flex-col items-center justify-center">
|
||||||
|
<span className="text-[12px] leading-[11px] text-white font-semibold mb-2">
|
||||||
|
Выполнить до
|
||||||
|
</span>
|
||||||
|
<span className="text-[15px] leading-[13px] text-white font-semibold">
|
||||||
|
{request.deadline || "17:00"}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Список покупок / описание */}
|
||||||
|
<div className="w-full bg-[#E4E4E4] rounded-[20px] px-3 py-3 mb-3 max-h-[40vh] overflow-y-auto">
|
||||||
|
<p className="text-[15px] leading-[20px] font-montserrat text-black whitespace-pre-line">
|
||||||
|
{request.description ||
|
||||||
|
"Необходимо приобрести:\n1. Белый хлеб\n2. Молоко\n3. Колбаса\n4. Фрукты"}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Данные человека */}
|
||||||
|
<div className="w-full flex flex-col gap-3 mb-4">
|
||||||
|
<p className="font-montserrat text-[20px] leading-[19px] font-medium">
|
||||||
|
Данные:
|
||||||
|
</p>
|
||||||
|
<p className="text-[20px] leading-[12px] font-montserrat">
|
||||||
|
ФИО: {request.fullName || "Клавдия Березова"}
|
||||||
|
</p>
|
||||||
|
<p className="text-[15px] leading-[12px] font-montserrat">
|
||||||
|
Место: {request.address}
|
||||||
|
</p>
|
||||||
|
{request.flat && (
|
||||||
|
<p className="text-[10px] leading-[12px] font-montserrat">
|
||||||
|
кв: {request.flat}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
{request.floor && (
|
||||||
|
<p className="text-[10px] leading-[12px] font-montserrat">
|
||||||
|
Этаж: {request.floor}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Кнопка отклика внизу */}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => onAccept(request)}
|
||||||
|
className="mt-auto w-full h-[40px] bg-[#94E067] rounded-[10px] flex items-center justify-center"
|
||||||
|
>
|
||||||
|
<span className="font-montserrat font-bold text-[16px] leading-[19px] text-white">
|
||||||
|
Откликнуться
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AcceptPopup;
|
||||||
@@ -99,7 +99,7 @@ const HistoryRequestPage = () => {
|
|||||||
<div className="w-8 h-8 rounded-full border border-white flex items-center justify-center">
|
<div className="w-8 h-8 rounded-full border border-white flex items-center justify-center">
|
||||||
<FaUser className="text-white text-sm" />
|
<FaUser className="text-white text-sm" />
|
||||||
</div>
|
</div>
|
||||||
<p className="font-montserrat font-extrabold text-[11px] leading-[11px] text-white">
|
<p className="font-montserrat font-extrabold text-[20px] leading-[11px] text-white">
|
||||||
Александр
|
Александр
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -111,7 +111,7 @@ const HistoryRequestPage = () => {
|
|||||||
</button>
|
</button>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<h1 className="font-montserrat font-extrabold text-[18px] leading-[22px] text-white mb-3">
|
<h1 className="font-montserrat font-extrabold text-[20px] leading-[22px] text-white mb-3">
|
||||||
История заявок
|
История заявок
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
|
|||||||
202
app/mainValounter/page.jsx
Normal file
202
app/mainValounter/page.jsx
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import dynamic from "next/dynamic";
|
||||||
|
import { FaUser, FaCog } from "react-icons/fa";
|
||||||
|
import TabBar from "../components/TabBar";
|
||||||
|
import AcceptPopup from "../components/acceptPopUp";
|
||||||
|
|
||||||
|
// динамический импорт карты, чтобы не падало на сервере
|
||||||
|
const MapContainer = dynamic(
|
||||||
|
() => import("react-leaflet").then((m) => m.MapContainer),
|
||||||
|
{ ssr: false }
|
||||||
|
);
|
||||||
|
const TileLayer = dynamic(
|
||||||
|
() => import("react-leaflet").then((m) => m.TileLayer),
|
||||||
|
{ ssr: false }
|
||||||
|
);
|
||||||
|
const Marker = dynamic(
|
||||||
|
() => import("react-leaflet").then((m) => m.Marker),
|
||||||
|
{ ssr: false }
|
||||||
|
);
|
||||||
|
const Popup = dynamic(
|
||||||
|
() => import("react-leaflet").then((m) => m.Popup),
|
||||||
|
{ ssr: false }
|
||||||
|
);
|
||||||
|
|
||||||
|
// центр Перми
|
||||||
|
const DEFAULT_POSITION = [58.0105, 56.2294];
|
||||||
|
|
||||||
|
const requests = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
title: "Приобрести продукты пенсионерке",
|
||||||
|
address: "г. Пермь, ул. Ленина 50, кв. 24, этаж 3",
|
||||||
|
coords: [58.0109, 56.2478], // район ул. Ленина
|
||||||
|
distance: "1.2 км",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
title: "Приобрести медикаменты бабушке",
|
||||||
|
address: "г. Пермь, ул. Пушкина 24, кв. 12, этаж 1",
|
||||||
|
coords: [58.0135, 56.2320], // район ул. Пушкина
|
||||||
|
distance: "2.0 км",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
title: "Сопроводить до поликлиники",
|
||||||
|
address: "г. Пермь, ул. Куйбышева 95, кв. 7, этаж 2",
|
||||||
|
coords: [58.0068, 56.2265], // район ул. Куйбышева
|
||||||
|
distance: "3.4 км",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
title: "Сопроводить до поликлиники",
|
||||||
|
address: "г. Пермь, ул. Куйбышева 95, кв. 7, этаж 2",
|
||||||
|
coords: [58.0068, 56.2265], // район ул. Куйбышева
|
||||||
|
distance: "3.4 км",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 5,
|
||||||
|
title: "Сопроводить до поликлиники",
|
||||||
|
address: "г. Пермь, ул. Куйбышева 95, кв. 7, этаж 2",
|
||||||
|
coords: [58.0068, 56.2265], // район ул. Куйбышева
|
||||||
|
distance: "3.4 км",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
const MainVolunteerPage = () => {
|
||||||
|
const [position, setPosition] = useState(DEFAULT_POSITION);
|
||||||
|
const [hasLocation, setHasLocation] = useState(false);
|
||||||
|
const [selectedRequest, setSelectedRequest] = useState(null);
|
||||||
|
const [isPopupOpen, setIsPopupOpen] = useState(false);
|
||||||
|
|
||||||
|
const openPopup = (req) => {
|
||||||
|
setSelectedRequest(req);
|
||||||
|
setIsPopupOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const closePopup = () => {
|
||||||
|
setIsPopupOpen(false);
|
||||||
|
setSelectedRequest(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!navigator.geolocation) return;
|
||||||
|
navigator.geolocation.getCurrentPosition(
|
||||||
|
(pos) => {
|
||||||
|
setPosition([pos.coords.latitude, pos.coords.longitude]);
|
||||||
|
setHasLocation(true);
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
setHasLocation(false);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleAccept = (req) => {
|
||||||
|
console.log("Откликнуться на заявку:", req.id);
|
||||||
|
// TODO: запрос на бэк
|
||||||
|
};
|
||||||
|
|
||||||
|
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 justify-between mb-3">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<div className="w-8 h-8 rounded-full border border-white flex items-center justify-center">
|
||||||
|
<FaUser className="text-white text-sm" />
|
||||||
|
</div>
|
||||||
|
<p className="font-montserrat font-extrabold text-[11px] leading-[11px] text-white">
|
||||||
|
Александр
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="w-8 h-8 rounded-full border border-white flex items-center justify-center"
|
||||||
|
>
|
||||||
|
<FaCog className="text-white text-sm" />
|
||||||
|
</button>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<h1 className="font-montserrat font-extrabold text-[16px] leading-[20px] text-white mb-2">
|
||||||
|
Кому нужна помощь
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
{/* Карта */}
|
||||||
|
<div className="w-full bg-transparent mb-3">
|
||||||
|
<div className="w-full h-[250px] bg-[#D9D9D9] rounded-2xl overflow-hidden">
|
||||||
|
<MapContainer
|
||||||
|
center={position}
|
||||||
|
zoom={13}
|
||||||
|
style={{ width: "100%", height: "100%" }}
|
||||||
|
>
|
||||||
|
<TileLayer
|
||||||
|
attribution='© OpenStreetMap contributors'
|
||||||
|
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
|
||||||
|
/>
|
||||||
|
{/* Маркер волонтёра */}
|
||||||
|
{hasLocation && (
|
||||||
|
<Marker position={position}>
|
||||||
|
<Popup>Вы здесь</Popup>
|
||||||
|
</Marker>
|
||||||
|
)}
|
||||||
|
{/* Маркеры заявок */}
|
||||||
|
{requests.map((req) => (
|
||||||
|
<Marker key={req.id} position={req.coords}>
|
||||||
|
<Popup>{req.title}</Popup>
|
||||||
|
</Marker>
|
||||||
|
))}
|
||||||
|
</MapContainer>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Заявки ниже карты */}
|
||||||
|
<main className="space-y-3">
|
||||||
|
{requests.map((req) => (
|
||||||
|
<div
|
||||||
|
key={req.id}
|
||||||
|
className="bg-white rounded-xl px-3 py-2 flex flex-col gap-1"
|
||||||
|
onClick={() => openPopup(req)}
|
||||||
|
>
|
||||||
|
<p className="font-montserrat font-semibold text-[12px] leading-[14px] text-black">
|
||||||
|
{req.title}
|
||||||
|
</p>
|
||||||
|
<p className="font-montserrat text-[10px] text-black">
|
||||||
|
{req.address}
|
||||||
|
</p>
|
||||||
|
{req.distance && (
|
||||||
|
<p className="font-montserrat text-[9px] text-gray-500">
|
||||||
|
Расстояние: {req.distance}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => handleAccept(req)}
|
||||||
|
className="mt-2 w-full bg-[#94E067] rounded-lg py-2 flex items-center justify-center"
|
||||||
|
>
|
||||||
|
<span className="font-montserrat font-bold text-[14px] text-white">
|
||||||
|
Откликнуться
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<TabBar />
|
||||||
|
</div>
|
||||||
|
<AcceptPopup
|
||||||
|
request={selectedRequest}
|
||||||
|
isOpen={isPopupOpen}
|
||||||
|
onClose={closePopup}
|
||||||
|
onAccept={handleAccept}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default MainVolunteerPage;
|
||||||
|
|
||||||
153
app/profileSettings/page.jsx
Normal file
153
app/profileSettings/page.jsx
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import { useRouter } from "next/navigation";
|
||||||
|
import { FaUserCircle } from "react-icons/fa";
|
||||||
|
import TabBar from "../components/TabBar";
|
||||||
|
|
||||||
|
const ProfileSettingsPage = () => {
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const [avatarUrl, setAvatarUrl] = useState("");
|
||||||
|
const [fullName, setFullName] = useState("Иванов Александр Сергеевич");
|
||||||
|
const [birthDate, setBirthDate] = useState("1990-03-12");
|
||||||
|
const [email, setEmail] = useState("example@mail.com");
|
||||||
|
const [phone, setPhone] = useState("+7 (900) 000-00-00");
|
||||||
|
|
||||||
|
const handleSave = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
console.log("Сохранить профиль:", {
|
||||||
|
avatarUrl,
|
||||||
|
fullName,
|
||||||
|
birthDate,
|
||||||
|
email,
|
||||||
|
phone,
|
||||||
|
});
|
||||||
|
// здесь будет запрос на бэк
|
||||||
|
};
|
||||||
|
|
||||||
|
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">
|
||||||
|
{/* Аватар */}
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<form onSubmit={handleSave} className="w-full flex flex-col gap-3">
|
||||||
|
{/* ФИО */}
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
<label className="font-montserrat text-[12px] text-black">
|
||||||
|
ФИО
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={fullName}
|
||||||
|
onChange={(e) => setFullName(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="date"
|
||||||
|
value={birthDate}
|
||||||
|
onChange={(e) => setBirthDate(e.target.value)}
|
||||||
|
className="w-full rounded-full bg-[#72B8E2] px-4 py-2 text-sm font-montserrat text-white outline-none border border-transparent focus:border-white/70"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Почта */}
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
<label className="font-montserrat text-[12px] text-black">
|
||||||
|
Почта
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="email"
|
||||||
|
value={email}
|
||||||
|
onChange={(e) => setEmail(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="example@mail.com"
|
||||||
|
/>
|
||||||
|
</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"
|
||||||
|
className="mt-2 w-full bg-[#94E067] rounded-full py-2.5 flex items-center justify-center"
|
||||||
|
>
|
||||||
|
<span className="font-montserrat font-extrabold text-[14px] text-white">
|
||||||
|
Сохранить изменения
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<TabBar />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ProfileSettingsPage;
|
||||||
35
package-lock.json
generated
35
package-lock.json
generated
@@ -8,10 +8,12 @@
|
|||||||
"name": "frontend",
|
"name": "frontend",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"leaflet": "^1.9.4",
|
||||||
"next": "16.0.10",
|
"next": "16.0.10",
|
||||||
"react": "19.2.1",
|
"react": "19.2.1",
|
||||||
"react-dom": "19.2.1",
|
"react-dom": "19.2.1",
|
||||||
"react-icons": "^5.5.0"
|
"react-icons": "^5.5.0",
|
||||||
|
"react-leaflet": "^5.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tailwindcss/postcss": "^4",
|
"@tailwindcss/postcss": "^4",
|
||||||
@@ -1227,6 +1229,17 @@
|
|||||||
"node": ">=12.4.0"
|
"node": ">=12.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@react-leaflet/core": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@react-leaflet/core/-/core-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-3EWmekh4Nz+pGcr+xjf0KNyYfC3U2JjnkWsh0zcqaexYqmmB5ZhH37kz41JXGmKzpaMZCnPofBBm64i+YrEvGQ==",
|
||||||
|
"license": "Hippocratic-2.1",
|
||||||
|
"peerDependencies": {
|
||||||
|
"leaflet": "^1.9.0",
|
||||||
|
"react": "^19.0.0",
|
||||||
|
"react-dom": "^19.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@rtsao/scc": {
|
"node_modules/@rtsao/scc": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
|
||||||
@@ -4514,6 +4527,12 @@
|
|||||||
"node": ">=0.10"
|
"node": ">=0.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/leaflet": {
|
||||||
|
"version": "1.9.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz",
|
||||||
|
"integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==",
|
||||||
|
"license": "BSD-2-Clause"
|
||||||
|
},
|
||||||
"node_modules/levn": {
|
"node_modules/levn": {
|
||||||
"version": "0.4.1",
|
"version": "0.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
|
||||||
@@ -5416,6 +5435,20 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/react-leaflet": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-leaflet/-/react-leaflet-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-CWbTpr5vcHw5bt9i4zSlPEVQdTVcML390TjeDG0cK59z1ylexpqC6M1PJFjV8jD7CF+ACBFsLIDs6DRMoLEofw==",
|
||||||
|
"license": "Hippocratic-2.1",
|
||||||
|
"dependencies": {
|
||||||
|
"@react-leaflet/core": "^3.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"leaflet": "^1.9.0",
|
||||||
|
"react": "^19.0.0",
|
||||||
|
"react-dom": "^19.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/reflect.getprototypeof": {
|
"node_modules/reflect.getprototypeof": {
|
||||||
"version": "1.0.10",
|
"version": "1.0.10",
|
||||||
"resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz",
|
"resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz",
|
||||||
|
|||||||
@@ -9,10 +9,12 @@
|
|||||||
"lint": "eslint"
|
"lint": "eslint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"leaflet": "^1.9.4",
|
||||||
"next": "16.0.10",
|
"next": "16.0.10",
|
||||||
"react": "19.2.1",
|
"react": "19.2.1",
|
||||||
"react-dom": "19.2.1",
|
"react-dom": "19.2.1",
|
||||||
"react-icons": "^5.5.0"
|
"react-icons": "^5.5.0",
|
||||||
|
"react-leaflet": "^5.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tailwindcss/postcss": "^4",
|
"@tailwindcss/postcss": "^4",
|
||||||
|
|||||||
Reference in New Issue
Block a user