diff --git a/package-lock.json b/package-lock.json index 01203b4..6478421 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,6 +22,9 @@ "socket.io": "^4.8.0", "socket.io-client": "^4.8.0", "web-vitals": "^2.1.4" + }, + "devDependencies": { + "sass": "^1.79.3" } }, "node_modules/@adobe/css-tools": { @@ -10356,6 +10359,13 @@ "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": { "version": "3.3.0", "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==", "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": { "version": "12.6.0", "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": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", diff --git a/package.json b/package.json index 4350e13..fb34334 100644 --- a/package.json +++ b/package.json @@ -41,5 +41,8 @@ "last 1 firefox version", "last 1 safari version" ] + }, + "devDependencies": { + "sass": "^1.79.3" } } diff --git a/src/App.js b/src/App.js index 804bd00..40ed20d 100644 --- a/src/App.js +++ b/src/App.js @@ -3,19 +3,37 @@ import DeviceGroups from "./pages/DeviceGroups"; import Devices from "./pages/Devices"; import Sidebar from "./Sidebar"; import Dashboard from './pages/Dashboard'; +import UserAccount from './pages/UserAccount'; // Импортируем компонент UserAccount +import Login from './pages/Login'; // Импортируем страницу логина +import Connections from './pages/Connections'; // Импортируем страницу подключений const App = () => { const [activeTab, setActiveTab] = useState('map'); // По умолчанию активная вкладка "Карта" + const [isAuthenticated, setIsAuthenticated] = useState(false); // Статус авторизации + + const handleLogin = () => { + setIsAuthenticated(true); // Устанавливаем авторизацию + }; + + const handleLogout = () => { + setIsAuthenticated(false); // Сбрасываем авторизацию + }; + + if (!isAuthenticated) { + return ; // Показываем страницу логина + } return ( -
- {/* Передаем активную вкладку */} -
- {activeTab === 'map' && } {/* Подключаем компонент Dashboard */} - {activeTab === 'connection' &&
Подключение
} - {activeTab === 'account' &&
Аккаунт пользователя
} - {activeTab === 'groups' && } {/* Группы устройств */} - {activeTab === 'devices' && } {/* Устройства */} +
+
+ {/* Передаем onLogout */} +
+ {activeTab === 'map' && } {/* Подключаем компонент Dashboard */} + {activeTab === 'connection' && } {/* Страница подключений */} + {activeTab === 'account' && } {/* Подключаем компонент UserAccount */} + {activeTab === 'groups' && } {/* Группы устройств */} + {activeTab === 'devices' && } {/* Устройства */} +
); diff --git a/src/Connections.scss b/src/Connections.scss new file mode 100644 index 0000000..f95be0f --- /dev/null +++ b/src/Connections.scss @@ -0,0 +1,34 @@ +.section { + margin-bottom: 20px; + + form { + input { + margin-right: 10px; + padding: 8px; + border-radius: 4px; + border: 1px solid #ddd; + } + + button { + padding: 8px 12px; + background-color: #2980b9; + color: white; + border: none; + border-radius: 4px; + cursor: pointer; + + &:hover { + background-color: #3498db; + } + } + } + + ul { + list-style-type: none; + padding: 0; + + li { + margin-top: 10px; + } + } + } \ No newline at end of file diff --git a/src/Login.scss b/src/Login.scss new file mode 100644 index 0000000..3daef89 --- /dev/null +++ b/src/Login.scss @@ -0,0 +1,70 @@ +// styles/Login.scss + +.login-container { + display: flex; + justify-content: center; + align-items: center; + height: 100vh; + background-color: #f0f0f0; + + .login-box { + background-color: #fff; + padding: 40px; + border-radius: 8px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + max-width: 400px; + width: 100%; + display: flex; + flex-direction: column; // Выровнять элементы по колонке + + h2 { + text-align: center; + margin-bottom: 20px; + color: #333; + } + + form { + display: flex; + flex-direction: column; + + .input-group { + display: flex; + flex-direction: column; + margin-bottom: 20px; + + label { + margin-bottom: 8px; + color: #666; + font-size: 14px; + } + + input { + padding: 10px; + border: 1px solid #ddd; + border-radius: 4px; + font-size: 16px; + transition: border-color 0.3s ease; + &:focus { + outline: none; + border-color: #007bff; + } + } + } + + button { + padding: 12px; + background-color: #007bff; + color: #fff; + border: none; + border-radius: 4px; + font-size: 16px; + cursor: pointer; + transition: background-color 0.3s ease; + + &:hover { + background-color: #0056b3; + } + } + } + } + } \ No newline at end of file diff --git a/src/Sidebar.js b/src/Sidebar.js index 1a288f0..e5304f2 100644 --- a/src/Sidebar.js +++ b/src/Sidebar.js @@ -1,7 +1,14 @@ -import React from 'react'; -import './Sidebar.css'; +import React, { useState } from 'react'; // Ensure useState is imported +import './Sidebar.css'; // Change this to Sidebar.scss if using SCSS + +const Sidebar = ({ onSelectTab, activeTab, onLogout }) => { + const [isSubMenuOpen, setIsSubMenuOpen] = useState(false); // For managing dropdown items + + const toggleSubMenu = () => { + setIsSubMenuOpen(!isSubMenuOpen); + onSelectTab('account'); // Keep the logic to display the account when clicking on "Аккаунт пользователя" + }; -const Sidebar = ({ onSelectTab, activeTab }) => { return (

Меню

@@ -12,14 +19,21 @@ const Sidebar = ({ onSelectTab, activeTab }) => {
  • onSelectTab('connection')}> Подключение
  • -
  • onSelectTab('account')}> +
  • Аккаунт пользователя
  • -
  • onSelectTab('groups')}> - Группы устройств -
  • -
  • onSelectTab('devices')}> - Устройства + {isSubMenuOpen && ( // Dropdown menu + <> +
  • onSelectTab('groups')}> + Группы устройств +
  • +
  • onSelectTab('devices')}> + Устройства +
  • + + )} +
  • + Выйти
  • diff --git a/src/UserAccount.scss b/src/UserAccount.scss new file mode 100644 index 0000000..eb5cf87 --- /dev/null +++ b/src/UserAccount.scss @@ -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); /* Легкий уменьшенный эффект при нажатии */ + } + } + } \ No newline at end of file diff --git a/src/pages/Connections.js b/src/pages/Connections.js new file mode 100644 index 0000000..edc243b --- /dev/null +++ b/src/pages/Connections.js @@ -0,0 +1,63 @@ +import React, { useState } from 'react'; +import '../Connections.scss'; + +const Connections = () => { + const [webhooks, setWebhooks] = useState([]); + const [websocketUrl, setWebsocketUrl] = useState(''); + + const addWebhook = (newWebhook) => { + setWebhooks([...webhooks, newWebhook]); + }; + + const handleWebhookSubmit = (e) => { + e.preventDefault(); + const newWebhook = { + url: e.target.webhookUrl.value, + groups: e.target.groups.value.split(','), + }; + addWebhook(newWebhook); + e.target.reset(); + }; + + return ( +
    +

    Подключения

    + + {/* Webhook подключение */} +
    +

    Подключения Webhooks

    +
    + + + +
    + +
      + {webhooks.map((webhook, index) => ( +
    • + Webhook: {webhook.url} (Группы: {webhook.groups.join(', ')}) +
    • + ))} +
    +
    + + {/* WebSocket подключение */} +
    +

    Подключения WebSockets

    +
    { + e.preventDefault(); + setWebsocketUrl(e.target.websocketUrl.value); + }} + > + + +
    + + {websocketUrl &&

    Подключено по WebSocket: {websocketUrl}

    } +
    +
    + ); +}; + +export default Connections; \ No newline at end of file diff --git a/src/pages/Login.js b/src/pages/Login.js new file mode 100644 index 0000000..a6b2673 --- /dev/null +++ b/src/pages/Login.js @@ -0,0 +1,52 @@ +import React, { useState } from 'react'; +import '../Login.scss'; + +const Login = ({ onLogin }) => { + const [username, setUsername] = useState(''); + const [password, setPassword] = useState(''); + const [error, setError] = useState(''); + + const handleSubmit = (e) => { + e.preventDefault(); + // Simple login/password check + if (username === 'admin' && password === 'admin') { + onLogin(); // Call function on successful login + } else { + setError('Неверный логин или пароль'); + } + }; + + return ( +
    +
    {/* Added a wrapper for styling */} +

    Вход

    + {error &&

    {error}

    } +
    +
    + + setUsername(e.target.value)} + /> +
    +
    + + setPassword(e.target.value)} + /> +
    + +
    +
    +
    + ); + }; + + export default Login; \ No newline at end of file diff --git a/src/pages/UserAccount.js b/src/pages/UserAccount.js new file mode 100644 index 0000000..0440cc8 --- /dev/null +++ b/src/pages/UserAccount.js @@ -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 ( +
    +

    Данные

    + + {isEditing ? ( +
    + + + +
    + + +
    +
    + ) : ( +
    +

    Имя: {userData.username}

    +

    Email: {userData.email}

    +

    Телефон: {userData.phone}

    + +
    + )} +
    + ); +}; + +export default UserAccount; \ No newline at end of file