diff --git a/.gitignore b/.gitignore index 4d29575..e9f1cd7 100644 --- a/.gitignore +++ b/.gitignore @@ -2,22 +2,22 @@ # dependencies /node_modules -/.pnp -.pnp.js +# /.pnp +# .pnp.js # testing -/coverage +# /coverage # production -/build +# /build # misc -.DS_Store -.env.local -.env.development.local -.env.test.local -.env.production.local +# .DS_Store +# .env.local +# .env.development.local +# .env.test.local +# .env.production.local -npm-debug.log* -yarn-debug.log* -yarn-error.log* +# npm-debug.log* +# yarn-debug.log* +# yarn-error.log* diff --git a/src/App.css b/src/App.css index 74b5e05..b036241 100644 --- a/src/App.css +++ b/src/App.css @@ -1,38 +1,11 @@ -.App { - text-align: center; +body { + width: 100%; + height: 100%; + background-color: rgb(44, 44, 44); } -.App-logo { - height: 40vmin; - pointer-events: none; -} - -@media (prefers-reduced-motion: no-preference) { - .App-logo { - animation: App-logo-spin infinite 20s linear; - } -} - -.App-header { - background-color: #282c34; - min-height: 100vh; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - font-size: calc(10px + 2vmin); - color: white; -} - -.App-link { - color: #61dafb; -} - -@keyframes App-logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} +#root { + width: 100%; + display: flex; + justify-content: center; +} \ No newline at end of file diff --git a/src/App.js b/src/App.js deleted file mode 100644 index 3784575..0000000 --- a/src/App.js +++ /dev/null @@ -1,25 +0,0 @@ -import logo from './logo.svg'; -import './App.css'; - -function App() { - return ( -
-
- logo -

- Edit src/App.js and save to reload. -

