From 637c8ae4ba37b4b1773d2f3e9f883ff5d772604c Mon Sep 17 00:00:00 2001 From: Noe Date: Fri, 15 Sep 2023 13:34:28 +0200 Subject: [PATCH] Updated backend --- csharp/App/Backend/Backend.csproj | 195 ++++++++++++++++++ csharp/App/Backend/Controller.cs | 19 +- csharp/App/Backend/DataTypes/Installation.cs | 5 +- .../App/Backend/DataTypes/Methods/Folder.cs | 5 + .../Backend/DataTypes/Methods/Installation.cs | 23 ++- .../App/Backend/DataTypes/Methods/Session.cs | 41 ++-- csharp/App/Backend/DataTypes/TreeNode.cs | 2 +- csharp/App/Backend/DataTypes/User.cs | 2 +- csharp/App/Backend/Database/Db.cs | 5 +- csharp/App/Backend/Database/Delete.cs | 17 +- csharp/App/Backend/Program.cs | 21 +- csharp/App/Backend/{ => S3}/exoscale.toml | 0 csharp/App/VrmGrabber/server.py | 2 +- typescript/Frontend/package.json | 1 + typescript/Frontend/src/App.tsx | 18 +- .../Context/S3CredentialsContextProvider.tsx | 4 +- .../Context/UserContextProvider.tsx | 1 + .../Installations/Detail/InstallationForm.tsx | 31 ++- .../Installations/Installations.tsx | 2 +- .../src/components/Layout/NavigationTabs.tsx | 7 +- typescript/Frontend/src/lang/en.json | 2 + .../src/Resources/axiosConfig.tsx | 26 +++ .../src/Resources/innoveng_logo_on_orange.png | Bin 0 -> 15277 bytes .../dashboards/Users/FlatUsersView.tsx | 126 +++++++++++ .../content/dashboards/Users/UsersSearch.tsx | 90 ++++++++ .../src/content/dashboards/Users/index.tsx | 25 +++ typescript/frontend-marios2/src/lang/de.json | 47 +++++ typescript/frontend-marios2/src/lang/en.json | 52 +++++ typescript/frontend-marios2/src/lang/fr.json | 47 +++++ 29 files changed, 749 insertions(+), 67 deletions(-) rename csharp/App/Backend/{ => S3}/exoscale.toml (100%) create mode 100644 typescript/frontend-marios2/src/Resources/axiosConfig.tsx create mode 100644 typescript/frontend-marios2/src/Resources/innoveng_logo_on_orange.png create mode 100644 typescript/frontend-marios2/src/content/dashboards/Users/FlatUsersView.tsx create mode 100644 typescript/frontend-marios2/src/content/dashboards/Users/UsersSearch.tsx create mode 100644 typescript/frontend-marios2/src/content/dashboards/Users/index.tsx create mode 100644 typescript/frontend-marios2/src/lang/de.json create mode 100644 typescript/frontend-marios2/src/lang/en.json create mode 100644 typescript/frontend-marios2/src/lang/fr.json diff --git a/csharp/App/Backend/Backend.csproj b/csharp/App/Backend/Backend.csproj index bf230bd4e..4b498515e 100644 --- a/csharp/App/Backend/Backend.csproj +++ b/csharp/App/Backend/Backend.csproj @@ -13,6 +13,7 @@ + @@ -39,6 +40,200 @@ PreserveNewest + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/csharp/App/Backend/Controller.cs b/csharp/App/Backend/Controller.cs index ada5ed86a..7adf7d751 100644 --- a/csharp/App/Backend/Controller.cs +++ b/csharp/App/Backend/Controller.cs @@ -197,7 +197,10 @@ public class Controller : ControllerBase if (user == null) return Unauthorized(); - return user.DescendantUsers().Select(u => u.HidePassword()).ToList(); + return user + .DescendantUsers() + .Select(u => u.HidePassword()) + .ToList(); } @@ -252,7 +255,7 @@ public class Controller : ControllerBase [HttpPost(nameof(CreateUser))] - public ActionResult CreateUser(User newUser, Token authToken) + public ActionResult CreateUser([FromBody] User newUser, Token authToken) { return Db.GetSession(authToken).Create(newUser) ? newUser.HidePassword() @@ -260,7 +263,7 @@ public class Controller : ControllerBase } [HttpPost(nameof(CreateInstallation))] - public async Task> CreateInstallation([FromBody]Installation installation, Token authToken) + public async Task> CreateInstallation([FromBody] Installation installation, Token authToken) { var session = Db.GetSession(authToken); @@ -271,7 +274,7 @@ public class Controller : ControllerBase } [HttpPost(nameof(CreateFolder))] - public ActionResult CreateFolder(Folder folder, Token authToken) + public ActionResult CreateFolder([FromBody] Folder folder, Token authToken) { var session = Db.GetSession(authToken); @@ -331,7 +334,7 @@ public class Controller : ControllerBase var session = Db.GetSession(authToken); // TODO: automatic BadRequest when properties are null during deserialization - var installation = Db.GetFolderById(installationAccess.InstallationId); + var installation = Db.GetInstallationById(installationAccess.InstallationId); var user = Db.GetUserById(installationAccess.UserId); return session.RevokeUserAccessTo(user, installation) @@ -342,7 +345,7 @@ public class Controller : ControllerBase [HttpPut(nameof(UpdateUser))] - public ActionResult UpdateUser(User updatedUser, Token authToken) + public ActionResult UpdateUser([FromBody] User updatedUser, Token authToken) { var session = Db.GetSession(authToken); @@ -366,7 +369,7 @@ public class Controller : ControllerBase [HttpPut(nameof(UpdateInstallation))] - public ActionResult UpdateInstallation(Installation installation, Token authToken) + public ActionResult UpdateInstallation([FromBody] Installation installation, Token authToken) { var session = Db.GetSession(authToken); @@ -378,7 +381,7 @@ public class Controller : ControllerBase [HttpPut(nameof(UpdateFolder))] - public ActionResult UpdateFolder(Folder folder, Token authToken) + public ActionResult UpdateFolder([FromBody] Folder folder, Token authToken) { var session = Db.GetSession(authToken); diff --git a/csharp/App/Backend/DataTypes/Installation.cs b/csharp/App/Backend/DataTypes/Installation.cs index 71fb0d640..492558c54 100644 --- a/csharp/App/Backend/DataTypes/Installation.cs +++ b/csharp/App/Backend/DataTypes/Installation.cs @@ -9,8 +9,9 @@ public class Installation : TreeNode public String Country { get; set; } = ""; // TODO: make relation - [Ignore] public IReadOnlyList OrderNumbers { get; set; } = Array.Empty(); - + //public IReadOnlyList OrderNumbers { get; set; } = Array.Empty(); + public String OrderNumbers { get; set; } = ""; + public Double Lat { get; set; } public Double Long { get; set; } diff --git a/csharp/App/Backend/DataTypes/Methods/Folder.cs b/csharp/App/Backend/DataTypes/Methods/Folder.cs index a71960f52..8098b53c0 100644 --- a/csharp/App/Backend/DataTypes/Methods/Folder.cs +++ b/csharp/App/Backend/DataTypes/Methods/Folder.cs @@ -52,6 +52,11 @@ public static class FolderMethods .Skip(1); // skip self } + public static IEnumerable DescendantFoldersAndSelf(this Folder parent) + { + return parent + .TraverseDepthFirstPreOrder(ChildFolders); + } public static Boolean IsDescendantOf(this Folder folder, Folder ancestor) { return folder diff --git a/csharp/App/Backend/DataTypes/Methods/Installation.cs b/csharp/App/Backend/DataTypes/Methods/Installation.cs index fbae86041..cf6bdb014 100644 --- a/csharp/App/Backend/DataTypes/Methods/Installation.cs +++ b/csharp/App/Backend/DataTypes/Methods/Installation.cs @@ -1,4 +1,5 @@ using InnovEnergy.App.Backend.Database; +using InnovEnergy.App.Backend.Relations; using InnovEnergy.App.Backend.S3; using InnovEnergy.Lib.Utils; @@ -119,12 +120,28 @@ public static class InstallationMethods return Db.Installations.Any(i => i.Id == installation.Id); } - public static IReadOnlyList GetOrderNumbers(this Installation installation) + public static Boolean SetOrderNumbers(this Installation installation) { - return Db.OrderNumber2Installation + foreach (var orderNumber in installation.OrderNumbers.Split(',')) + { + + var o2I = new OrderNumber2Installation + { + OrderNumber = orderNumber, + InstallationId = installation.Id + }; + Db.Create(o2I); + } + + return true; + } + + public static String GetOrderNumbers(this Installation installation) + { + return string.Join(", ", Db.OrderNumber2Installation .Where(i => i.InstallationId == installation.Id) .Select(i => i.OrderNumber) - .ToReadOnlyList(); + .ToReadOnlyList()); } public static Installation FillOrderNumbers(this Installation installation) diff --git a/csharp/App/Backend/DataTypes/Methods/Session.cs b/csharp/App/Backend/DataTypes/Methods/Session.cs index 8f2ee1191..f2a1540c3 100644 --- a/csharp/App/Backend/DataTypes/Methods/Session.cs +++ b/csharp/App/Backend/DataTypes/Methods/Session.cs @@ -83,18 +83,19 @@ public static class SessionMethods var user = session?.User; return user is not null - && installation is not null - && user.HasWriteAccess - && user.HasAccessToParentOf(installation) - && Db.Create(installation) // TODO: these two in a transaction - && Db.Create(new InstallationAccess { UserId = user.Id, InstallationId = installation.Id }) - && await installation.CreateBucket() - && await installation.RenewS3Credentials(); // generation of access _after_ generation of - // bucket to prevent "zombie" access-rights. - // This might fuck us over if the creation of access rights fails, - // as bucket-names are unique and bound to the installation id... -K + && installation is not null + && user.HasWriteAccess + && user.HasAccessToParentOf(installation) + && Db.Create(installation) // TODO: these two in a transaction + && installation.SetOrderNumbers() + && Db.Create(new InstallationAccess { UserId = user.Id, InstallationId = installation.Id }); + //&& await installation.CreateBucket() + //&& await installation.RenewS3Credentials(); // generation of access _after_ generation of + // bucket to prevent "zombie" access-rights. + // This might fuck us over if the creation of access rights fails, + // as bucket-names are unique and bound to the installation id... -K } - + public static Boolean Update(this Session? session, Installation? installation) { var user = session?.User; @@ -104,7 +105,7 @@ public static class SessionMethods if (!Equals(originalOrderNumbers, installation?.OrderNumbers)) { - foreach (var orderNumber in installation!.OrderNumbers) + foreach (var orderNumber in installation!.OrderNumbers.Split(',')) { if (originalOrderNumbers.Contains(orderNumber)) continue; var o2I = new OrderNumber2Installation @@ -115,7 +116,7 @@ public static class SessionMethods Db.Create(o2I); } - foreach (var orderNumberOld in originalOrderNumbers) + foreach (var orderNumberOld in originalOrderNumbers.Split(',')) { if (!installation!.OrderNumbers.Contains(orderNumberOld)) { @@ -138,13 +139,13 @@ public static class SessionMethods public static async Task Delete(this Session? session, Installation? installation) { var user = session?.User; - + return user is not null - && installation is not null - && user.HasWriteAccess - && user.HasAccessTo(installation) - && Db.Delete(installation) - && await installation.DeleteBucket(); + && installation is not null + && user.HasWriteAccess + && user.HasAccessTo(installation) + && Db.Delete(installation); + //&& await installation.DeleteBucket(); } public static Boolean Create(this Session? session, User newUser) @@ -158,7 +159,7 @@ public static class SessionMethods && newUser .WithParent(sessionUser) .Do(() => newUser.MustResetPassword = true) - .Do(() => newUser.Password = newUser.SaltAndHashPassword(newUser.Password)) + .Do(() => newUser.Password = null) .Apply(Db.Create); // && Mailer.Mailer.SendVerificationMessage(newUser); diff --git a/csharp/App/Backend/DataTypes/TreeNode.cs b/csharp/App/Backend/DataTypes/TreeNode.cs index b1a4a72fa..5613fbbdf 100644 --- a/csharp/App/Backend/DataTypes/TreeNode.cs +++ b/csharp/App/Backend/DataTypes/TreeNode.cs @@ -5,7 +5,7 @@ namespace InnovEnergy.App.Backend.DataTypes; public abstract partial class TreeNode { [PrimaryKey, AutoIncrement] - public virtual Int64 Id { get; set; } + public virtual Int64 Id { get; set; } public virtual String Name { get; set; } = ""; // overridden by User (unique) public String Information { get; set; } = ""; // unstructured random info diff --git a/csharp/App/Backend/DataTypes/User.cs b/csharp/App/Backend/DataTypes/User.cs index a9c6b4bdd..7397eb6e2 100644 --- a/csharp/App/Backend/DataTypes/User.cs +++ b/csharp/App/Backend/DataTypes/User.cs @@ -8,7 +8,7 @@ public class User : TreeNode public Boolean HasWriteAccess { get; set; } = false; public Boolean MustResetPassword { get; set; } = false; public String Language { get; set; } = null!; - public String Password { get; set; } = null!; + public String? Password { get; set; } = null!; [Unique] public override String Name { get; set; } = null!; diff --git a/csharp/App/Backend/Database/Db.cs b/csharp/App/Backend/Database/Db.cs index 0a630bc50..ed95f6340 100644 --- a/csharp/App/Backend/Database/Db.cs +++ b/csharp/App/Backend/Database/Db.cs @@ -27,7 +27,8 @@ public static partial class Db .Last().Name; var fileConnection = new SQLiteConnection("DbBackups/"+latestDb); - + + Console.Out.Write(latestDb); var memoryConnection = new SQLiteConnection(":memory:"); // fileConnection.Backup(memoryConnection.DatabasePath); @@ -74,7 +75,7 @@ public static partial class Db public static void BackupDatabase() { var filename = "db-" + DateTimeOffset.UtcNow.ToUnixTimeSeconds() + ".sqlite"; - Connection.Backup("DbBackups/"+filename); + Connection.Backup("DbBackups/" + filename); } public static TableQuery Sessions => Connection.Table(); diff --git a/csharp/App/Backend/Database/Delete.cs b/csharp/App/Backend/Database/Delete.cs index 55ff12f84..cb3895037 100644 --- a/csharp/App/Backend/Database/Delete.cs +++ b/csharp/App/Backend/Database/Delete.cs @@ -10,12 +10,18 @@ public static partial class Db { public static Boolean Delete(Folder folder) { - return RunTransaction(DeleteFolderAndAllItsDependencies); + var deleteSuccess= RunTransaction(DeleteFolderAndAllItsDependencies); + if (deleteSuccess) + { + BackupDatabase(); + } + + return deleteSuccess; Boolean DeleteFolderAndAllItsDependencies() { return folder - .DescendantFolders() + .DescendantFoldersAndSelf() .All(DeleteDescendantFolderAndItsDependencies); } @@ -24,10 +30,8 @@ public static partial class Db FolderAccess .Delete(r => r.FolderId == f.Id); Installations.Delete(r => r.ParentId == f.Id); var delete = Folders.Delete(r => r.Id == f.Id); - var deleteSuccess = delete > 0; - if (deleteSuccess) - BackupDatabase(); - return deleteSuccess; + + return delete>0; } } @@ -42,6 +46,7 @@ public static partial class Db Boolean DeleteInstallationAndItsDependencies() { InstallationAccess.Delete(i => i.InstallationId == installation.Id); + OrderNumber2Installation.Delete(i => i.InstallationId == installation.Id); return Installations.Delete(i => i.Id == installation.Id) > 0; } } diff --git a/csharp/App/Backend/Program.cs b/csharp/App/Backend/Program.cs index 3e49baeb2..f39e5e463 100644 --- a/csharp/App/Backend/Program.cs +++ b/csharp/App/Backend/Program.cs @@ -3,6 +3,8 @@ using InnovEnergy.App.Backend.Database; using Microsoft.AspNetCore.HttpOverrides; using Microsoft.AspNetCore.Mvc; using Microsoft.OpenApi.Models; +using System.Net; +using InnovEnergy.Lib.Utils; namespace InnovEnergy.App.Backend; @@ -12,8 +14,8 @@ public static class Program { //Db.CreateFakeRelations(); Db.Init(); - var builder = WebApplication.CreateBuilder(args); + builder.Services.AddControllers(); builder.Services.AddProblemDetails(setup => { @@ -21,7 +23,7 @@ public static class Program setup.IncludeExceptionDetails = (ctx, env) => builder.Environment.IsDevelopment() || builder.Environment.IsStaging(); //This handles our Exceptions - setup.Map(exception => new ProblemDetails() + setup.Map(exception => new ProblemDetails { Detail = exception.Detail, Status = exception.Status, @@ -38,6 +40,16 @@ public static class Program }); var app = builder.Build(); + + app.Use(async (context, next) => + { + var x = 2; + + context.Request.WriteLine(); + + await next(context); + }); + app.UseForwardedHeaders(new ForwardedHeadersOptions { @@ -51,12 +63,11 @@ public static class Program } app.UseCors(p => p.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod()) ; - app.UseHttpsRedirection(); + //app.UseHttpsRedirection(); app.MapControllers(); app.UseProblemDetails(); - - app.Run(); + app.Run(); } private static OpenApiInfo OpenApiInfo { get; } = new OpenApiInfo diff --git a/csharp/App/Backend/exoscale.toml b/csharp/App/Backend/S3/exoscale.toml similarity index 100% rename from csharp/App/Backend/exoscale.toml rename to csharp/App/Backend/S3/exoscale.toml diff --git a/csharp/App/VrmGrabber/server.py b/csharp/App/VrmGrabber/server.py index 27abdbd52..5d5c350ec 100644 --- a/csharp/App/VrmGrabber/server.py +++ b/csharp/App/VrmGrabber/server.py @@ -3,7 +3,7 @@ from flask import Flask from json2html import json2html app = Flask(__name__) -serverUrl = "https://127.0.0.1:7087/api" #todo change me +serverUrl = "https://127.0.0.1:8000/api" #todo change me @app.route('/') def hello(): diff --git a/typescript/Frontend/package.json b/typescript/Frontend/package.json index 97d6cfe9c..6e32e3d2f 100644 --- a/typescript/Frontend/package.json +++ b/typescript/Frontend/package.json @@ -35,6 +35,7 @@ "yup": "^1.1.0" }, "scripts": { + "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test", diff --git a/typescript/Frontend/src/App.tsx b/typescript/Frontend/src/App.tsx index ea3ca8592..e7216a7c5 100644 --- a/typescript/Frontend/src/App.tsx +++ b/typescript/Frontend/src/App.tsx @@ -1,11 +1,11 @@ import useToken from "./hooks/useToken"; import Login from "./Login"; -import { BrowserRouter, Navigate, Route, Routes } from "react-router-dom"; +import {BrowserRouter, Navigate, Route, Routes} from "react-router-dom"; -import { Container, Grid, Box } from "@mui/material"; +import {Box, Container, Grid} from "@mui/material"; import routes from "./routes.json"; -import { IntlProvider } from "react-intl"; -import { useContext, useState } from "react"; +import {IntlProvider} from "react-intl"; +import {useContext, useState} from "react"; import en from "./lang/en.json"; import de from "./lang/de.json"; import fr from "./lang/fr.json"; @@ -14,10 +14,10 @@ import LogoutButton from "./components/Layout/LogoutButton"; import Users from "./components/Users/Users"; import NavigationTabs from "./components/Layout/NavigationTabs"; import InstallationPage from "./components/Installations/InstallationPage"; -import { UserContext } from "./components/Context/UserContextProvider"; +import {UserContext} from "./components/Context/UserContextProvider"; import ResetPassword from "./ResetPassword"; import innovenergyLogo from "./resources/innoveng_logo_on_orange.png"; -import { colors } from "./index"; +import {colors} from "./index"; const App = () => { const { token, setToken, removeToken } = useToken(); @@ -51,8 +51,10 @@ const App = () => { > - - innovenergy logo + + + innovenergy logo + void; fetchData: (timestamp: UnixTime) => Promise>; } - + export const S3CredentialsContext = createContext({ s3Credentials: {} as I_S3Credentials, @@ -27,7 +27,7 @@ const S3CredentialsContextProvider = ({ const [s3Credentials, setS3Credentials] = useState(); const saveS3Credentials = (credentials: I_S3Credentials, id: string) => { - const s3Bucket = id + "-3e5b3069-214a-43ee-8d85-57d72000c10d"; + const s3Bucket = id + "-3e5b3069-214a-43ee-8d85-57d72000c19d"; setS3Credentials({ s3Bucket, ...credentials }); }; diff --git a/typescript/Frontend/src/components/Context/UserContextProvider.tsx b/typescript/Frontend/src/components/Context/UserContextProvider.tsx index 2a1ea29b5..f7a83464c 100644 --- a/typescript/Frontend/src/components/Context/UserContextProvider.tsx +++ b/typescript/Frontend/src/components/Context/UserContextProvider.tsx @@ -1,6 +1,7 @@ import { createContext, ReactNode, useState } from "react"; import { I_User } from "../../util/user.util"; + interface I_InstallationContextProviderProps { currentUser?: I_User; setCurrentUser: (value: I_User) => void; diff --git a/typescript/Frontend/src/components/Installations/Detail/InstallationForm.tsx b/typescript/Frontend/src/components/Installations/Detail/InstallationForm.tsx index e780ab854..307ab6118 100644 --- a/typescript/Frontend/src/components/Installations/Detail/InstallationForm.tsx +++ b/typescript/Frontend/src/components/Installations/Detail/InstallationForm.tsx @@ -33,7 +33,7 @@ const InstallationForm = (props: I_InstallationFormProps) => { const readOnly = !getCurrentUser().hasWriteAccess; const intl = useIntl(); - + const validationSchema = Yup.object().shape({ name: Yup.string().required( intl.formatMessage({ @@ -53,6 +53,19 @@ const InstallationForm = (props: I_InstallationFormProps) => { defaultMessage: "Location is required", }) ), + country: Yup.string().required( + intl.formatMessage({ + id: "requiredCountry", + defaultMessage: "Country is required", + }) + ), + orderNumbers: Yup.string().required( + intl.formatMessage({ + id: "requiredOrderNumber", + defaultMessage: "Order Number is required", + }) + ), + }); const formik = useFormik({ @@ -64,6 +77,12 @@ const InstallationForm = (props: I_InstallationFormProps) => { orderNumbers: values.orderNumbers, }, onSubmit: (formikValues) => { + /*const updatedValues = { + ...formikValues, + + orderNumbers: formikValues.orderNumbers.split(','), + };*/ + handleSubmit(values, formikValues) .then(() => { setOpen(true); @@ -164,12 +183,22 @@ const InstallationForm = (props: I_InstallationFormProps) => { additionalButtons.map((button) => button)} {!readOnly && ( + + )} + {!readOnly && ( + + + + )} { ); }; -export default Installations; +export default Installations; \ No newline at end of file diff --git a/typescript/Frontend/src/components/Layout/NavigationTabs.tsx b/typescript/Frontend/src/components/Layout/NavigationTabs.tsx index b74f31a0d..bf8de966f 100644 --- a/typescript/Frontend/src/components/Layout/NavigationTabs.tsx +++ b/typescript/Frontend/src/components/Layout/NavigationTabs.tsx @@ -16,12 +16,7 @@ const NavigationTabs = () => { return ( <> diff --git a/typescript/Frontend/src/lang/en.json b/typescript/Frontend/src/lang/en.json index 3cbbc6634..2751313ca 100644 --- a/typescript/Frontend/src/lang/en.json +++ b/typescript/Frontend/src/lang/en.json @@ -2,6 +2,7 @@ "liveView": "Live view", "allInstallations": "All installations", "applyChanges": "Apply changes", + "deleteInstallation": "Delete Installation", "country": "Country", "customerName": "Customer name", "english": "English", @@ -44,6 +45,7 @@ "requiredLocation": "Location is required", "requiredName": "Name is required", "requiredRegion": "Region is required", + "requiredOrderNumber": "Required Order Number", "submit": "Submit", "user": "User", "userTabs": "user tabs" diff --git a/typescript/frontend-marios2/src/Resources/axiosConfig.tsx b/typescript/frontend-marios2/src/Resources/axiosConfig.tsx new file mode 100644 index 000000000..7406652c7 --- /dev/null +++ b/typescript/frontend-marios2/src/Resources/axiosConfig.tsx @@ -0,0 +1,26 @@ +import axios from 'axios'; + +export const axiosConfigWithoutToken = axios.create({ + baseURL: 'https://localhost:7087/api' +}); + +const axiosConfig = axios.create({ + baseURL: 'https://localhost:7087/api' +}); + +axiosConfig.defaults.params = {}; +axiosConfig.interceptors.request.use( + (config) => { + const tokenString = localStorage.getItem('token'); + const token = tokenString !== null ? tokenString : ''; + if (token) { + config.params['authToken'] = token; + } + return config; + }, + (error) => { + return Promise.reject(error); + } +); + +export default axiosConfig; diff --git a/typescript/frontend-marios2/src/Resources/innoveng_logo_on_orange.png b/typescript/frontend-marios2/src/Resources/innoveng_logo_on_orange.png new file mode 100644 index 0000000000000000000000000000000000000000..4f1714ecd5d0649bc2424b75edf0dfe10e188ccc GIT binary patch literal 15277 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 S1_9Buza&NFL~4Z$0{<7YTnP67 literal 0 HcmV?d00001 diff --git a/typescript/frontend-marios2/src/content/dashboards/Users/FlatUsersView.tsx b/typescript/frontend-marios2/src/content/dashboards/Users/FlatUsersView.tsx new file mode 100644 index 000000000..452f81c9c --- /dev/null +++ b/typescript/frontend-marios2/src/content/dashboards/Users/FlatUsersView.tsx @@ -0,0 +1,126 @@ +import React, { useState } from 'react'; +import { + Card, + Divider, + Grid, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + Typography, + useTheme +} from '@mui/material'; +import { InnovEnergyUser } from 'src/interfaces/UserTypes'; +import User from './User'; + +interface FlatUsersViewProps { + users: InnovEnergyUser[]; + fetchDataAgain: () => void; +} + +const FlatUsersView = (props: FlatUsersViewProps) => { + const [selectedUser, setSelectedUser] = useState(-1); + const selectedBulkActions = selectedUser !== -1; + + const handleSelectOneUser = (installationID: number): void => { + if (selectedUser != installationID) { + setSelectedUser(installationID); + } else { + setSelectedUser(-1); + } + }; + + const theme = useTheme(); + const [isRowHovered, setHoveredRow] = useState(-1); + + const handleRowMouseEnter = (id: number) => { + setHoveredRow(id); + }; + + const handleRowMouseLeave = () => { + setHoveredRow(-1); + }; + const findUser = (id: number) => { + return props.users.find((user) => user.id === id); + }; + + return ( + + + + + + + + + + Username + Email + + + + {props.users.map((user) => { + const isInstallationSelected = user.id === selectedUser; + const rowStyles = + isRowHovered === user.id + ? { + cursor: 'pointer', + backgroundColor: theme.colors.primary.lighter // Set your desired hover background color here + } + : {}; + + return ( + handleSelectOneUser(user.id)} + style={rowStyles} + onMouseEnter={() => handleRowMouseEnter(user.id)} + onMouseLeave={() => handleRowMouseLeave()} + > + + + + {user.name} + + + + + {user.email} + + + + ); + })} + +
+
+
+
+ + {selectedBulkActions && ( + + )} +
+ ); +}; + +export default FlatUsersView; diff --git a/typescript/frontend-marios2/src/content/dashboards/Users/UsersSearch.tsx b/typescript/frontend-marios2/src/content/dashboards/Users/UsersSearch.tsx new file mode 100644 index 000000000..3ae60674a --- /dev/null +++ b/typescript/frontend-marios2/src/content/dashboards/Users/UsersSearch.tsx @@ -0,0 +1,90 @@ +import React, { useContext, useEffect, useState } from 'react'; +import { + FormControl, + Grid, + InputAdornment, + TextField, + useTheme +} from '@mui/material'; +import SearchTwoToneIcon from '@mui/icons-material/SearchTwoTone'; +import FlatUsersView from './FlatUsersView'; +import { UsersContext } from '../../../contexts/UsersContextProvider'; +import Button from '@mui/material/Button'; +import UserForm from './userForm'; +import { UserContext } from '../../../contexts/userContext'; + +function UsersSearch() { + const theme = useTheme(); + const [searchTerm, setSearchTerm] = useState(''); + const { availableUsers, fetchAvailableUsers } = useContext(UsersContext); + const [filteredData, setFilteredData] = useState(availableUsers); + const [openModal, setOpenModal] = useState(false); + const context = useContext(UserContext); + const { currentUser, setUser } = context; + + useEffect(() => { + fetchAvailableUsers(); + }, []); + + const fetchDataAgain = () => { + fetchAvailableUsers(); + }; + + useEffect(() => { + const filtered = availableUsers.filter((item) => + item.name.toLowerCase().includes(searchTerm.toLowerCase()) + ); + setFilteredData(filtered); + }, [searchTerm, availableUsers]); + + const handleSubmit = () => { + setOpenModal(true); + }; + const handleUserFormSubmit = () => { + setOpenModal(false); + fetchAvailableUsers(); + }; + + const handleUserFormCancel = () => { + setOpenModal(false); + }; + + return ( + <> + + + {currentUser.hasWriteAccess && ( + + )} + + + {openModal && ( + + )} + + + + setSearchTerm(e.target.value)} + fullWidth + InputProps={{ + startAdornment: ( + + + + ) + }} + /> + + + + + + ); +} + +export default UsersSearch; diff --git a/typescript/frontend-marios2/src/content/dashboards/Users/index.tsx b/typescript/frontend-marios2/src/content/dashboards/Users/index.tsx new file mode 100644 index 000000000..5980287f0 --- /dev/null +++ b/typescript/frontend-marios2/src/content/dashboards/Users/index.tsx @@ -0,0 +1,25 @@ +import Footer from 'src/components/Footer'; +import { Box, Container, Grid, useTheme } from '@mui/material'; +import UsersSearch from './UsersSearch'; +import UsersContextProvider from 'src/contexts/UsersContextProvider'; + +function Users() { + const theme = useTheme(); + + return ( + <> + + + + + + + + +