diff --git a/csharp/App/Backend/Controller.cs b/csharp/App/Backend/Controller.cs index 62df917c0..5e27a6256 100644 --- a/csharp/App/Backend/Controller.cs +++ b/csharp/App/Backend/Controller.cs @@ -446,6 +446,41 @@ public class Controller : ControllerBase return installation.FillOrderNumbers().HideParentIfUserHasNoAccessToParent(session!.User).HideWriteKeyIfUserIsNotAdmin(session.User.HasWriteAccess); } + [HttpPost(nameof(AcknowledgeError))] + public ActionResult AcknowledgeError(Int64 id, Token authToken) + { + var session = Db.GetSession(authToken); + + if (session == null) + return Unauthorized(); + + var error=Db.Errors + .FirstOrDefault(error => error.Id == id); + + error.Seen = true; + + return Db.Update(error) + ? Ok() + : Unauthorized(); + } + + [HttpPost(nameof(AcknowledgeWarning))] + public ActionResult AcknowledgeWarning(Int64 id, Token authToken) + { + var session = Db.GetSession(authToken); + + if (session == null) + return Unauthorized(); + + var warning=Db.Warnings + .FirstOrDefault(warning => warning.Id == id); + + warning.Seen = true; + + return Db.Update(warning) + ? Ok() + : Unauthorized(); + } [HttpPut(nameof(UpdateFolder))] public ActionResult UpdateFolder([FromBody] Folder folder, Token authToken) diff --git a/csharp/App/Backend/DataTypes/Error.cs b/csharp/App/Backend/DataTypes/Error.cs index da655498a..bbddb018a 100644 --- a/csharp/App/Backend/DataTypes/Error.cs +++ b/csharp/App/Backend/DataTypes/Error.cs @@ -7,9 +7,10 @@ public abstract class LogEntry [PrimaryKey, AutoIncrement] public Int64 Id { get; set; } public Int64 InstallationId { get; set; } - public String Description { get; set; } = null!; - public DateTime CreatedAt { get; set; } - public String DeviceCreatedTheMessage { get; set; } = null!; + public String Description { get; set; } = null!; + public String Date { get; set; } = null!; + public String Time { get; set; } = null!; + public String DeviceCreatedTheMessage{ get; set; } = null!; public Boolean Seen { get; set; } } diff --git a/csharp/App/Backend/Database/Create.cs b/csharp/App/Backend/Database/Create.cs index 72638f096..8900e6f1c 100644 --- a/csharp/App/Backend/Database/Create.cs +++ b/csharp/App/Backend/Database/Create.cs @@ -70,7 +70,7 @@ public static partial class Db { var oldestError = Errors.Where(error => error.InstallationId == installationId) - .OrderBy(error => error.CreatedAt) + .OrderBy(error => error.Date) .FirstOrDefault(); //Remove the old error @@ -96,7 +96,7 @@ public static partial class Db { var oldestWarning = Warnings.Where(warning => warning.InstallationId == installationId) - .OrderBy(warning => warning.CreatedAt) + .OrderBy(warning => warning.Date) .FirstOrDefault(); //Remove the old error diff --git a/csharp/App/Backend/Database/Update.cs b/csharp/App/Backend/Database/Update.cs index 16d9d76b0..1c75504f9 100644 --- a/csharp/App/Backend/Database/Update.cs +++ b/csharp/App/Backend/Database/Update.cs @@ -16,6 +16,17 @@ public static partial class Db { return Update(obj: folder); } + + public static Boolean Update(Error error) + { + return Update(obj: error); + } + + + public static Boolean Update(Warning warning) + { + return Update(obj: warning); + } public static Boolean Update(Installation installation) { diff --git a/csharp/App/Backend/Resources/exoscaleS3.json b/csharp/App/Backend/Resources/exoscaleS3.json index c55b7c6a9..ee3a39359 100644 --- a/csharp/App/Backend/Resources/exoscaleS3.json +++ b/csharp/App/Backend/Resources/exoscaleS3.json @@ -1,4 +1,4 @@ { - "Key": "EXOea18f5a82bd358896154c783", - "Secret": "lYtzU7R5e0L6XKOgBaLVPFr41nEBDxDdXU47zBAEI6M" + "Key": "EXO4d838d1360ba9fb7d51648b0", + "Secret": "_bmrp6ewWAvNwdAQoeJuC-9y02Lsx7NV6zD-WjljzCU" } \ No newline at end of file diff --git a/csharp/App/Backend/Websockets/StatusMessage.cs b/csharp/App/Backend/Websockets/StatusMessage.cs index 3fb6b90ec..b64390c4e 100644 --- a/csharp/App/Backend/Websockets/StatusMessage.cs +++ b/csharp/App/Backend/Websockets/StatusMessage.cs @@ -3,7 +3,8 @@ public class StatusMessage { public required int InstallationId { get; init; } public required int Status { get; init; } - public DateTime CreatedAt { get; set; } + public String Date { get; set; } = null!; + public String Time { get; set; } = null!; public String Description { get; init; } = null!; public String CreatedBy { get; init; } = null!; } \ No newline at end of file diff --git a/csharp/App/Backend/Websockets/WebsockerManager.cs b/csharp/App/Backend/Websockets/WebsockerManager.cs index 069e5cbd6..bf83fd1f7 100644 --- a/csharp/App/Backend/Websockets/WebsockerManager.cs +++ b/csharp/App/Backend/Websockets/WebsockerManager.cs @@ -100,7 +100,8 @@ public static class WebsocketManager { InstallationId = receivedStatusMessage.InstallationId, Description = receivedStatusMessage.Description, - CreatedAt = receivedStatusMessage.CreatedAt, + Date = receivedStatusMessage.Date, + Time = receivedStatusMessage.Time, DeviceCreatedTheMessage = receivedStatusMessage.CreatedBy, Seen = false }; @@ -115,7 +116,8 @@ public static class WebsocketManager { InstallationId = receivedStatusMessage.InstallationId, Description = receivedStatusMessage.Description, - CreatedAt = receivedStatusMessage.CreatedAt, + Date = receivedStatusMessage.Date, + Time = receivedStatusMessage.Time, DeviceCreatedTheMessage = receivedStatusMessage.CreatedBy, Seen = false }; diff --git a/csharp/App/SaliMax/src/MiddlewareClasses/StatusMessage.cs b/csharp/App/SaliMax/src/MiddlewareClasses/StatusMessage.cs index 4d4df09b5..ed9f94b19 100644 --- a/csharp/App/SaliMax/src/MiddlewareClasses/StatusMessage.cs +++ b/csharp/App/SaliMax/src/MiddlewareClasses/StatusMessage.cs @@ -6,7 +6,8 @@ public class StatusMessage public required int InstallationId { get; set; } public required int Status { get; set; } - public DateTime CreatedAt { get; set; } + public String Date { get; set; } = null!; + public String Time { get; set; } = null!; public String Description { get; set; } = null!; public String CreatedBy { get; set; } = null!; } \ No newline at end of file diff --git a/csharp/App/SaliMax/src/Program.cs b/csharp/App/SaliMax/src/Program.cs index f6463e40e..969ee12b1 100644 --- a/csharp/App/SaliMax/src/Program.cs +++ b/csharp/App/SaliMax/src/Program.cs @@ -356,13 +356,15 @@ internal static class Program if (status == 2) { - jsonObject.CreatedAt = DateTime.Now; - jsonObject.Description = "Battery Temperature High"; - jsonObject.CreatedBy = "Battery/1"; + jsonObject.Date = DateTime.Now.ToString("yyyy-MM-dd"); + jsonObject.Time = DateTime.Now.ToString("HH:mm:ss"); + jsonObject.Description = "Battery Overvoltage"; + jsonObject.CreatedBy = "Battery/4"; } else if (status == 1) { - jsonObject.CreatedAt = DateTime.Now; + jsonObject.Date = DateTime.Now.ToString("yyyy-MM-dd"); + jsonObject.Time = DateTime.Now.ToString("HH:mm:ss"); jsonObject.Description = "Temp warning message"; jsonObject.CreatedBy = "Battery/4"; } diff --git a/csharp/Lib/Utils/TaskUtils.cs b/csharp/Lib/Utils/TaskUtils.cs index bc048bc37..a6fd6b3de 100644 --- a/csharp/Lib/Utils/TaskUtils.cs +++ b/csharp/Lib/Utils/TaskUtils.cs @@ -27,7 +27,17 @@ public static class TaskUtils return await task; // await the task to bubble up any errors etc } + public static void SupressAwaitWarning(this Task task) + { + } + + public static void SupressAwaitWarning(this Task task) + { + } + + + public static Task WhenAny(this IEnumerable tasks) { diff --git a/typescript/frontend-marios2/src/content/dashboards/Log/Log.tsx b/typescript/frontend-marios2/src/content/dashboards/Log/Log.tsx index 3dc656ced..cd9823a17 100644 --- a/typescript/frontend-marios2/src/content/dashboards/Log/Log.tsx +++ b/typescript/frontend-marios2/src/content/dashboards/Log/Log.tsx @@ -6,12 +6,6 @@ import { Divider, Grid, IconButton, - Table, - TableBody, - TableCell, - TableContainer, - TableHead, - TableRow, useTheme } from '@mui/material'; import Typography from '@mui/material/Typography'; @@ -25,6 +19,8 @@ import { useNavigate } from 'react-router-dom'; import { TokenContext } from '../../../contexts/tokenContext'; import { ErrorMessage } from '../../../interfaces/S3Types'; import Button from '@mui/material/Button'; +import FormControlLabel from '@mui/material/FormControlLabel'; +import Checkbox from '@mui/material/Checkbox'; interface LogProps { errorLoadingS3Data: boolean; @@ -37,7 +33,7 @@ function Log(props: LogProps) { const [errors, setErrors] = useState([]); const [errorButtonPressed, setErrorButtonPressed] = useState(false); const [warningButtonPressed, setWarningButtonPressed] = useState(false); - + const [updateCount, setUpdateCount] = useState(0); const navigate = useNavigate(); const tokencontext = useContext(TokenContext); const { removeToken } = tokencontext; @@ -66,7 +62,7 @@ function Log(props: LogProps) { navigate(routes.login); } }); - }, []); + }, [updateCount]); const handleErrorButtonPressed = () => { setErrorButtonPressed(!errorButtonPressed); @@ -76,6 +72,34 @@ function Log(props: LogProps) { setWarningButtonPressed(!warningButtonPressed); }; + const handleErrorAcknowledgeButtonPressed = (errorId: number) => { + axiosConfig + .post(`/AcknowledgeError?id=${errorId}`) + .then((res: AxiosResponse) => { + setUpdateCount(updateCount + 1); + }) + .catch((err: AxiosError) => { + if (err.response && err.response.status == 401) { + removeToken(); + navigate(routes.login); + } + }); + }; + + const handleWarningAcknowledgeButtonPressed = (warningId: number) => { + axiosConfig + .post(`/AcknowledgeWarning?id=${warningId}`) + .then((res: AxiosResponse) => { + setUpdateCount(updateCount + 1); + }) + .catch((err: AxiosError) => { + if (err.response && err.response.status == 401) { + removeToken(); + navigate(routes.login); + } + }); + }; + return ( @@ -91,97 +115,278 @@ function Log(props: LogProps) { {errorButtonPressed && errors.length > 0 && ( - - - - - - - - - - - - - - - - - - - - - - - {errors.map((error, index) => ( - - +
+
+
+ + + +
+ +
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ +
+ {errors.map((error, index) => ( + <> + +
+
- - +
+ +
{error.description} - - +
+
{error.deviceCreatedTheMessage} - - +
+
- {error.createdAt} + {error.date} - - +
+
- {error.seen == false ? 'No' : 'Yes'} + {error.time} - - - ))} - -
-
+ +
+ + error.seen === false && + handleErrorAcknowledgeButtonPressed(error.id) + } + sx={{ + marginLeft: '25px' + }} + /> + } + label="" + sx={{ marginTop: 1 }} + /> +
+ + + ))} + +
)} @@ -224,99 +429,280 @@ function Log(props: LogProps) { {warningButtonPressed && warnings.length > 0 && ( - - - - - - - - - - - - - - - - - - - - - - - {warnings.map((warning, index) => { - return ( - - - - - - - {warning.description} - - - - - {warning.deviceCreatedTheMessage} - - - - - {warning.createdAt} - - - - - {warning.seen == false ? 'No' : 'Yes'} - - - - ); - })} - -
-
+
+
+
+ + + +
+ +
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ +
+ {warnings.map((warning, index) => ( + <> + +
+
+ +
+ +
+ + {warning.description} + +
+
+ + {warning.deviceCreatedTheMessage} + +
+
+ + {warning.date} + +
+
+ + {warning.time} + +
+
+ + warning.seen === false && + handleWarningAcknowledgeButtonPressed( + warning.id + ) + } + sx={{ + marginLeft: '25px' + }} + /> + } + label="" + sx={{ marginTop: 1 }} + /> +
+
+ + ))} +
+
)} diff --git a/typescript/frontend-marios2/src/content/dashboards/Users/UsersSearch.tsx b/typescript/frontend-marios2/src/content/dashboards/Users/UsersSearch.tsx index d7ca160e5..ea0fa2950 100644 --- a/typescript/frontend-marios2/src/content/dashboards/Users/UsersSearch.tsx +++ b/typescript/frontend-marios2/src/content/dashboards/Users/UsersSearch.tsx @@ -1,7 +1,9 @@ import React, { useContext, useEffect, useState } from 'react'; import { + Alert, FormControl, Grid, + IconButton, InputAdornment, TextField, useTheme @@ -13,6 +15,7 @@ import Button from '@mui/material/Button'; import UserForm from './userForm'; import { UserContext } from '../../../contexts/userContext'; import { FormattedMessage } from 'react-intl'; +import { Close as CloseIcon } from '@mui/icons-material'; function UsersSearch() { const theme = useTheme(); @@ -21,6 +24,8 @@ function UsersSearch() { const [filteredData, setFilteredData] = useState(availableUsers); const [openModal, setOpenModal] = useState(false); const context = useContext(UserContext); + const [userCreated, setUserCreated] = useState(false); + const [errorOccured, setErrorOccured] = useState(false); const { currentUser, setUser } = context; useEffect(() => { @@ -43,13 +48,27 @@ function UsersSearch() { }; const handleUserFormSubmit = () => { setOpenModal(false); + setUserCreated(true); + fetchAvailableUsers(); + + setTimeout(() => { + setUserCreated(false); + }, 8000); }; const handleUserFormCancel = () => { setOpenModal(false); }; + const handleUserError = () => { + setOpenModal(false); + setErrorOccured(true); + setTimeout(() => { + setErrorOccured(false); + }, 3000); + }; + const isMobile = window.innerWidth <= 1490; return ( @@ -63,8 +82,67 @@ function UsersSearch() { )}
+ {userCreated && ( + + + + + + setUserCreated(false)} + sx={{ marginLeft: '4px' }} + > + + + + + + )} + {errorOccured && ( + + + + + setErrorOccured(false)} + sx={{ marginLeft: '4px' }} + > + + + + + + )} {openModal && ( - + )} diff --git a/typescript/frontend-marios2/src/content/dashboards/Users/userForm.tsx b/typescript/frontend-marios2/src/content/dashboards/Users/userForm.tsx index 819a0985f..c59abbf00 100644 --- a/typescript/frontend-marios2/src/content/dashboards/Users/userForm.tsx +++ b/typescript/frontend-marios2/src/content/dashboards/Users/userForm.tsx @@ -25,6 +25,7 @@ import { FormattedMessage } from 'react-intl'; interface userFormProps { cancel: () => void; submit: () => void; + error: () => void; } function userForm(props: userFormProps) { @@ -106,44 +107,67 @@ function userForm(props: userFormProps) { const isMobile = window.innerWidth <= 1490; const handleSubmit = async (e) => { - const res = await axiosConfig.post('/CreateUser', { - ...formValues, - password: '', - language: 'english' - }); + const res = await axiosConfig + .post('/CreateUser', { + ...formValues, + password: '', + language: 'english' + }) + .catch((err) => { + setLoading(false); + if (err.response) { + props.error(); + } + }); - try { - for (const folderName of selectedFolderNames) { - const folder = folders.find((folder) => folder.name === folderName); + if (res) { + try { + for (const folderName of selectedFolderNames) { + const folder = folders.find((folder) => folder.name === folderName); - await axiosConfig.post( - `/GrantUserAccessToFolder?UserId=${res.data.id}&FolderId=${folder.id}` - ); + await axiosConfig + .post( + `/GrantUserAccessToFolder?UserId=${res.data.id}&FolderId=${folder.id}` + ) + .catch((err) => { + setLoading(false); + if (err.response) { + props.error(); + } + }); + } + + for (const installationName of selectedInstallationNames) { + const installation = installations.find( + (installation) => installation.name === installationName + ); + + await axiosConfig + .post( + `/GrantUserAccessToInstallation?UserId=${res.data.id}&InstallationId=${installation.id}` + ) + .catch((err) => { + setLoading(false); + if (err.response) { + props.error(); + } + }); + } + + setLoading(false); + props.submit(); + } catch (error) { + await axiosConfig + .delete(`/DeleteUser?userId=${res.data.id}`) + .then((response) => { + setLoading(false); + setErrorMessage('An error has occured'); + setError(true); + setTimeout(() => { + props.cancel(); + }, 2000); + }); } - - for (const installationName of selectedInstallationNames) { - const installation = installations.find( - (installation) => installation.name === installationName - ); - - await axiosConfig.post( - `/GrantUserAccessToInstallation?UserId=${res.data.id}&InstallationId=${installation.id}` - ); - } - - setLoading(false); - props.submit(); - } catch (error) { - await axiosConfig - .delete(`/DeleteUser?userId=${res.data.id}`) - .then((response) => { - setLoading(false); - setErrorMessage('An error has occured'); - setError(true); - setTimeout(() => { - props.cancel(); - }, 2000); - }); } }; diff --git a/typescript/frontend-marios2/src/interfaces/S3Types.tsx b/typescript/frontend-marios2/src/interfaces/S3Types.tsx index b5860df23..e910f702a 100644 --- a/typescript/frontend-marios2/src/interfaces/S3Types.tsx +++ b/typescript/frontend-marios2/src/interfaces/S3Types.tsx @@ -7,9 +7,11 @@ export interface I_S3Credentials { } export interface ErrorMessage { + id: number; installationId: number; description: string; - createdAt: Date; + date: string; + time: string; deviceCreatedTheMessage: string; seen: boolean; }