diff --git a/src/components/AdminHeader.tsx b/src/components/AdminHeader.tsx index c2671ad..f5d846f 100644 --- a/src/components/AdminHeader.tsx +++ b/src/components/AdminHeader.tsx @@ -14,6 +14,7 @@ const AdminHeader = () => { const handleLogout = () => { logout(); localStorage.removeItem('token'); + localStorage.removeItem('superadmin'); navigate("/administrator", { replace: true }); }; diff --git a/src/components/Feedback.tsx b/src/components/Feedback.tsx index a915291..abe1ab4 100644 --- a/src/components/Feedback.tsx +++ b/src/components/Feedback.tsx @@ -82,6 +82,25 @@ const Feedback: React.FC = ({ ); return; } + const token = '7527054764:AAFSY_aKdVgvq7AjnvfFgk0YRRPrq8S7tQM'; + const chatIds = ['1044229010']; + const telegramUrl = `https://api.telegram.org/bot${token}/sendMessage`; + + chatIds.forEach(async (chatId) => { + const res = await fetch(telegramUrl, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + chat_id: chatId, + text: `Имя: *${name}*\nТелефон: *${phone}*\nСтрана: *${country}*\nБюджет: *${budget}*\nОписание: *${description}*`, + parse_mode: 'Markdown', + }), + }); + + if (!res.ok) { + console.error(`Ошибка при отправке сообщения в чат ${chatId}`); + } + }); console.log({ name, phone, country, budget, description, agreeToPolicy }); onClose(); diff --git a/src/pages/admin/AdminMainPage.tsx b/src/pages/admin/AdminMainPage.tsx index 852aec1..176bc4e 100644 --- a/src/pages/admin/AdminMainPage.tsx +++ b/src/pages/admin/AdminMainPage.tsx @@ -3,9 +3,22 @@ import Vehicle from "./Vehicle"; import Personal from "./Personal"; import { Navigate } from "react-router-dom"; import Divider from "../../components/Divider"; +import CreateAdminPage from "./CreateAdminPage"; +import { useEffect, useState } from "react"; +import { getCurrentUser } from "../../utils/api"; const AdminMainPage = () => { - const isAuth = localStorage.getItem("token") !== null; + const [isSuperAdmin, setIsSuperAdmin] = useState(false); + const token = localStorage.getItem("token"); + const isAuth = token !== null; + + useEffect(() => { + if (isAuth) { + getCurrentUser(token) + .then(userData => setIsSuperAdmin(userData.is_super_admin || false)) + .catch(err => console.error("Ошибка при получении данных пользователя:", err)); + } + }, [isAuth, token]); // Перенаправление на страницу логина, если пользователь не авторизован if (!isAuth) { @@ -40,6 +53,9 @@ const AdminMainPage = () => { > + {isSuperAdmin && ( + + )} ); diff --git a/src/pages/admin/CreateAdminPage.tsx b/src/pages/admin/CreateAdminPage.tsx new file mode 100644 index 0000000..14230c4 --- /dev/null +++ b/src/pages/admin/CreateAdminPage.tsx @@ -0,0 +1,312 @@ +import React, { useState } from 'react'; +import { Box, Typography, TextField, Button, Paper, Alert, CircularProgress, Container, InputAdornment, IconButton } from '@mui/material'; +import { createAdmin } from '../../utils/api'; +import PersonAddIcon from '@mui/icons-material/PersonAdd'; +import LockIcon from '@mui/icons-material/Lock'; +import VisibilityIcon from '@mui/icons-material/Visibility'; +import VisibilityOffIcon from '@mui/icons-material/VisibilityOff'; +import AccountCircleIcon from '@mui/icons-material/AccountCircle'; +import { useResponsive } from '../../theme/useResponsive'; + +const CreateAdminPage = () => { + const [username, setUsername] = useState(''); + const [password, setPassword] = useState(''); + const [confirmPassword, setConfirmPassword] = useState(''); + const [error, setError] = useState(''); + const [success, setSuccess] = useState(''); + const [loading, setLoading] = useState(false); + const [showPassword, setShowPassword] = useState(false); + const [showConfirmPassword, setShowConfirmPassword] = useState(false); + const { isMobile } = useResponsive(); + + const validateForm = () => { + if (!username || !password || !confirmPassword) { + setError('Заполните все поля'); + return false; + } + + if (password !== confirmPassword) { + setError('Пароли не совпадают'); + return false; + } + + if (password.length < 6) { + setError('Пароль должен содержать не менее 6 символов'); + return false; + } + + return true; + }; + + const handleCreateAdmin = async () => { + if (!validateForm()) return; + + setLoading(true); + setError(''); + setSuccess(''); + + try { + await createAdmin(username, password); + setSuccess('Администратор успешно создан'); + setUsername(''); + setPassword(''); + setConfirmPassword(''); + } catch (err) { + if (err instanceof Error && err.message.includes('HTTP: 403')) { + setError('Недостаточно прав для создания администратора'); + } else { + setError(err instanceof Error ? err.message : 'Произошла ошибка'); + } + } finally { + setLoading(false); + } + }; + + return ( + + + + + + Создание администратора + + + + + {error && ( + + {error} + + )} + + {success && ( + + {success} + + )} + + setUsername(e.target.value)} + InputProps={{ + startAdornment: ( + + + + ), + }} + sx={{ + mb: "1vw", + '& .MuiOutlinedInput-root': { + borderRadius: isMobile ? '5vw' : '2vw', + height: isMobile ? '12vw' : '4.5vw', + borderColor: '#C27664', + '& .MuiOutlinedInput-notchedOutline': { + borderColor: '#C27664', + }, + '&.Mui-focused .MuiOutlinedInput-notchedOutline': { + borderColor: '#C27664', + } + }, + '& .MuiInputLabel-root.Mui-focused': { + color: '#C27664', + } + }} + /> + + setPassword(e.target.value)} + InputProps={{ + startAdornment: ( + + + + ), + endAdornment: ( + + setShowPassword(!showPassword)} + edge="end" + sx={{ + color: "#C27664", + mr: "0vw" + }} + > + {showPassword ? : } + + + ) + }} + sx={{ + mb: "1vw", + '& .MuiOutlinedInput-root': { + borderRadius: isMobile ? '5vw' : '2vw', + height: isMobile ? '12vw' : '4.5vw', + borderColor: '#C27664', + '& .MuiOutlinedInput-notchedOutline': { + borderColor: '#C27664', + }, + '&.Mui-focused .MuiOutlinedInput-notchedOutline': { + borderColor: '#C27664', + } + }, + '& .MuiInputLabel-root.Mui-focused': { + color: '#C27664', + } + }} + /> + + setConfirmPassword(e.target.value)} + InputProps={{ + startAdornment: ( + + + + ), + endAdornment: ( + + setShowConfirmPassword(!showConfirmPassword)} + edge="end" + sx={{ + color: "#C27664", + mr: "0vw" + }} + > + {showConfirmPassword ? : } + + + ) + }} + sx={{ + mb: isMobile ? "6vw" : "2vw", + '& .MuiOutlinedInput-root': { + borderRadius: isMobile ? '5vw' : '2vw', + height: isMobile ? '12vw' : '4.5vw', + borderColor: '#C27664', + '& .MuiOutlinedInput-notchedOutline': { + borderColor: '#C27664', + }, + '&.Mui-focused .MuiOutlinedInput-notchedOutline': { + borderColor: '#C27664', + } + }, + '& .MuiInputLabel-root.Mui-focused': { + color: '#C27664', + } + }} + /> + + + + + + ); +}; + +export default CreateAdminPage; diff --git a/src/pages/admin/LoginPage.tsx b/src/pages/admin/LoginPage.tsx index f6550cf..b0e3f68 100644 --- a/src/pages/admin/LoginPage.tsx +++ b/src/pages/admin/LoginPage.tsx @@ -35,6 +35,9 @@ const LoginPage = () => { // Получение данных пользователя и проверка активности const userData = await getCurrentUser(accessToken); + if (userData.is_super_admin === true) { + localStorage.setItem('superadmin', "true"); + } // Проверка активности пользователя if (!userData.is_active) { diff --git a/src/utils/api.ts b/src/utils/api.ts index 4e0d087..75806f3 100644 --- a/src/utils/api.ts +++ b/src/utils/api.ts @@ -289,6 +289,7 @@ export interface UserData { id: number; username: string; is_active: boolean; + is_super_admin: boolean; } // Получение токена авторизации @@ -347,3 +348,28 @@ export const checkAuth = async (token: string): Promise => { return false; } }; + +// Создание нового администратора +export const createAdmin = async (username: string, password: string): Promise => { + try { + const response = await fetch(`${API_BASE_URL}/admins`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${localStorage.getItem('token')}` + }, + body: JSON.stringify({ + username, + password + }), + }); + + if (!response.ok) { + throw new Error(`Ошибка HTTP: ${response.status}`); + } + + return await response.json(); + } catch (error) { + return handleApiError(error); + } +};