Main GUI screen and service for server-side logic
This commit is contained in:
parent
a7d936fff6
commit
fc949f83a8
1
.env
1
.env
@ -1,5 +1,6 @@
|
|||||||
SERVER_BASE_ADDRESS=0.0.0.0:8080
|
SERVER_BASE_ADDRESS=0.0.0.0:8080
|
||||||
|
|
||||||
|
|
||||||
MONGO_INITDB_ROOT_USERNAME=moxitech
|
MONGO_INITDB_ROOT_USERNAME=moxitech
|
||||||
MONGO_INITDB_ROOT_PASSWORD=moxitech
|
MONGO_INITDB_ROOT_PASSWORD=moxitech
|
||||||
MONGO_INITDB_DATABASE=drone-network-simulator
|
MONGO_INITDB_DATABASE=drone-network-simulator
|
||||||
|
BIN
src/frontend/public/blueprints.jpg
Normal file
BIN
src/frontend/public/blueprints.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 57 KiB |
@ -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 Main from './pages/Main';
|
||||||
import UserAccount from './pages/UserAccount'; // Импортируем компонент UserAccount
|
import UserAccount from './pages/UserAccount'; // Импортируем компонент UserAccount
|
||||||
import Login from './pages/Login'; // Импортируем страницу логина
|
import Login from './pages/Login'; // Импортируем страницу логина
|
||||||
import Connections from './pages/Connections'; // Импортируем страницу подключений
|
import Connections from './pages/Connections'; // Импортируем страницу подключений
|
||||||
@ -13,7 +14,7 @@ import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-d
|
|||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
const [isLoggedIn, setIsLoggedIn] = useState(false); // Статус авторизации
|
const [isLoggedIn, setIsLoggedIn] = useState(false); // Статус авторизации
|
||||||
const [activeTab, setActiveTab] = useState('map'); // По умолчанию активная вкладка "Карта"
|
const [activeTab, setActiveTab] = useState('main'); // По умолчанию активная вкладка "Карта"
|
||||||
|
|
||||||
// Функция для выхода из системы
|
// Функция для выхода из системы
|
||||||
const handleLogout = () => {
|
const handleLogout = () => {
|
||||||
@ -42,6 +43,7 @@ const App = () => {
|
|||||||
onLogout={handleLogout} // Передаем функцию handleLogout через пропс onLogout
|
onLogout={handleLogout} // Передаем функцию handleLogout через пропс onLogout
|
||||||
/>
|
/>
|
||||||
<div style={{ padding: '20px', flex: 1 }}>
|
<div style={{ padding: '20px', flex: 1 }}>
|
||||||
|
{activeTab === 'main' && <Main />} {/* Подключаем компонент Dashboard */}
|
||||||
{activeTab === 'map' && <Dashboard />} {/* Подключаем компонент Dashboard */}
|
{activeTab === 'map' && <Dashboard />} {/* Подключаем компонент Dashboard */}
|
||||||
{activeTab === 'connection' && <Connections />} {/* Страница подключений */}
|
{activeTab === 'connection' && <Connections />} {/* Страница подключений */}
|
||||||
{activeTab === 'account' && <UserAccount />} {/* Подключаем компонент UserAccount */}
|
{activeTab === 'account' && <UserAccount />} {/* Подключаем компонент UserAccount */}
|
||||||
|
9
src/frontend/src/Main.css
Normal file
9
src/frontend/src/Main.css
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
|
||||||
|
.main__imager {
|
||||||
|
height: 100%;
|
||||||
|
background-image: url('../public/blueprints.jpg');
|
||||||
|
}
|
||||||
|
|
||||||
|
.main__t_b {
|
||||||
|
color: black;
|
||||||
|
}
|
@ -16,9 +16,12 @@ const Sidebar = ({ onSelectTab, activeTab, onLogout }) => {
|
|||||||
<div className="sidebar">
|
<div className="sidebar">
|
||||||
<h2>Меню</h2>
|
<h2>Меню</h2>
|
||||||
<ul>
|
<ul>
|
||||||
<li className={activeTab === 'map' ? 'active' : ''} onClick={() => onSelectTab('map')}>
|
<li className={activeTab === 'main' ? 'active' : ''} onClick={() => onSelectTab('main')}>
|
||||||
Главная
|
Главная
|
||||||
</li>
|
</li>
|
||||||
|
<li className={activeTab === 'map' ? 'active' : ''} onClick={() => onSelectTab('map')}>
|
||||||
|
Сессия 1
|
||||||
|
</li>
|
||||||
<li className={activeTab === 'connection' ? 'active' : ''} onClick={() => onSelectTab('connection')}>
|
<li className={activeTab === 'connection' ? 'active' : ''} onClick={() => onSelectTab('connection')}>
|
||||||
Настройки
|
Настройки
|
||||||
</li>
|
</li>
|
||||||
|
@ -41,16 +41,16 @@ const DeviceGroups = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="device-groups">
|
<div className="device-groups">
|
||||||
<h2>Группы устройств</h2>
|
<h2>Шаблоны / СШК</h2>
|
||||||
|
|
||||||
<div className="group-creation">
|
<div className="group-creation">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={newGroupName}
|
value={newGroupName}
|
||||||
onChange={(e) => setNewGroupName(e.target.value)}
|
onChange={(e) => setNewGroupName(e.target.value)}
|
||||||
placeholder="Название новой группы"
|
placeholder="Название шаблона"
|
||||||
/>
|
/>
|
||||||
<button onClick={createGroup}>Создать группу</button>
|
<button onClick={createGroup}>Создать шаблон</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
|
@ -3,91 +3,19 @@ import { QRCodeCanvas } from "qrcode.react";
|
|||||||
import '../Devices.css';
|
import '../Devices.css';
|
||||||
|
|
||||||
const Devices = () => {
|
const Devices = () => {
|
||||||
const [devices, setDevices] = useState([]);
|
|
||||||
const [selectedDevice, setSelectedDevice] = useState(null);
|
|
||||||
const [deviceName, setDeviceName] = useState('');
|
|
||||||
const [deviceStatus, setDeviceStatus] = useState('');
|
|
||||||
|
|
||||||
const addDevice = () => {
|
|
||||||
if (deviceName) {
|
|
||||||
const newDevice = { name: deviceName, status: deviceStatus || 'Неактивен', id: Date.now() };
|
|
||||||
setDevices([...devices, newDevice]);
|
|
||||||
setDeviceName('');
|
|
||||||
setDeviceStatus('');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const removeDevice = () => {
|
|
||||||
if (selectedDevice) {
|
|
||||||
setDevices(devices.filter(device => device.id !== selectedDevice.id));
|
|
||||||
setSelectedDevice(null);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const editDevice = () => {
|
|
||||||
if (selectedDevice) {
|
|
||||||
const updatedDevices = devices.map(device =>
|
|
||||||
device.id === selectedDevice.id
|
|
||||||
? { ...device, name: deviceName, status: deviceStatus }
|
|
||||||
: device
|
|
||||||
);
|
|
||||||
setDevices(updatedDevices);
|
|
||||||
setSelectedDevice(null);
|
|
||||||
setDeviceName('');
|
|
||||||
setDeviceStatus('');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const generateQR = (device) => {
|
|
||||||
return (
|
|
||||||
<QRCodeCanvas value={JSON.stringify(device)} size={128} />
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="devices">
|
<div>
|
||||||
<h2>Устройства</h2>
|
<button className='button is-danger'>Удалить неиспользуемые шаблоны</button>
|
||||||
<input
|
<hr/>
|
||||||
type="text"
|
<button className='button is-danger'>Удалить неактивных пользователей</button>
|
||||||
placeholder="Название устройства"
|
<hr/>
|
||||||
value={deviceName}
|
<button className='button is-danger'>Перезагрузить сервер</button>
|
||||||
onChange={(e) => setDeviceName(e.target.value)}
|
<hr/>
|
||||||
/>
|
|
||||||
<input
|
<button className='button is-danger'>Перезагрузить фронтенд</button>
|
||||||
type="text"
|
|
||||||
placeholder="Статус устройства"
|
|
||||||
value={deviceStatus}
|
|
||||||
onChange={(e) => setDeviceStatus(e.target.value)}
|
|
||||||
/>
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Название</th>
|
|
||||||
<th>Статус</th>
|
|
||||||
<th>QR Код</th>
|
|
||||||
<th>Действия</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{devices.map((device) => (
|
|
||||||
<tr key={device.id} onClick={() => setSelectedDevice(device)}>
|
|
||||||
<td>{device.name}</td>
|
|
||||||
<td>{device.status}</td>
|
|
||||||
<td>{generateQR(device)}</td>
|
|
||||||
<td>
|
|
||||||
<button onClick={editDevice}>Редактировать</button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<div className="buttons">
|
|
||||||
<button onClick={addDevice}>Добавить</button>
|
|
||||||
<button onClick={removeDevice} disabled={!selectedDevice}>Удалить выбранное</button>
|
|
||||||
<button onClick={editDevice} disabled={!selectedDevice}>Сохранить изменения</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Devices;
|
export default Devices;
|
35
src/frontend/src/pages/Main.js
Normal file
35
src/frontend/src/pages/Main.js
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import '../Main.css';
|
||||||
|
import { InfoBlock } from './components/DroneAssets/InfoBlock';
|
||||||
|
|
||||||
|
const Main = () => {
|
||||||
|
|
||||||
|
const makeSimulation = () => {
|
||||||
|
// POST $BASE_ADDR/createSimulation?userToken=?
|
||||||
|
}
|
||||||
|
|
||||||
|
const connectSimulation = () => {
|
||||||
|
// POST $BASE_ADDR/connectSimulation?userToken=?
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='main__imager'>
|
||||||
|
<InfoBlock />
|
||||||
|
<div class="columns ">
|
||||||
|
<div class="column">
|
||||||
|
<h1 class="title">Создать новую симуляцию</h1>
|
||||||
|
<button class="button">Create</button>
|
||||||
|
</div>
|
||||||
|
<div class="column">
|
||||||
|
<h1 class="title">Присоединиться к моделированию</h1>
|
||||||
|
<div>
|
||||||
|
<p>Расчет полета до камчатки</p>
|
||||||
|
<button class="button">Connect</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Main;
|
@ -2,83 +2,25 @@ import React, { useState } from 'react';
|
|||||||
import '../UserAccount.scss';
|
import '../UserAccount.scss';
|
||||||
|
|
||||||
const PrevCalc = () => {
|
const PrevCalc = () => {
|
||||||
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 (
|
return (
|
||||||
<div className="user-account">
|
<table class="table">
|
||||||
<h1>Данные</h1>
|
<thead>
|
||||||
|
<th><abbr title="Уникальный идентификатор в базе данных">UID</abbr></th>
|
||||||
{isEditing ? (
|
<th><abbr title="Название">Имя</abbr></th>
|
||||||
<div className="edit-form">
|
<th>Пользователь</th>
|
||||||
<label>
|
<th>Время начала</th>
|
||||||
Имя пользователя:
|
<th>Время конца</th>
|
||||||
<input
|
</thead>
|
||||||
type="text"
|
<tbody>
|
||||||
name="username"
|
<th>1</th>
|
||||||
value={formData.username}
|
<td>Калькуляция полета до варшавы</td>
|
||||||
onChange={handleChange}
|
<td>Гитлер</td>
|
||||||
/>
|
<td>1:00</td>
|
||||||
</label>
|
<td>4:04</td>
|
||||||
<label>
|
</tbody>
|
||||||
Никнейм авторизации:
|
</table>
|
||||||
<input
|
)
|
||||||
type="text"
|
|
||||||
name="email"
|
|
||||||
value={formData.email}
|
|
||||||
onChange={handleChange}
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<label>
|
|
||||||
Пароль:
|
|
||||||
<input
|
|
||||||
type="password"
|
|
||||||
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 PrevCalc;
|
export default PrevCalc;
|
@ -3,8 +3,8 @@ import '../UserAccount.scss';
|
|||||||
|
|
||||||
const UserAccount = () => {
|
const UserAccount = () => {
|
||||||
const [userData, setUserData] = useState({
|
const [userData, setUserData] = useState({
|
||||||
username: 'Мамут Рахал',
|
username: 'Никита Николаевич',
|
||||||
email: 'yatupoidayn@mail.ru',
|
email: 'moxitech@moxitech.com',
|
||||||
phone: '+666',
|
phone: '+666',
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -30,7 +30,7 @@ const UserAccount = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="user-account">
|
<div className=" user-account ">
|
||||||
<h1>Данные</h1>
|
<h1>Данные</h1>
|
||||||
|
|
||||||
{isEditing ? (
|
{isEditing ? (
|
||||||
|
35
src/frontend/src/pages/components/DroneAssets/InfoBlock.js
Normal file
35
src/frontend/src/pages/components/DroneAssets/InfoBlock.js
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
|
||||||
|
import React from "react"
|
||||||
|
|
||||||
|
export const InfoBlock = () => {
|
||||||
|
return(
|
||||||
|
|
||||||
|
|
||||||
|
<nav className="level">
|
||||||
|
<div className="level-item has-text-centered">
|
||||||
|
<div>
|
||||||
|
<p className="heading">Активных сессий</p>
|
||||||
|
<p className="title">0</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="level-item has-text-centered">
|
||||||
|
<div>
|
||||||
|
<p className="heading">Горутин</p>
|
||||||
|
<p className="title">123</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="level-item has-text-centered">
|
||||||
|
<div>
|
||||||
|
<p className="heading">Нагрузка сервера</p>
|
||||||
|
<p className="title">456K</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="level-item has-text-centered">
|
||||||
|
<div>
|
||||||
|
<p className="heading">Объем хранилища</p>
|
||||||
|
<p className="title">789</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
)
|
||||||
|
}
|
10
src/frontend/src/pages/service/Local.js
Normal file
10
src/frontend/src/pages/service/Local.js
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export const LocalSimulationJson = () => {
|
||||||
|
|
||||||
|
|
||||||
|
return {
|
||||||
|
"TODO": "JSON RETURN"
|
||||||
|
}
|
||||||
|
}
|
40
src/frontend/src/pages/service/Server.js
Normal file
40
src/frontend/src/pages/service/Server.js
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export const giveMeServerAddress = () => {
|
||||||
|
if (process.env.SERVER_BASE_ADDRESS) {
|
||||||
|
return process.env.SERVER_BASE_ADDRESS
|
||||||
|
}
|
||||||
|
return "http://localhost:3000";
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ServerRequest = async (req, type, data = null) => {
|
||||||
|
let addr = giveMeServerAddress(); // получаем адрес сервера
|
||||||
|
let options = {
|
||||||
|
method: type, // тип запроса (GET, POST, PUT, DELETE и т.д.)
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json', // заголовки для JSON данных
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Если данные есть и метод не GET, добавляем тело запроса
|
||||||
|
if (data && type !== 'GET') {
|
||||||
|
options.body = JSON.stringify(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${addr}/${req}`, options); // выполняем запрос
|
||||||
|
const responseData = await response.json(); // парсим JSON ответ
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: responseData, // данные от сервера
|
||||||
|
status: response.status // код состояния ответа
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error:', error);
|
||||||
|
return {
|
||||||
|
data: null, // если ошибка, данных нет
|
||||||
|
status: 500 // возвращаем код ошибки сервера
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
21
src/frontend/src/pages/service/SimulationRequests.js
Normal file
21
src/frontend/src/pages/service/SimulationRequests.js
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
|
||||||
|
import {ServerRequest} from './Server'
|
||||||
|
|
||||||
|
export const RequestSaveOnServer = () => {
|
||||||
|
// Запрос на сохранение результатов на сервере
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export const RequestRunCalculations = () => {
|
||||||
|
// Запрос на сохранение результатов на запуск вычислений
|
||||||
|
}
|
||||||
|
|
||||||
|
export const RequestGetClientSettings = () => {
|
||||||
|
// Запрос на получение клиентских настроек
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export const RequestUpdateClientSettings = () => {
|
||||||
|
// Запрос на обновление клиентских настроек
|
||||||
|
|
||||||
|
}
|
@ -57,7 +57,7 @@ func CreateSocket(c *websocket.Conn) {
|
|||||||
fmt.Printf("Received message from user %s: %s\n", userToken, msg)
|
fmt.Printf("Received message from user %s: %s\n", userToken, msg)
|
||||||
|
|
||||||
// Отправляем обратно сообщение клиенту
|
// Отправляем обратно сообщение клиенту
|
||||||
err = c.WriteMessage(websocket.BinaryMessage, msg)
|
err = c.WriteMessage(websocket.TextMessage, msg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Error writing message: %v\n", err)
|
fmt.Printf("Error writing message: %v\n", err)
|
||||||
break
|
break
|
||||||
|
Loading…
Reference in New Issue
Block a user