From 04a374c3803ba47f6c2b1d37db9224c72695ef2a Mon Sep 17 00:00:00 2001 From: Proddy Date: Fri, 15 Apr 2022 16:22:05 +0200 Subject: [PATCH] more tables --- .../framework/security/ManageUsersForm.tsx | 181 +++++++++++------- interface/src/project/DashboardStatus.tsx | 151 ++++++++------- interface/src/project/types.ts | 31 +-- interface/src/types/security.ts | 1 + lib/framework/SecuritySettingsService.h | 1 + mock-api/server.js | 40 ++-- src/web/WebDataService.cpp | 11 +- src/web/WebStatusService.cpp | 79 +++++--- 8 files changed, 275 insertions(+), 220 deletions(-) diff --git a/interface/src/framework/security/ManageUsersForm.tsx b/interface/src/framework/security/ManageUsersForm.tsx index f5dbfaae2..16c30560d 100644 --- a/interface/src/framework/security/ManageUsersForm.tsx +++ b/interface/src/framework/security/ManageUsersForm.tsx @@ -1,6 +1,6 @@ import { FC, useContext, useState } from 'react'; -import { Button, IconButton, Table, TableBody, TableCell, TableFooter, TableHead, TableRow } from '@mui/material'; +import { Button, IconButton, Box } from '@mui/material'; import SaveIcon from '@mui/icons-material/Save'; import DeleteIcon from '@mui/icons-material/Delete'; import PersonAddIcon from '@mui/icons-material/PersonAdd'; @@ -9,6 +9,10 @@ import CheckIcon from '@mui/icons-material/Check'; import CloseIcon from '@mui/icons-material/Close'; import VpnKeyIcon from '@mui/icons-material/VpnKey'; +import { Table } from '@table-library/react-table-library/table'; +import { useTheme } from '@table-library/react-table-library/theme'; +import { Header, HeaderRow, HeaderCell, Body, Row, Cell } from '@table-library/react-table-library/table'; + import * as SecurityApi from '../../api/security'; import { SecuritySettings, User } from '../../types'; import { ButtonRow, FormLoader, MessageBox, SectionContent } from '../../components'; @@ -19,16 +23,6 @@ import { AuthenticatedContext } from '../../contexts/authentication'; import GenerateToken from './GenerateToken'; import UserForm from './UserForm'; -function compareUsers(a: User, b: User) { - if (a.username < b.username) { - return -1; - } - if (a.username > b.username) { - return 1; - } - return 0; -} - const ManageUsersForm: FC = () => { const { loadData, saving, data, setData, saveData, errorMessage } = useRest({ read: SecurityApi.readSecuritySettings, @@ -40,6 +34,45 @@ const ManageUsersForm: FC = () => { const [generatingToken, setGeneratingToken] = useState(); const authenticatedContext = useContext(AuthenticatedContext); + const table_theme = useTheme({ + BaseRow: ` + font-size: 14px; + color: white; + `, + HeaderRow: ` + text-transform: uppercase; + background-color: black; + color: #90CAF9; + border-bottom: 1px solid #e0e0e0; + `, + Row: ` + &:nth-of-type(odd) { + background-color: #303030; + } + &:nth-of-type(even) { + background-color: #1e1e1e; + } + border-top: 1px solid #565656; + border-bottom: 1px solid #565656; + position: relative; + z-index: 1; + &:not(:last-of-type) { + margin-bottom: -1px; + } + &:not(:first-of-type) { + margin-top: -1px; + } + &:hover { + color: white; + } + `, + BaseCell: ` + border-top: 1px solid transparent; + border-right: 1px solid transparent; + border-bottom: 1px solid transparent; + ` + }); + const content = () => { if (!data) { return ; @@ -48,13 +81,14 @@ const ManageUsersForm: FC = () => { const noAdminConfigured = () => !data.users.find((u) => u.admin); const removeUser = (toRemove: User) => { - const users = data.users.filter((u) => u.username !== toRemove.username); + const users = data.users.filter((u) => u.id !== toRemove.id); setData({ ...data, users }); }; const createUser = () => { setCreating(true); setUser({ + id: '', username: '', password: '', admin: true @@ -72,7 +106,7 @@ const ManageUsersForm: FC = () => { const doneEditingUser = () => { if (user) { - const users = [...data.users.filter((u) => u.username !== user.username), user]; + const users = [...data.users.filter((u) => u.id !== user.id), user]; setData({ ...data, users }); setUser(undefined); } @@ -82,8 +116,8 @@ const ManageUsersForm: FC = () => { setGeneratingToken(undefined); }; - const generateToken = (username: string) => { - setGeneratingToken(username); + const generateToken = (id: string) => { + setGeneratingToken(id); }; const onSubmit = async () => { @@ -93,66 +127,71 @@ const ManageUsersForm: FC = () => { return ( <> - - - - Username - is Admin? - - - - - {data.users.sort(compareUsers).map((u) => ( - - - {u.username} - - {u.admin ? : } - - generateToken(u.username)} - > - - - removeUser(u)}> - - - editUser(u)}> - - - - - ))} - - - - - - - - - +
+ {(tableList: any) => ( + <> +
+ + USERNAME + IS ADMIN + + +
+ + {tableList.map((u: User, index: number) => ( + + {u.id} + {u.admin ? : } + + generateToken(u.id)} + > + + + removeUser(u)}> + + + editUser(u)}> + + + + + ))} + + + )}
+ {noAdminConfigured() && ( )} - - - + + + + + + + + + + + + + status !== busConnectionStatus.BUS_STATUS_OFFLINE; const busStatusHighlight = ({ status }: Status, theme: Theme) => { @@ -60,7 +56,7 @@ const busStatus = ({ status }: Status) => { case busConnectionStatus.BUS_STATUS_CONNECTED: return 'Connected'; case busConnectionStatus.BUS_STATUS_TX_ERRORS: - return 'Tx issues - try a different Tx-Mode'; + return 'Tx issues - try a different Tx Mode'; case busConnectionStatus.BUS_STATUS_OFFLINE: return 'Disconnected'; default: @@ -68,41 +64,17 @@ const busStatus = ({ status }: Status) => { } }; -const formatRow = (name: string, success: number, fail: number, quality: number) => { - if (success === 0 && fail === 0) { - return ( - - {name} - - - - - ); +const showQuality = (stat: Stat) => { + if (stat.q === 0 || stat.s + stat.f === 0) { + return; } - - return ( - - {name} - {Intl.NumberFormat().format(success)} - {Intl.NumberFormat().format(fail)} - {showQuality(quality)} - - ); -}; - -const showQuality = (quality: number) => { - if (quality === 0) { - return ; + if (stat.q === 100) { + return
{stat.q}%
; } - - if (quality === 100) { - return {quality}%; - } - - if (quality >= 95) { - return {quality}%; + if (stat.q >= 95) { + return
{stat.q}%
; } else { - return {quality}%; + return
{stat.q}%
; } }; @@ -115,6 +87,48 @@ const DashboardStatus: FC = () => { const { me } = useContext(AuthenticatedContext); + const stats_theme = tableTheme({ + BaseRow: ` + font-size: 14px; + color: white; + `, + HeaderRow: ` + text-transform: uppercase; + background-color: black; + color: #90CAF9; + border-bottom: 1px solid #e0e0e0; + `, + Row: ` + &:nth-of-type(odd) { + background-color: #303030; + } + &:nth-of-type(even) { + background-color: #1e1e1e; + } + border-top: 1px solid #565656; + border-bottom: 1px solid #565656; + position: relative; + z-index: 1; + &:not(:last-of-type) { + margin-bottom: -1px; + } + &:not(:first-of-type) { + margin-top: -1px; + } + &:hover { + color: white; + } + `, + BaseCell: ` + border-top: 1px solid transparent; + border-right: 1px solid transparent; + border-bottom: 1px solid transparent; + &:nth-of-type(1) { + flex: 1; + } + ` + }); + useEffect(() => { const timer = setInterval(() => loadData(), 30000); return () => { @@ -183,27 +197,30 @@ const DashboardStatus: FC = () => { /> - - - - - - SUCCESS - FAIL - QUALITY - - - - {formatRow('EMS Telegrams Received (Rx)', data.rx_received, data.rx_fails, data.rx_quality)} - {formatRow('EMS Reads (Tx)', data.tx_reads, data.tx_read_fails, data.tx_read_quality)} - {formatRow('EMS Writes (Tx)', data.tx_writes, data.tx_write_fails, data.tx_write_quality)} - {formatRow('Temperature Sensor Reads', data.sensor_reads, data.sensor_fails, data.sensor_quality)} - {formatRow('Analog Sensor Reads', data.analog_reads, data.analog_fails, data.analog_quality)} - {formatRow('MQTT Publishes', data.mqtt_count, data.mqtt_fails, data.mqtt_quality)} - {formatRow('API Calls', data.api_calls, data.api_fails, data.api_quality)} - -
-
+ + {(tableList: any) => ( + <> +
+ + + SUCCESS + FAIL + QUALITY + +
+ + {tableList.map((stat: Stat, index: number) => ( + + {stat.id} + {Intl.NumberFormat().format(stat.s)} + {Intl.NumberFormat().format(stat.f)} + {showQuality(stat)} + + ))} + + + )} +
{renderScanDialog()} diff --git a/interface/src/project/types.ts b/interface/src/project/types.ts index ac63dee65..b41053881 100644 --- a/interface/src/project/types.ts +++ b/interface/src/project/types.ts @@ -38,36 +38,21 @@ export enum busConnectionStatus { BUS_STATUS_OFFLINE = 2 } +export interface Stat { + id: string; // name + s: number; // success + f: number; // fail + q: number; // quality +} export interface Status { status: busConnectionStatus; tx_mode: number; - rx_received: number; - tx_reads: number; - tx_writes: number; - rx_quality: number; - tx_read_quality: number; - tx_write_quality: number; - tx_read_fails: number; - tx_write_fails: number; - rx_fails: number; - sensor_fails: number; - sensor_reads: number; - sensor_quality: number; - analog_fails: number; - analog_reads: number; - analog_quality: number; - mqtt_count: number; - mqtt_fails: number; - mqtt_quality: number; - api_calls: number; - api_fails: number; - api_quality: number; + uptime: number; num_devices: number; num_sensors: number; num_analogs: number; - uptime: number; + stats: Stat[]; } - export interface Device { id: string; // id index t: string; // type diff --git a/interface/src/types/security.ts b/interface/src/types/security.ts index 90db06a1e..472248b12 100644 --- a/interface/src/types/security.ts +++ b/interface/src/types/security.ts @@ -1,4 +1,5 @@ export interface User { + id: string; // needed for Table username: string; password: string; admin: boolean; diff --git a/lib/framework/SecuritySettingsService.h b/lib/framework/SecuritySettingsService.h index cdbd8d085..51f57461e 100644 --- a/lib/framework/SecuritySettingsService.h +++ b/lib/framework/SecuritySettingsService.h @@ -43,6 +43,7 @@ class SecuritySettings { JsonArray users = root.createNestedArray("users"); for (User user : settings.users) { JsonObject userRoot = users.createNestedObject(); + userRoot["id"] = user.username; // for React Table userRoot["username"] = user.username; userRoot["password"] = user.password; userRoot["admin"] = user.admin; diff --git a/mock-api/server.js b/mock-api/server.js index 4bcfec3f7..0a8bdd56f 100644 --- a/mock-api/server.js +++ b/mock-api/server.js @@ -270,8 +270,8 @@ const system_status = { security_settings = { jwt_secret: 'naughty!', users: [ - { username: 'admin', password: 'admin', admin: true }, - { username: 'guest', password: 'guest', admin: false }, + { id: 'admin', username: 'admin', password: 'admin', admin: true }, + { id: 'guest', username: 'guest', password: 'guest', admin: false }, ], } const features = { @@ -417,33 +417,21 @@ const emsesp_sensordata = { } const status = { - analog_fails: 0, - analog_quality: 100, - analog_reads: 203, - api_calls: 4, - api_fails: 0, - api_quality: 100, - mqtt_count: 40243, - mqtt_fails: 0, - mqtt_quality: 100, - num_analogs: 1, - num_devices: 2, - num_sensors: 1, - rx_fails: 11, - rx_quality: 100, - rx_received: 56506, - sensor_fails: 0, - sensor_quality: 100, - sensor_reads: 15438, status: 0, tx_mode: 1, - tx_read_fails: 0, - tx_read_quality: 100, - tx_reads: 9026, - tx_write_fails: 2, - tx_write_quality: 95, - tx_writes: 33, uptime: 77186, + num_devices: 2, + num_sensors: 1, + num_analogs: 1, + stats: [ + { id: 'EMS Telegrams Received (Rx)', s: 56506, f: 11, q: 100 }, + { id: 'EMS Reads (Tx)', s: 9026, f: 0, q: 100 }, + { id: 'EMS Writes (Tx)', s: 33, f: 2, q: 95 }, + { id: 'Temperature Sensor Reads', s: 56506, f: 11, q: 100 }, + { id: 'Analog Sensor Reads', s: 0, f: 0, q: 100 }, + { id: 'MQTT Publishes', s: 12, f: 10, q: 20 }, + { id: 'API Calls', s: 0, f: 0, q: 0 }, + ], } // Dashboard data diff --git a/src/web/WebDataService.cpp b/src/web/WebDataService.cpp index d1df1596e..0d9347ce5 100644 --- a/src/web/WebDataService.cpp +++ b/src/web/WebDataService.cpp @@ -176,11 +176,12 @@ void WebDataService::device_data(AsyncWebServerRequest * request, JsonVariant & emsdevice->generate_values_web(output); #endif -#ifdef EMSESP_USE_SERIAL -#ifdef EMSESP_DEBUG - serializeJson(output, Serial); -#endif -#endif + // #ifdef EMSESP_USE_SERIAL + // #ifdef EMSESP_DEBUG + // serializeJson(output, Serial); + // #endif + // #endif + response->setLength(); request->send(response); return; diff --git a/src/web/WebStatusService.cpp b/src/web/WebStatusService.cpp index bb9dd3050..f4122656b 100644 --- a/src/web/WebStatusService.cpp +++ b/src/web/WebStatusService.cpp @@ -129,34 +129,57 @@ void WebStatusService::webStatusService(AsyncWebServerRequest * request) { auto * response = new AsyncJsonResponse(false, EMSESP_JSON_SIZE_MEDIUM_DYN); JsonObject root = response->getRoot(); - root["status"] = EMSESP::bus_status(); // 0, 1 or 2 - root["num_devices"] = EMSESP::count_devices(); // excluding Controller - root["num_sensors"] = EMSESP::dallassensor_.no_sensors(); - root["num_analogs"] = EMSESP::analogsensor_.no_sensors(); - root["tx_mode"] = EMSESP::txservice_.tx_mode(); - root["rx_received"] = EMSESP::rxservice_.telegram_count(); - root["tx_reads"] = EMSESP::txservice_.telegram_read_count(); - root["tx_writes"] = EMSESP::txservice_.telegram_write_count(); - root["rx_quality"] = EMSESP::rxservice_.quality(); - root["tx_read_quality"] = EMSESP::txservice_.read_quality(); - root["tx_write_quality"] = EMSESP::txservice_.write_quality(); - root["rx_fails"] = EMSESP::rxservice_.telegram_error_count(); - root["tx_read_fails"] = EMSESP::txservice_.telegram_read_fail_count(); - root["tx_write_fails"] = EMSESP::txservice_.telegram_write_fail_count(); - root["sensor_fails"] = EMSESP::dallassensor_.fails(); - root["sensor_reads"] = EMSESP::dallassensor_.reads(); - root["sensor_quality"] = EMSESP::dallassensor_.reads() == 0 ? 100 : 100 - (uint8_t)((100 * EMSESP::dallassensor_.fails()) / EMSESP::dallassensor_.reads()); - root["analog_fails"] = EMSESP::analogsensor_.fails(); - root["analog_reads"] = EMSESP::analogsensor_.reads(); - root["analog_quality"] = EMSESP::analogsensor_.reads() == 0 ? 100 : 100 - (uint8_t)((100 * EMSESP::analogsensor_.fails()) / EMSESP::analogsensor_.reads()); - root["mqtt_fails"] = Mqtt::publish_fails(); - root["mqtt_count"] = Mqtt::publish_count(); - root["mqtt_quality"] = Mqtt::publish_count() == 0 ? 100 : 100 - (Mqtt::publish_fails() * 100) / (Mqtt::publish_count() + Mqtt::publish_fails()); - root["api_calls"] = WebAPIService::api_count(); // + WebAPIService::api_fails(); - root["api_fails"] = WebAPIService::api_fails(); - root["api_quality"] = - WebAPIService::api_count() == 0 ? 100 : 100 - (WebAPIService::api_fails() * 100) / (WebAPIService::api_count() + WebAPIService::api_fails()); - root["uptime"] = EMSbus::bus_uptime(); + root["status"] = EMSESP::bus_status(); // 0, 1 or 2 + root["tx_mode"] = EMSESP::txservice_.tx_mode(); + root["uptime"] = EMSbus::bus_uptime(); + root["num_devices"] = EMSESP::count_devices(); // excluding Controller + root["num_sensors"] = EMSESP::dallassensor_.no_sensors(); + root["num_analogs"] = EMSESP::analogsensor_.no_sensors(); + + JsonArray statsJson = root.createNestedArray("stats"); + JsonObject statJson; + + statJson = statsJson.createNestedObject(); + statJson["id"] = "EMS Telegrams Received (Rx)"; + statJson["s"] = EMSESP::rxservice_.telegram_count(); + statJson["f"] = EMSESP::rxservice_.telegram_error_count(); + statJson["q"] = EMSESP::rxservice_.quality(); + + statJson = statsJson.createNestedObject(); + statJson["id"] = "EMS Reads (Tx)"; + statJson["s"] = EMSESP::txservice_.telegram_read_count(); + statJson["f"] = EMSESP::txservice_.telegram_read_fail_count(); + statJson["q"] = EMSESP::txservice_.read_quality(); + + statJson = statsJson.createNestedObject(); + statJson["id"] = "EMS Writes (Tx)"; + statJson["s"] = EMSESP::txservice_.telegram_write_count(); + statJson["f"] = EMSESP::txservice_.telegram_write_fail_count(); + statJson["q"] = EMSESP::txservice_.write_quality(); + + statJson = statsJson.createNestedObject(); + statJson["id"] = "Temperature Sensor Reads"; + statJson["s"] = EMSESP::dallassensor_.reads(); + statJson["f"] = EMSESP::dallassensor_.fails(); + statJson["q"] = EMSESP::dallassensor_.reads() == 0 ? 100 : 100 - (uint8_t)((100 * EMSESP::dallassensor_.fails()) / EMSESP::dallassensor_.reads()); + + statJson = statsJson.createNestedObject(); + statJson["id"] = "Analog Sensor Reads"; + statJson["s"] = EMSESP::analogsensor_.reads(); + statJson["f"] = EMSESP::analogsensor_.fails(); + statJson["q"] = EMSESP::analogsensor_.reads() == 0 ? 100 : 100 - (uint8_t)((100 * EMSESP::analogsensor_.fails()) / EMSESP::analogsensor_.reads()); + + statJson = statsJson.createNestedObject(); + statJson["id"] = "MQTT Publishes"; + statJson["s"] = Mqtt::publish_count(); + statJson["f"] = Mqtt::publish_fails(); + statJson["q"] = Mqtt::publish_count() == 0 ? 100 : 100 - (Mqtt::publish_fails() * 100) / (Mqtt::publish_count() + Mqtt::publish_fails()); + + statJson = statsJson.createNestedObject(); + statJson["id"] = "API Calls"; + statJson["s"] = WebAPIService::api_count(); // + WebAPIService::api_fails(); + statJson["f"] = WebAPIService::api_fails(); + statJson["q"] = WebAPIService::api_count() == 0 ? 100 : 100 - (WebAPIService::api_fails() * 100) / (WebAPIService::api_count() + WebAPIService::api_fails()); response->setLength(); request->send(response);