glist #3
58
package-lock.json
generated
58
package-lock.json
generated
@ -22,6 +22,9 @@
|
|||||||
"socket.io": "^4.8.0",
|
"socket.io": "^4.8.0",
|
||||||
"socket.io-client": "^4.8.0",
|
"socket.io-client": "^4.8.0",
|
||||||
"web-vitals": "^2.1.4"
|
"web-vitals": "^2.1.4"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"sass": "^1.79.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@adobe/css-tools": {
|
"node_modules/@adobe/css-tools": {
|
||||||
@ -10356,6 +10359,13 @@
|
|||||||
"url": "https://opencollective.com/immer"
|
"url": "https://opencollective.com/immer"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/immutable": {
|
||||||
|
"version": "4.3.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz",
|
||||||
|
"integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==",
|
||||||
|
"devOptional": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/import-fresh": {
|
"node_modules/import-fresh": {
|
||||||
"version": "3.3.0",
|
"version": "3.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
|
||||||
@ -17053,6 +17063,24 @@
|
|||||||
"integrity": "sha512-ZRwKbh/eQ6w9vmTjkuG0Ioi3HBwPFce0O+v//ve+aOq1oeCy7jMV2qzzAlpsNuqpqCBjjriM1lbtZbF/Q8jVyA==",
|
"integrity": "sha512-ZRwKbh/eQ6w9vmTjkuG0Ioi3HBwPFce0O+v//ve+aOq1oeCy7jMV2qzzAlpsNuqpqCBjjriM1lbtZbF/Q8jVyA==",
|
||||||
"license": "CC0-1.0"
|
"license": "CC0-1.0"
|
||||||
},
|
},
|
||||||
|
"node_modules/sass": {
|
||||||
|
"version": "1.79.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/sass/-/sass-1.79.3.tgz",
|
||||||
|
"integrity": "sha512-m7dZxh0W9EZ3cw50Me5GOuYm/tVAJAn91SUnohLRo9cXBixGUOdvmryN+dXpwR831bhoY3Zv7rEFt85PUwTmzA==",
|
||||||
|
"devOptional": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"chokidar": "^4.0.0",
|
||||||
|
"immutable": "^4.0.0",
|
||||||
|
"source-map-js": ">=0.6.2 <2.0.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"sass": "sass.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/sass-loader": {
|
"node_modules/sass-loader": {
|
||||||
"version": "12.6.0",
|
"version": "12.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.6.0.tgz",
|
||||||
@ -17091,6 +17119,36 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/sass/node_modules/chokidar": {
|
||||||
|
"version": "4.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.1.tgz",
|
||||||
|
"integrity": "sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==",
|
||||||
|
"devOptional": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"readdirp": "^4.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 14.16.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://paulmillr.com/funding/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/sass/node_modules/readdirp": {
|
||||||
|
"version": "4.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.1.tgz",
|
||||||
|
"integrity": "sha512-GkMg9uOTpIWWKbSsgwb5fA4EavTR+SG/PMPoAY8hkhHfEEY0/vqljY+XHqtDf2cr2IJtoNRDbrrEpZUiZCkYRw==",
|
||||||
|
"devOptional": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 14.16.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://paulmillr.com/funding/"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/sax": {
|
"node_modules/sax": {
|
||||||
"version": "1.2.4",
|
"version": "1.2.4",
|
||||||
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
|
||||||
|
@ -41,5 +41,8 @@
|
|||||||
"last 1 firefox version",
|
"last 1 firefox version",
|
||||||
"last 1 safari version"
|
"last 1 safari version"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"sass": "^1.79.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ import DeviceGroups from "./pages/DeviceGroups";
|
|||||||
import Devices from "./pages/Devices";
|
import Devices from "./pages/Devices";
|
||||||
import Sidebar from "./Sidebar";
|
import Sidebar from "./Sidebar";
|
||||||
import Dashboard from './pages/Dashboard';
|
import Dashboard from './pages/Dashboard';
|
||||||
|
import UserAccount from './pages/UserAccount'; // Импортируем страницу аккаунта
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
const [activeTab, setActiveTab] = useState('map'); // По умолчанию активная вкладка "Карта"
|
const [activeTab, setActiveTab] = useState('map'); // По умолчанию активная вкладка "Карта"
|
||||||
@ -13,7 +14,7 @@ const App = () => {
|
|||||||
<div style={{ padding: '20px', flex: 1 }}>
|
<div style={{ padding: '20px', flex: 1 }}>
|
||||||
{activeTab === 'map' && <Dashboard />} {/* Подключаем компонент Dashboard */}
|
{activeTab === 'map' && <Dashboard />} {/* Подключаем компонент Dashboard */}
|
||||||
{activeTab === 'connection' && <div>Подключение</div>}
|
{activeTab === 'connection' && <div>Подключение</div>}
|
||||||
{activeTab === 'account' && <div>Аккаунт пользователя</div>}
|
{activeTab === 'account' && <UserAccount />} {/* Подключаем компонент аккаунта */}
|
||||||
{activeTab === 'groups' && <DeviceGroups />} {/* Группы устройств */}
|
{activeTab === 'groups' && <DeviceGroups />} {/* Группы устройств */}
|
||||||
{activeTab === 'devices' && <Devices />} {/* Устройства */}
|
{activeTab === 'devices' && <Devices />} {/* Устройства */}
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,7 +1,13 @@
|
|||||||
import React from 'react';
|
import React, { useState } from 'react';
|
||||||
import './Sidebar.css';
|
import './Sidebar.css';
|
||||||
|
|
||||||
const Sidebar = ({ onSelectTab, activeTab }) => {
|
const Sidebar = ({ onSelectTab, activeTab }) => {
|
||||||
|
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
|
||||||
|
|
||||||
|
const toggleDropdown = () => {
|
||||||
|
setIsDropdownOpen(!isDropdownOpen); // Переключаем состояние выпадающего списка
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="sidebar">
|
<div className="sidebar">
|
||||||
<h2>Меню</h2>
|
<h2>Меню</h2>
|
||||||
@ -12,15 +18,26 @@ const Sidebar = ({ onSelectTab, activeTab }) => {
|
|||||||
<li className={activeTab === 'connection' ? 'active' : ''} onClick={() => onSelectTab('connection')}>
|
<li className={activeTab === 'connection' ? 'active' : ''} onClick={() => onSelectTab('connection')}>
|
||||||
Подключение
|
Подключение
|
||||||
</li>
|
</li>
|
||||||
<li className={activeTab === 'account' ? 'active' : ''} onClick={() => onSelectTab('account')}>
|
<li
|
||||||
|
className={activeTab === 'account' ? 'active' : ''}
|
||||||
|
onClick={() => {
|
||||||
|
onSelectTab('account');
|
||||||
|
toggleDropdown();
|
||||||
|
}}
|
||||||
|
>
|
||||||
Аккаунт пользователя
|
Аккаунт пользователя
|
||||||
</li>
|
</li>
|
||||||
|
{/* Выпадающий список для групп и устройств */}
|
||||||
|
{isDropdownOpen && (
|
||||||
|
<>
|
||||||
<li className={activeTab === 'groups' ? 'active' : ''} onClick={() => onSelectTab('groups')}>
|
<li className={activeTab === 'groups' ? 'active' : ''} onClick={() => onSelectTab('groups')}>
|
||||||
Группы устройств
|
Группы устройств
|
||||||
</li>
|
</li>
|
||||||
<li className={activeTab === 'devices' ? 'active' : ''} onClick={() => onSelectTab('devices')}>
|
<li className={activeTab === 'devices' ? 'active' : ''} onClick={() => onSelectTab('devices')}>
|
||||||
Устройства
|
Устройства
|
||||||
</li>
|
</li>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
87
src/UserAccount.scss
Normal file
87
src/UserAccount.scss
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
.user-account {
|
||||||
|
padding: 20px;
|
||||||
|
background-color: #f4f4f4;
|
||||||
|
border-radius: 10px;
|
||||||
|
max-width: 400px;
|
||||||
|
margin: 0 auto;
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-info, .edit-form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
|
||||||
|
label {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
padding: 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
font-size: 18px;
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.buttons {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
button {
|
||||||
|
padding: 10px 20px;
|
||||||
|
font-size: 14px;
|
||||||
|
cursor: pointer;
|
||||||
|
border: none;
|
||||||
|
border-radius: 5px;
|
||||||
|
background-color: #3498db;
|
||||||
|
color: white;
|
||||||
|
transition: background-color 0.3s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #2980b9;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
background-color: #e74c3c;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #c0392b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit-button {
|
||||||
|
margin-top: 20px;
|
||||||
|
padding: 10px 25px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: #2ecc71; /* Зелёный цвет */
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 5px;
|
||||||
|
transition: background-color 0.3s ease, transform 0.2s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #27ae60;
|
||||||
|
transform: scale(1.05); /* Небольшой зум при наведении */
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background-color: #1e8449;
|
||||||
|
transform: scale(0.98); /* Легкий уменьшенный эффект при нажатии */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
42
src/pages/Login.js
Normal file
42
src/pages/Login.js
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import './Login.css';
|
||||||
|
|
||||||
|
const Login = ({ onLogin }) => {
|
||||||
|
const [username, setUsername] = useState('');
|
||||||
|
const [password, setPassword] = useState('');
|
||||||
|
const [error, setError] = useState('');
|
||||||
|
|
||||||
|
const handleSubmit = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
// Простейшая проверка логина/пароля
|
||||||
|
if (username === 'admin' && password === 'admin') {
|
||||||
|
onLogin(); // Вызываем функцию при успешной авторизации
|
||||||
|
} else {
|
||||||
|
setError('Неверный логин или пароль');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="login-container">
|
||||||
|
<form onSubmit={handleSubmit}>
|
||||||
|
<h2>Вход</h2>
|
||||||
|
{error && <p className="error">{error}</p>}
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Логин"
|
||||||
|
value={username}
|
||||||
|
onChange={(e) => setUsername(e.target.value)}
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
placeholder="Пароль"
|
||||||
|
value={password}
|
||||||
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
|
/>
|
||||||
|
<button type="submit">Войти</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Login;
|
84
src/pages/UserAccount.js
Normal file
84
src/pages/UserAccount.js
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import '../UserAccount.scss';
|
||||||
|
|
||||||
|
const UserAccount = () => {
|
||||||
|
const [userData, setUserData] = useState({
|
||||||
|
username: 'Мамут Рахал',
|
||||||
|
email: 'yatupoidayn@mail.ru',
|
||||||
|
phone: '+666',
|
||||||
|
});
|
||||||
|
|
||||||
|
const [isEditing, setIsEditing] = useState(false);
|
||||||
|
const [formData, setFormData] = useState(userData);
|
||||||
|
|
||||||
|
const handleChange = (e) => {
|
||||||
|
const { name, value } = e.target;
|
||||||
|
setFormData((prevData) => ({
|
||||||
|
...prevData,
|
||||||
|
[name]: value,
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSave = () => {
|
||||||
|
setUserData(formData);
|
||||||
|
setIsEditing(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCancel = () => {
|
||||||
|
setFormData(userData);
|
||||||
|
setIsEditing(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="user-account">
|
||||||
|
<h1>Данные</h1>
|
||||||
|
|
||||||
|
{isEditing ? (
|
||||||
|
<div className="edit-form">
|
||||||
|
<label>
|
||||||
|
Имя:
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="username"
|
||||||
|
value={formData.username}
|
||||||
|
onChange={handleChange}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
Email:
|
||||||
|
<input
|
||||||
|
type="email"
|
||||||
|
name="email"
|
||||||
|
value={formData.email}
|
||||||
|
onChange={handleChange}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
Телефон:
|
||||||
|
<input
|
||||||
|
type="tel"
|
||||||
|
name="phone"
|
||||||
|
value={formData.phone}
|
||||||
|
onChange={handleChange}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<div className="buttons">
|
||||||
|
<button onClick={handleSave}>Сохранить</button>
|
||||||
|
<button onClick={handleCancel}>Отмена</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="user-info">
|
||||||
|
<p><strong>Имя:</strong> {userData.username}</p>
|
||||||
|
<p><strong>Email:</strong> {userData.email}</p>
|
||||||
|
<p><strong>Телефон:</strong> {userData.phone}</p>
|
||||||
|
<button className="edit-button" onClick={() => setIsEditing(true)}>
|
||||||
|
Редактировать
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default UserAccount;
|
Loading…
Reference in New Issue
Block a user