- - Learn React - -
-
- ); -} - -export default App; diff --git a/src/App.jsx b/src/App.jsx new file mode 100644 index 0000000..4522f66 --- /dev/null +++ b/src/App.jsx @@ -0,0 +1,8 @@ +import './App.css'; +import { MainPage } from './pages/Home.jsx'; + +export default function App() { + return ( + + ); +} \ No newline at end of file diff --git a/src/App.test.js b/src/App.test.js deleted file mode 100644 index 1f03afe..0000000 --- a/src/App.test.js +++ /dev/null @@ -1,8 +0,0 @@ -import { render, screen } from '@testing-library/react'; -import App from './App'; - -test('renders learn react link', () => { - render(); - const linkElement = screen.getByText(/learn react/i); - expect(linkElement).toBeInTheDocument(); -}); diff --git a/src/components/Client/Client.css b/src/components/Client/Client.css new file mode 100644 index 0000000..88ef6e3 --- /dev/null +++ b/src/components/Client/Client.css @@ -0,0 +1,104 @@ +.acc-icon { + border-radius: 30px; + background-image: 'client_icon.svg'; +} + +.circle { + width: 40px; + height: 40px; + border: 2px solid none; + border-radius: 50%; + background: #e1e1e1; + display: flex; +} + +.circle svg { + margin: auto; +} + +.device { + height: 90px; + display: flex; + align-items: center; + justify-content: space-between; + background-color: #333; + color: white; + padding: 10px; + border-top: rgb(94, 93, 93) 1px solid; + flex-wrap: wrap; +} + +.device .info { + width: 270px; + display: flex; + align-items: center; + justify-content: space-around; +} + +.device .avatar { + width: 40px; + height: 40px; + border-radius: 50%; + margin-right: 10px; +} + +.device .ip { + color: #959393; + font-size: 0.9rem; +} + +.device .details .name { + font-weight: bold; +} + +.info .lst-time { + font-size: 0.8rem; + color: #959393; + align-self: self-end; +} + +.device .data { + display: flex; + align-items: center; +} + +.device .data div { + display: flex; + flex-direction: column; + margin: 0 10px; +} + +.data .total { + color: #959393; + font-size: 0.8rem; +} + +.device .controls { + display: flex; + align-items: center; +} + +.device .controls button { + background: none; + border: none; + width: 45px; + height: 45px; + cursor: pointer; + padding: 5px 10px; + margin: 0 10px; + background-color: rgb(94, 93, 93); + border-radius: 30%; +} + +.device .controls button:hover { + background-color: #830202; +} + +.device .toggle.red { + background-color: red; + border-radius: 50%; +} + +.controls button svg { + margin: auto; +} \ No newline at end of file diff --git a/src/components/Client/Client.jsx b/src/components/Client/Client.jsx new file mode 100644 index 0000000..d7d0aba --- /dev/null +++ b/src/components/Client/Client.jsx @@ -0,0 +1,88 @@ +import React from "react" +import './Client.css' +import './checkbox.css' +import TrafficFormatter from "../DataFormatter/TrafficFormatter" +import TimeFormatter from "../DataFormatter/TimeFormatter" + +export default function Client({deviceName, ip, lastConnect, uploadSpeed, downloadSpeed, downloadTraffic, uploadTraffic}) { + return ( + <> +
+
+
+ + + + + + +
+
+
{deviceName}
+
{ip}
+
+
+ +
+
+
+ + +
+
+ + +
+
+
+ + + +
+
+ + {/* */} + {/* Клиент */} + + ) +} \ No newline at end of file diff --git a/src/components/Client/checkbox.css b/src/components/Client/checkbox.css new file mode 100644 index 0000000..5bd679c --- /dev/null +++ b/src/components/Client/checkbox.css @@ -0,0 +1,61 @@ +.switch { + position: relative; + display: inline-block; + width: 60px; + height: 34px; +} + +.switch input { + display: none; +} + +.slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: #ccc; + -webkit-transition: .4s; + transition: .4s; + border-radius: 34px; +} + +.slider:before { + position: absolute; + content: ""; + height: 26px; + width: 26px; + left: 4px; + bottom: 4px; + background-color: white; + -webkit-transition: .4s; + transition: .4s; + border-radius: 50%; +} + +input:checked+.slider { + background-color: #830202; +} + +input:focus+.slider { + box-shadow: 0 0 1px #830202; +} + +input:checked+.slider:before { + -webkit-transform: translateX(26px); + -ms-transform: translateX(26px); + transform: translateX(26px); +} + + +/* Rounded sliders */ + +.slider.round { + border-radius: 34px; +} + +.slider.round:before { + border-radius: 50%; +} \ No newline at end of file diff --git a/src/components/ClientList/ClientList.css b/src/components/ClientList/ClientList.css new file mode 100644 index 0000000..292b6a9 --- /dev/null +++ b/src/components/ClientList/ClientList.css @@ -0,0 +1,28 @@ +#clients { + border-radius: 5px; + background-color: #333; + color: white; +} + +#clients-header { + padding: 10px 20px; + display: flex; + justify-content: space-between; + align-items: center; +} + +#new-device { + color: white; + background: none; + border: 1px solid rgb(94, 93, 93); + cursor: pointer; + padding: 10px 15px; + margin: 0 10px; + background-color: #333; + border-radius: 5px; +} + +#new-device:hover { + background-color: #830202; + border: #830202; +} \ No newline at end of file diff --git a/src/components/ClientList/ClientList.jsx b/src/components/ClientList/ClientList.jsx new file mode 100644 index 0000000..a597d06 --- /dev/null +++ b/src/components/ClientList/ClientList.jsx @@ -0,0 +1,17 @@ +import React from "react"; +import Client from "../Client/Client"; +import clientsData from '../../data/clients.json' +import './ClientList.css' + +export default function ClientsList() { + return
+
+

Клиенты

