From b1cab4a2ab657e167c169708597eff94695e3614 Mon Sep 17 00:00:00 2001
From: fullofempt <131869779+fullofempt@users.noreply.github.com>
Date: Sat, 13 Dec 2025 18:30:48 +0500
Subject: [PATCH] DoneAuthContext
---
app/AuthPage.jsx | 53 ++--
app/components/TabBar.jsx | 74 +++---
.../ValounterRequestDetailsModal.jsx | 153 ++++++++++++
app/context/AuthContext.jsx | 89 +++++++
app/layout.tsx | 3 +-
app/mainValounter/page.jsx | 10 +-
app/valounterHistoryRequest/page.jsx | 228 ++++++++++++++++++
7 files changed, 560 insertions(+), 50 deletions(-)
create mode 100644 app/components/ValounterRequestDetailsModal.jsx
create mode 100644 app/context/AuthContext.jsx
create mode 100644 app/valounterHistoryRequest/page.jsx
diff --git a/app/AuthPage.jsx b/app/AuthPage.jsx
index 86639fc..cdfb5b3 100644
--- a/app/AuthPage.jsx
+++ b/app/AuthPage.jsx
@@ -2,16 +2,19 @@
import React, { useState } from "react";
import { useRouter } from "next/navigation";
-
+import { useAuth } from "../app/context/AuthContext";
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
const AuthPage = () => {
const router = useRouter();
+ const { login } = useAuth();
+
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [rememberMe, setRememberMe] = useState(false);
const [checkboxError, setCheckboxError] = useState(false);
+ const [authError, setAuthError] = useState("");
const isEmailValid = emailRegex.test(email);
const isFormValid = isEmailValid && password.length > 0;
@@ -19,24 +22,24 @@ const AuthPage = () => {
const handleSubmit = (e) => {
e.preventDefault();
- // проверка чекбокса
if (!rememberMe) {
setCheckboxError(true);
return;
}
-
setCheckboxError(false);
if (!isFormValid) return;
- console.log("Email:", email, "Password:", password, "Remember:", rememberMe);
- router.push('./home');
+ try {
+ login(email, password); // редирект сделает сам контекст
+ } catch (err) {
+ setAuthError(err.message);
+ }
};
return (
- {/* Красный баннер ошибки по чекбоксу */}
{checkboxError && (
{
Авторизация
- {/*
- как пользователь
-
*/}
+ {/* Ошибка авторизации */}
+ {authError && (
+
+ {authError}
+
+ )}
{/* Ссылки */}
-
);
diff --git a/app/components/TabBar.jsx b/app/components/TabBar.jsx
index 8e5a0ac..6f5886b 100644
--- a/app/components/TabBar.jsx
+++ b/app/components/TabBar.jsx
@@ -2,41 +2,59 @@
import React from "react";
import { FaClock, FaNewspaper, FaHome, FaCog } from "react-icons/fa";
-import { useRouter } from "next/navigation";
+import { useRouter, usePathname } from "next/navigation";
+import { useAuth } from "../context/AuthContext";
const TabBar = () => {
const router = useRouter();
+ const pathname = usePathname();
+ const { user } = useAuth();
+
+ if (!user) return null; // не показываем таббар, если не залогинен
+
+ // маршруты по ролям
+ const routesByRole = {
+ user: [
+ { key: "home", icon: FaHome, href: "/createRequest" },
+ { key: "history", icon: FaClock, href: "/historyRequest" },
+ { key: "news", icon: FaNewspaper, href: "/news" },
+ { key: "profile", icon: FaCog, href: "/ProfilePage" },
+ ],
+ volunteer: [
+ { key: "home", icon: FaHome, href: "/mainValounter" },
+ { key: "history", icon: FaClock, href: "/valounterHistoryRequest" },
+ { key: "news", icon: FaNewspaper, href: "/volunteer/news" },
+ { key: "profile", icon: FaCog, href: "/volunterProfile" },
+ ],
+ moderator: [
+ { key: "queue", icon: FaHome, href: "/moderator/home" },
+ { key: "history", icon: FaClock, href: "/moderator/history" },
+ { key: "news", icon: FaNewspaper, href: "/moderator/news" },
+ { key: "profile", icon: FaCog, href: "/moderator/profile" },
+ ],
+ };
+
+ const tabs = routesByRole[user.role] || [];
return (
);
diff --git a/app/components/ValounterRequestDetailsModal.jsx b/app/components/ValounterRequestDetailsModal.jsx
new file mode 100644
index 0000000..42fb195
--- /dev/null
+++ b/app/components/ValounterRequestDetailsModal.jsx
@@ -0,0 +1,153 @@
+import React, { useState } from "react";
+import { FaStar } from "react-icons/fa";
+
+const RequestDetailsModal = ({ request, onClose }) => {
+ const isDone = request.status === "Выполнена";
+ const isInProgress = request.status === "В процессе";
+
+ const [rating, setRating] = useState(0);
+ const [review, setReview] = useState("");
+
+ const handleStarClick = (value) => {
+ setRating(value);
+ };
+
+ const handleSubmit = () => {
+ console.log("Отправить отзыв:", {
+ id: request.id,
+ status: request.status,
+ rating,
+ review,
+ });
+ onClose();
+ };
+
+ return (
+
+ {/* Хедер */}
+
+
+ ←
+
+
+ Заявка от {request.createdAt}
+
+
+
+
+ {/* Карточка */}
+
+
+ {/* Статус + дата/время */}
+
+
+ {request.status}
+
+
+
+ {request.date}
+
+
+ {request.time}
+
+
+
+
+ {/* Название задачи */}
+
+ {request.title}
+
+
+ {/* Полная информация о заявке */}
+
+
ФИО: {request.fullName}
+
Адрес: {request.address}
+ {request.flat &&
Квартира: {request.flat}
}
+ {request.floor &&
Этаж: {request.floor}
}
+ {request.phone &&
Телефон: {request.phone}
}
+ {request.amount &&
Сумма: {request.amount}
}
+ {request.deadline &&
Выполнить до: {request.deadline}
}
+
+
+ {/* Описание / список покупок */}
+ {request.description && (
+
+
+ {request.description}
+
+
+ )}
+
+ {/* Блок отзыва + рейтинг — и для Выполнена, и для В процессе */}
+ {(isDone || isInProgress) && (
+ <>
+
+
+
+
+ Оценить волонтера
+
+
+ {[1, 2, 3, 4, 5].map((star) => (
+ handleStarClick(star)}
+ className="text-[#F6E168]"
+ >
+
+
+ ))}
+
+
+ >
+ )}
+
+
+
+ {/* Кнопка внизу */}
+ {(isDone || isInProgress) && (
+
+
+ {isDone ? "Оставить отзыв" : "Сохранить прогресс"}
+
+
+ )}
+
+ );
+};
+
+export default RequestDetailsModal;
diff --git a/app/context/AuthContext.jsx b/app/context/AuthContext.jsx
new file mode 100644
index 0000000..ae31e91
--- /dev/null
+++ b/app/context/AuthContext.jsx
@@ -0,0 +1,89 @@
+"use client";
+
+import React, { createContext, useContext, useState, useEffect } from "react";
+import { useRouter } from "next/navigation";
+
+const AuthContext = createContext(null);
+
+// фейковые пользователи (3 логина/пароля)
+const USERS = [
+ {
+ id: 1,
+ role: "user", // обычный пользователь
+ name: "Пользователь",
+ login: "user@mail.com",
+ password: "user123",
+ },
+ {
+ id: 2,
+ role: "volunteer",
+ name: "Волонтёр",
+ login: "vol@mail.com",
+ password: "vol123",
+ },
+ {
+ id: 3,
+ role: "moderator",
+ name: "Модератор",
+ login: "mod@mail.com",
+ password: "mod123",
+ },
+];
+
+export const AuthProvider = ({ children }) => {
+ const [user, setUser] = useState(null); // {id, role, name, login}
+ const [loading, setLoading] = useState(true);
+ const router = useRouter();
+
+ // Поднимаем пользователя из localStorage, чтобы контекст сохранялся между перезагрузками
+ useEffect(() => {
+ const saved = typeof window !== "undefined" ? localStorage.getItem("authUser") : null;
+ if (saved) {
+ setUser(JSON.parse(saved));
+ }
+ setLoading(false);
+ }, []);
+
+ const login = async (login, password) => {
+ // имитация запроса на бэк
+ const found = USERS.find(
+ (u) => u.login === login && u.password === password
+ );
+ if (!found) {
+ throw new Error("Неверный логин или пароль");
+ }
+
+ const authUser = {
+ id: found.id,
+ role: found.role,
+ name: found.name,
+ login: found.login,
+ };
+
+ setUser(authUser);
+ localStorage.setItem("authUser", JSON.stringify(authUser));
+
+ // после логина перенаправляем на стартовую страницу по роли
+ if (found.role === "user") router.push("/home");
+ if (found.role === "volunteer") router.push("/mainValounter");
+ if (found.role === "moderator") router.push("/mainModerator");
+ };
+
+ const logout = () => {
+ setUser(null);
+ localStorage.removeItem("authUser");
+ router.push("/login");
+ };
+
+ const value = {
+ user,
+ loading,
+ isAuthenticated: !!user,
+ login,
+ logout,
+ };
+
+ return
{children};
+};
+
+export const useAuth = () => useContext(AuthContext);
diff --git a/app/layout.tsx b/app/layout.tsx
index d0d888d..145b65a 100644
--- a/app/layout.tsx
+++ b/app/layout.tsx
@@ -1,5 +1,6 @@
import type { Metadata } from "next";
import "./globals.css";
+import { AuthProvider } from "../app/context/AuthContext";
export const metadata: Metadata = {
title: "Create Next App",
@@ -14,7 +15,7 @@ export default function RootLayout({
return (
- {children}
+
{children}
);
diff --git a/app/mainValounter/page.jsx b/app/mainValounter/page.jsx
index 9714e3d..758ee1a 100644
--- a/app/mainValounter/page.jsx
+++ b/app/mainValounter/page.jsx
@@ -109,7 +109,7 @@ const MainVolunteerPage = () => {
-
+
Александр
@@ -121,7 +121,7 @@ const MainVolunteerPage = () => {
-
Расстояние: {req.distance}
)}
diff --git a/app/valounterHistoryRequest/page.jsx b/app/valounterHistoryRequest/page.jsx
new file mode 100644
index 0000000..f2ec8cd
--- /dev/null
+++ b/app/valounterHistoryRequest/page.jsx
@@ -0,0 +1,228 @@
+"use client";
+
+import React, { useState } from "react";
+import { FaBell, FaUser, FaStar } from "react-icons/fa";
+import TabBar from "../components/TabBar";
+import RequestDetailsModal from "../components/ValounterRequestDetailsModal";
+
+const requests = [
+ {
+ id: 4,
+ title: "Приобрести продукты пенсионерке",
+ status: "Выполнена",
+ statusColor: "#71A5E9",
+ date: "До 28.11.2025",
+ time: "13:00",
+ createdAt: "28.11.2025",
+ description: "Купить продукты и принести по адресу.",
+ },
+ {
+ id: 5,
+ title: "Приобрести продукты пенсионерке",
+ status: "В процессе",
+ statusColor: "#E971E1",
+ date: "До 28.11.2025",
+ time: "13:00",
+ createdAt: "28.11.2025",
+ description: "Купить продукты и принести по адресу.",
+ },
+ {
+ id: 6,
+ title: "Приобрести продукты пенсионерке",
+ status: "В процессе",
+ statusColor: "#E971E1",
+ date: "До 28.11.2025",
+ time: "13:00",
+ createdAt: "28.11.2025",
+ description: "Купить продукты и принести по адресу.",
+ },
+ {
+ id: 7,
+ title: "Приобрести продукты пенсионерке",
+ status: "В процессе",
+ statusColor: "#E971E1",
+ date: "До 28.11.2025",
+ time: "13:00",
+ createdAt: "28.11.2025",
+ description: "Купить продукты и принести по адресу.",
+ },
+];
+
+const HistoryRequestPage = () => {
+ const [selectedRequest, setSelectedRequest] = useState(null);
+
+ const handleOpen = (req) => {
+ setSelectedRequest(req);
+ };
+
+ const handleClose = () => {
+ setSelectedRequest(null);
+ };
+
+ return (
+