From d13c98998f5c28178546f2459a0247df58d36f4e Mon Sep 17 00:00:00 2001 From: romantarkin Date: Tue, 22 Jul 2025 19:14:49 +0500 Subject: [PATCH] SideBar --- data/nginx/proxy_host/1.conf | 1 - frontend/src/components/Header.js | 15 ++++++ frontend/src/components/Header.module.css | 38 +++++++++++++++ frontend/src/components/SideMenu.js | 23 +++++++++ frontend/src/components/SideMenu.module.css | 53 +++++++++++++++++++++ frontend/src/context/UserContext.js | 42 ++++++++++++++++ frontend/src/index.js | 5 +- frontend/src/pages/Dashboard.js | 40 ++++++++++++++-- frontend/src/pages/Login.js | 5 +- 9 files changed, 214 insertions(+), 8 deletions(-) create mode 100644 frontend/src/components/Header.js create mode 100644 frontend/src/components/Header.module.css create mode 100644 frontend/src/components/SideMenu.js create mode 100644 frontend/src/components/SideMenu.module.css create mode 100644 frontend/src/context/UserContext.js diff --git a/data/nginx/proxy_host/1.conf b/data/nginx/proxy_host/1.conf index a904151..443b547 100644 --- a/data/nginx/proxy_host/1.conf +++ b/data/nginx/proxy_host/1.conf @@ -101,4 +101,3 @@ http2 off; # Custom include /data/nginx/custom/server_proxy[.]conf; } - diff --git a/frontend/src/components/Header.js b/frontend/src/components/Header.js new file mode 100644 index 0000000..1d66f4d --- /dev/null +++ b/frontend/src/components/Header.js @@ -0,0 +1,15 @@ +import React from 'react'; +import styles from './Header.module.css'; + +const Header = ({ user, onLogout }) => { + return ( +
+
+ {user?.email || 'Аккаунт'} + +
+
+ ); +}; + +export default Header; \ No newline at end of file diff --git a/frontend/src/components/Header.module.css b/frontend/src/components/Header.module.css new file mode 100644 index 0000000..95defd8 --- /dev/null +++ b/frontend/src/components/Header.module.css @@ -0,0 +1,38 @@ +.header { + width: 100%; + height: 64px; + background: #fff; + border-bottom: 1.5px solid #e5e7eb; + display: flex; + align-items: center; + justify-content: flex-end; + padding: 0 32px; + box-sizing: border-box; + position: sticky; + top: 0; + z-index: 10; +} +.right { + display: flex; + align-items: center; + gap: 18px; +} +.user { + font-size: 16px; + color: #6366f1; + font-weight: 600; +} +.logout { + background: linear-gradient(90deg, #6366f1 0%, #06b6d4 100%); + color: #fff; + border: none; + border-radius: 8px; + padding: 8px 18px; + font-size: 15px; + font-weight: 600; + cursor: pointer; + transition: background 0.2s; +} +.logout:hover { + background: linear-gradient(90deg, #3730a3 0%, #06b6d4 100%); +} \ No newline at end of file diff --git a/frontend/src/components/SideMenu.js b/frontend/src/components/SideMenu.js new file mode 100644 index 0000000..82a5247 --- /dev/null +++ b/frontend/src/components/SideMenu.js @@ -0,0 +1,23 @@ +import React from 'react'; +import styles from './SideMenu.module.css'; + +const SideMenu = ({ active, onSelect }) => { + return ( + + ); +}; + +export default SideMenu; \ No newline at end of file diff --git a/frontend/src/components/SideMenu.module.css b/frontend/src/components/SideMenu.module.css new file mode 100644 index 0000000..165b80f --- /dev/null +++ b/frontend/src/components/SideMenu.module.css @@ -0,0 +1,53 @@ +.menu { + width: 250px; + min-height: 100vh; + background: #f3f4f6; + border-right: 1.5px solid #e5e7eb; + display: flex; + flex-direction: column; + padding: 0 0 24px 0; +} +.project { + font-size: 22px; + font-weight: 700; + color: #3730a3; + padding: 32px 24px 16px 24px; + letter-spacing: 1px; + border-bottom: 1px solid #e5e7eb; + margin-bottom: 12px; +} +.nav { + flex: 1; + padding: 0 0 0 0; +} +.section { + font-size: 15px; + color: #6366f1; + font-weight: 600; + padding: 12px 24px 6px 24px; + text-transform: uppercase; + letter-spacing: 0.5px; +} +ul { + list-style: none; + margin: 0; + padding: 0 0 0 0; +} +li { + padding: 12px 24px; + font-size: 16px; + color: #374151; + cursor: pointer; + border-left: 3px solid transparent; + transition: background 0.15s, border 0.15s, color 0.15s; +} +li:hover { + background: #e0e7ff; + color: #3730a3; +} +.active { + background: #e0e7ff; + color: #3730a3; + border-left: 3px solid #6366f1; + font-weight: 600; +} \ No newline at end of file diff --git a/frontend/src/context/UserContext.js b/frontend/src/context/UserContext.js new file mode 100644 index 0000000..410e6c7 --- /dev/null +++ b/frontend/src/context/UserContext.js @@ -0,0 +1,42 @@ +import React, { createContext, useContext, useState, useEffect } from 'react'; + +const UserContext = createContext(); + +export function UserProvider({ children }) { + const [user, setUser] = useState(null); + const [token, setToken] = useState(null); + + useEffect(() => { + // Инициализация из localStorage + const storedUser = localStorage.getItem('user'); + const storedToken = localStorage.getItem('token'); + if (storedUser && storedToken) { + setUser(JSON.parse(storedUser)); + setToken(storedToken); + } + }, []); + + const login = (user, token) => { + setUser(user); + setToken(token); + localStorage.setItem('user', JSON.stringify(user)); + localStorage.setItem('token', token); + }; + + const logout = () => { + setUser(null); + setToken(null); + localStorage.removeItem('user'); + localStorage.removeItem('token'); + }; + + return ( + + {children} + + ); +} + +export function useUser() { + return useContext(UserContext); +} \ No newline at end of file diff --git a/frontend/src/index.js b/frontend/src/index.js index 0ebbc88..2b7667e 100644 --- a/frontend/src/index.js +++ b/frontend/src/index.js @@ -3,11 +3,14 @@ import ReactDOM from 'react-dom/client'; import './index.css'; import App from './App'; import * as serviceWorkerRegistration from './serviceWorkerRegistration'; +import { UserProvider } from './context/UserContext'; const root = ReactDOM.createRoot(document.getElementById('root')); root.render( - + + + ); diff --git a/frontend/src/pages/Dashboard.js b/frontend/src/pages/Dashboard.js index 89935ca..4d86f2b 100644 --- a/frontend/src/pages/Dashboard.js +++ b/frontend/src/pages/Dashboard.js @@ -1,12 +1,44 @@ -import React from 'react'; +import React, { useState } from 'react'; +import SideMenu from '../components/SideMenu'; +import Header from '../components/Header'; +import { useUser } from '../context/UserContext'; const Dashboard = () => { + const [active, setActive] = useState('smtp'); + const { user, logout } = useUser(); + + const handleLogout = () => { + logout(); + window.location.href = '/login'; + }; + return ( -
-

Добро пожаловать в Dashboard!

-

Вы успешно вошли в систему.

+
+ +
+
+
+ {/* Здесь будет контент выбранного раздела */} +

Раздел: {getSectionTitle(active)}

+
+ Здесь будет содержимое для "{getSectionTitle(active)}". +
+
+
); }; +function getSectionTitle(key) { + switch (key) { + case 'smtp': return 'SMTP-сервера'; + case 'template': return 'Шаблон письма'; + case 'unsubscribed': return 'Отписались'; + case 'groups': return 'Подписчики и группы'; + case 'history': return 'История отправок'; + case 'campaign': return 'Кампания'; + default: return ''; + } +} + export default Dashboard; \ No newline at end of file diff --git a/frontend/src/pages/Login.js b/frontend/src/pages/Login.js index eb452dc..f36a52e 100644 --- a/frontend/src/pages/Login.js +++ b/frontend/src/pages/Login.js @@ -1,6 +1,7 @@ import React, { useState } from 'react'; import { useNavigate } from 'react-router-dom'; import styles from './Login.module.css'; +import { useUser } from '../context/UserContext'; const Login = () => { const [email, setEmail] = useState(''); @@ -8,6 +9,7 @@ const Login = () => { const [error, setError] = useState(''); const [loading, setLoading] = useState(false); const navigate = useNavigate(); + const { login } = useUser(); const handleSubmit = async (e) => { e.preventDefault(); @@ -23,8 +25,7 @@ const Login = () => { if (!res.ok) { setError(data.error || 'Ошибка входа'); } else { - localStorage.setItem('token', data.token); - // Можно сохранить и user, если нужно: localStorage.setItem('user', JSON.stringify(data.user)); + login(data.user, data.token); navigate('/dashboard'); } } catch (err) {