+ +
+ {clientsData.map(client => ( + + ))} +
+} \ No newline at end of file diff --git a/src/components/DataFormatter/TimeFormatter.jsx b/src/components/DataFormatter/TimeFormatter.jsx new file mode 100644 index 0000000..4c72e28 --- /dev/null +++ b/src/components/DataFormatter/TimeFormatter.jsx @@ -0,0 +1,43 @@ +import React from "react"; + +// отвечает за преобразование поступающего в секундах значения в минуты, часы, дни +export default function TimeFormatter ( {seconds, withAgo = true }) { + if (seconds === undefined || seconds === null) return null; + + const formatTime = (sec) => { + const timeUnits = [ + { limit: 60, unit: 'сек' }, + { limit: 3600, unit: 'мин' }, // 60*60 + { limit: 86400, unit: 'ч' }, // 60*60*24 + { limit: Infinity, unit: 'д' } + ]; + + for (const { limit, unit } of timeUnits) { + if (sec < limit) { + const value = unit == 'д' + ? Math.floor(sec / 86400) + : unit === 'ч' + ? Math.floor(sec / 3600) + : unit === 'мин' + ? Math.floor(sec / 60) + : sec; + + return {value, unit}; + } + } + + return {value: sec, unit: 'сек'}; + }; + + const {value, unit} = formatTime(seconds) + + if (value === 0 && unit === 'сек') { + return только что + } + + return ( + + {value} {unit}{withAgo && ' назад'} + + ) +} \ No newline at end of file diff --git a/src/components/DataFormatter/TrafficFormatter.jsx b/src/components/DataFormatter/TrafficFormatter.jsx new file mode 100644 index 0000000..abc33c7 --- /dev/null +++ b/src/components/DataFormatter/TrafficFormatter.jsx @@ -0,0 +1,22 @@ +import React from "react"; + +// отвечает за преобразование поступающего в байтах значения в КБ, МБ, ГБ +export default function TrafficFormatter ({ bytes, precision = 2}) { + if (bytes === undefined || bytes === null) return null; + + const units = ['Б', 'КБ', 'МБ', 'ГБ']; + + let value = bytes; + let unitIndex = 0; + + while (value >= 1024 && unitIndex < units.length - 1) { + value /= 1024; + unitIndex += 1; + } + + return ( + + {value.toFixed(precision)} {units[unitIndex]} + + ) +} \ No newline at end of file diff --git a/src/data/clients.json b/src/data/clients.json new file mode 100644 index 0000000..a7d8e18 --- /dev/null +++ b/src/data/clients.json @@ -0,0 +1,9 @@ +[{ + "deviceName": "homePC", + "ip": "10.8.0.3", + "lastConnect": 23260, + "uploadSpeed": 500, + "downloadSpeed": 12300, + "uploadTraffic": 120838, + "downloadTraffic": 3423534 +}] \ No newline at end of file diff --git a/src/logo.svg b/src/logo.svg deleted file mode 100644 index 9dfc1c0..0000000 --- a/src/logo.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/pages/Home.css b/src/pages/Home.css new file mode 100644 index 0000000..d8b9523 --- /dev/null +++ b/src/pages/Home.css @@ -0,0 +1,10 @@ +#main-container { + width: 60%; +} + +#main-header { + display: flex; + justify-content: space-between; + align-items: center; + color: white; +} \ No newline at end of file diff --git a/src/pages/Home.jsx b/src/pages/Home.jsx new file mode 100644 index 0000000..7145755 --- /dev/null +++ b/src/pages/Home.jsx @@ -0,0 +1,18 @@ +import React from "react"; +import Client from "../components/Client/Client"; +import ClientsList from "../components/ClientList/ClientList"; +import './Home.css' + +export function MainPage() { + return ( + <> +
+
+

WireGuard

+ Выйти +
+ +
+ + ) +} \ No newline at end of file diff --git a/src/pages/Login.jsx b/src/pages/Login.jsx new file mode 100644 index 0000000..e69de29