diff --git a/typescript/Frontend/src/App.tsx b/typescript/Frontend/src/App.tsx index 3fa25c407..c13163b0c 100644 --- a/typescript/Frontend/src/App.tsx +++ b/typescript/Frontend/src/App.tsx @@ -1,18 +1,114 @@ -import Home from "./routes/Home"; import useToken from "./hooks/useToken"; import Login from "./Login"; -import { BrowserRouter } from "react-router-dom"; +import { BrowserRouter, Route, Routes } from "react-router-dom"; +import { + Box, + Grid, + ButtonGroup, + Button, + Divider, + Select, + MenuItem, +} from "@mui/material"; +import NestedList from "./components/NestedList"; +import BasicTable from "./components/Table"; +import BasicTabs from "./components/Tabs"; +import Alarms from "./routes/Alarms"; +import InstallationDetail from "./routes/Installation"; +import Log from "./routes/Log"; +import routes from "./routes.json"; +import { FormattedMessage, IntlProvider } from "react-intl"; +import { useState } from "react"; const App = () => { const { token, setToken } = useToken(); + const [language, setLanguage] = useState("en"); if (!token) { return ; } + const de = { + allInstallations: "Alle Installationen", + }; + + const en = { + allInstallations: "All installations", + }; + + const getTranslations = () => { + if (language === "de") { + return de; + } + return en; + }; + return ( - + + + + + + + + + + + + + + + + + + } + /> + } /> + } /> + } /> + + + + + ); }; diff --git a/typescript/Frontend/src/Login.tsx b/typescript/Frontend/src/Login.tsx index b6065dee3..cc81da8fc 100644 --- a/typescript/Frontend/src/Login.tsx +++ b/typescript/Frontend/src/Login.tsx @@ -1,15 +1,14 @@ import React, { useState } from "react"; -import axios from "axios"; -import { Button, CircularProgress, TextField } from "@mui/material"; +import { Button, CircularProgress } from "@mui/material"; import Container from "@mui/material/Container"; +import InnovenergyTextfield from "./components/InnovenergyTextfield"; +import { axiosConfigWithoutToken } from "./config/axiosConfig"; const loginUser = async (username: string, password: string) => { - return axios - .post("https://localhost:7087/api/Login", { - username, - password, - }) - .then((data) => data); + return axiosConfigWithoutToken.post("/Login", { + username, + password, + }); }; const Login = ({ setToken }: { setToken: any }) => { @@ -26,24 +25,27 @@ const Login = ({ setToken }: { setToken: any }) => { }; return ( - - + setUsername(e.target.value)} + value={username} + handleChange={(e) => setUsername(e.target.value)} /> - setPassword(e.target.value)} + value={password} + handleChange={(e) => setPassword(e.target.value)} /> - +
+ +
{loading && }
); diff --git a/typescript/Frontend/src/components/CustomerForm.tsx b/typescript/Frontend/src/components/CustomerForm.tsx new file mode 100644 index 000000000..30689dc4d --- /dev/null +++ b/typescript/Frontend/src/components/CustomerForm.tsx @@ -0,0 +1,95 @@ +import { Button } from "@mui/material"; +import { useFormik } from "formik"; +import { FormattedMessage, useIntl } from "react-intl"; +import axiosConfig from "../config/axiosConfig"; +import { I_Installation } from "../util/installation.util"; +import InnovenergyTextfield from "./InnovenergyTextfield"; + +interface I_CustomerFormProps { + values: I_Installation; + id: string | undefined; +} +const CustomerForm = (props: I_CustomerFormProps) => { + const { values, id } = props; + const intl = useIntl(); + + const formik = useFormik({ + initialValues: { + name: values.name, + region: values.region, + location: values.location, + country: values.country, + orderNumber: values.orderNumber, + }, + onSubmit: (formikValues) => { + axiosConfig + .put("https://localhost:7087/api/UpdateInstallation", { + ...formikValues, + id, + }) + .then((res) => { + console.log(res); + }); + }, + }); + + return ( +
+ + + + + + + + ); +}; + +export default CustomerForm; diff --git a/typescript/Frontend/src/components/FlowComp.tsx b/typescript/Frontend/src/components/FlowComp.tsx new file mode 100644 index 000000000..9868366f2 --- /dev/null +++ b/typescript/Frontend/src/components/FlowComp.tsx @@ -0,0 +1,57 @@ +import { useState, useCallback } from "react"; +import ReactFlow, { + addEdge, + FitViewOptions, + applyNodeChanges, + applyEdgeChanges, + Node, + Edge, + NodeChange, + EdgeChange, + Connection, +} from "reactflow"; + +const initialNodes: Node[] = [ + { id: "1", data: { label: "Node 1" }, position: { x: 5, y: 5 } }, + { id: "2", data: { label: "Node 2" }, position: { x: 5, y: 100 } }, +]; + +const initialEdges: Edge[] = [{ id: "e1-2", source: "1", target: "2" }]; + +const fitViewOptions: FitViewOptions = { + padding: 0.2, +}; + +function Flow() { + const [nodes, setNodes] = useState(initialNodes); + const [edges, setEdges] = useState(initialEdges); + + const onNodesChange = useCallback( + (changes: NodeChange[]) => + setNodes((nds) => applyNodeChanges(changes, nds)), + [setNodes] + ); + const onEdgesChange = useCallback( + (changes: EdgeChange[]) => + setEdges((eds) => applyEdgeChanges(changes, eds)), + [setEdges] + ); + const onConnect = useCallback( + (connection: Connection) => setEdges((eds) => addEdge(connection, eds)), + [setEdges] + ); + + return ( + + ); +} + +export default Flow; diff --git a/typescript/Frontend/src/components/InnovenergyTextfield.tsx b/typescript/Frontend/src/components/InnovenergyTextfield.tsx new file mode 100644 index 000000000..6f857db14 --- /dev/null +++ b/typescript/Frontend/src/components/InnovenergyTextfield.tsx @@ -0,0 +1,28 @@ +import { TextField } from "@mui/material"; + +interface I_InnovenergyTextfieldProps { + id: string; + label: string; + value: string; + name: string; + handleChange: (e: React.ChangeEvent) => void; + type?: string; +} + +const InnovenergyTextfield = (props: I_InnovenergyTextfieldProps) => { + return ( + + ); +}; + +export default InnovenergyTextfield; diff --git a/typescript/Frontend/src/components/NestedList.tsx b/typescript/Frontend/src/components/NestedList.tsx index 656de0d7d..02759877c 100644 --- a/typescript/Frontend/src/components/NestedList.tsx +++ b/typescript/Frontend/src/components/NestedList.tsx @@ -1,14 +1,14 @@ import List from "@mui/material/List"; import ListItemButton from "@mui/material/ListItemButton"; import ListItemText from "@mui/material/ListItemText"; -import { Divider } from "@mui/material"; +import { Divider, TextField } from "@mui/material"; import { Link } from "react-router-dom"; import useRouteMatch from "../hooks/useRouteMatch"; import routes from "../routes.json"; import { useEffect, useState } from "react"; -import axios from "axios"; -import { Installation } from "../util/installation.util"; - +import { I_Installation } from "../util/installation.util"; +import axiosConfig from "../config/axiosConfig"; +import { useIntl } from "react-intl"; const getPathWithoutId = (path?: string) => { if (path) { @@ -18,8 +18,26 @@ const getPathWithoutId = (path?: string) => { return routes.installation; }; +const filterData = ( + searchQuery: string, + data: I_Installation[] | undefined +) => { + if (data) { + return data.filter( + (installation) => + installation.name.toLowerCase().includes(searchQuery.toLowerCase()) || + installation.location.toLowerCase().includes(searchQuery.toLowerCase()) + ); + } + return data; +}; + const NestedList = () => { - const [data, setData] = useState(); + const [data, setData] = useState(); + const [searchQuery, setSearchQuery] = useState(""); + const intl = useIntl(); + const filteredData = filterData(searchQuery, data); + const routeMatch = useRouteMatch([ routes.installationWithId, routes.alarmsWithId, @@ -28,41 +46,51 @@ const NestedList = () => { ]); useEffect(() => { - const tokenString = sessionStorage.getItem("token"); - const token = tokenString !== null ? JSON.parse(tokenString) : ""; - axios - .get("https://localhost:7087/api/GetAllInstallations", { - headers: { - auth: token, - }, - }) + axiosConfig + .get("https://localhost:7087/api/GetAllInstallations", {}) .then((res) => { setData(res.data); }); }, []); return ( - - {data?.map((installation) => ( - <> - - - - - - - - ))} - + <> + setSearchQuery(e.target.value)} + /> + + {filteredData?.map((installation) => ( + <> + + + + + + + + ))} + + ); }; diff --git a/typescript/Frontend/src/components/Tabs.tsx b/typescript/Frontend/src/components/Tabs.tsx index 59ab676a6..a1eee2a9d 100644 --- a/typescript/Frontend/src/components/Tabs.tsx +++ b/typescript/Frontend/src/components/Tabs.tsx @@ -5,6 +5,7 @@ import Box from "@mui/material/Box"; import { Link } from "react-router-dom"; import routes from "../routes.json"; import useRouteMatch from "../hooks/useRouteMatch"; +import { useIntl } from "react-intl"; const BasicTabs = () => { const routeMatch = useRouteMatch([ @@ -15,31 +16,44 @@ const BasicTabs = () => { ]); const id = routeMatch?.params?.id; + const intl = useIntl(); return ( { + const tokenString = sessionStorage.getItem("token"); + const token = tokenString !== null ? JSON.parse(tokenString) : ""; + if (token) { + config.headers.auth = token; + } + return config; + }, + (error) => { + return Promise.reject(error); + } +); + +export default axiosConfig; diff --git a/typescript/Frontend/src/hooks/useToken.tsx b/typescript/Frontend/src/hooks/useToken.tsx index c18c80138..3182dc672 100644 --- a/typescript/Frontend/src/hooks/useToken.tsx +++ b/typescript/Frontend/src/hooks/useToken.tsx @@ -1,22 +1,22 @@ import { useState } from "react"; - -const getToken = (): string => { - const tokenString = sessionStorage.getItem("token"); - return tokenString !== null ? JSON.parse(tokenString).token : ""; -}; +import axiosConfig from "../config/axiosConfig"; const useToken = () => { - const [token, setToken] = useState(getToken()); + const getToken = () => { + const tokenString = sessionStorage.getItem("token"); + return tokenString !== null ? JSON.parse(tokenString) : ""; + }; + const [token, setToken] = useState(getToken()); const saveToken = (userToken: any) => { - setToken(userToken); sessionStorage.setItem("token", JSON.stringify(userToken)); + setToken(userToken); + axiosConfig.defaults.headers.common["auth"] = userToken; }; return { setToken: saveToken, token, - getToken: getToken, }; }; diff --git a/typescript/Frontend/src/routes/Alarms.tsx b/typescript/Frontend/src/routes/Alarms.tsx new file mode 100644 index 000000000..a7cb1420d --- /dev/null +++ b/typescript/Frontend/src/routes/Alarms.tsx @@ -0,0 +1,5 @@ +const Alarms = () => { + return
alarms
; +}; + +export default Alarms; diff --git a/typescript/Frontend/src/routes/Installation.tsx b/typescript/Frontend/src/routes/Installation.tsx index d24a22b4d..612d71002 100644 --- a/typescript/Frontend/src/routes/Installation.tsx +++ b/typescript/Frontend/src/routes/Installation.tsx @@ -1,93 +1,31 @@ -import { Box, TextField } from "@mui/material"; -import axios from "axios"; +import { Box, CircularProgress } from "@mui/material"; import { useEffect, useState } from "react"; import { useParams } from "react-router-dom"; -import { Installation } from "../util/installation.util"; +import CustomerForm from "../components/CustomerForm"; +import axiosConfig from "../config/axiosConfig"; +import { I_Installation } from "../util/installation.util"; const InstallationDetail = () => { const { id } = useParams(); - - const [values, setValues] = useState(); + const [values, setValues] = useState(); + const [loading, setLoading] = useState(false); useEffect(() => { - const tokenString = sessionStorage.getItem("token"); - const token = tokenString !== null ? JSON.parse(tokenString) : ""; - axios - .get("https://localhost:7087/api/GetInstallationById?id=" + id, { - headers: { - auth: token, - }, - }) - .then((res) => { - setValues(res.data); - console.log(res.data); - }); + setLoading(true); + axiosConfig.get("/GetInstallationById?id=" + id).then((res) => { + setValues(res.data); + setLoading(false); + }); }, [id]); - const handleChange = (e: React.ChangeEvent) => { - const { name, value } = e.target; - setValues({ - ...values, - [name]: value, - } as Installation); - }; - - if (values) { + if (values && values.id.toString() === id) { return ( - - - - - + ); + } else if (loading) { + return ; } return null; }; diff --git a/typescript/Frontend/src/routes/Log.tsx b/typescript/Frontend/src/routes/Log.tsx new file mode 100644 index 000000000..8b1eb817a --- /dev/null +++ b/typescript/Frontend/src/routes/Log.tsx @@ -0,0 +1,156 @@ +const Log = () => { + const foo = { + TimeStamp: "1676643900", + Devices: [ + { + "TruConvertAc 205330741": { + Ac: { + L1: { + Current: 2.49, + Voltage: 239.4, + Phi: 0.200334842323119592691046359, + }, + L2: { + Current: 2.65, + Voltage: 239.6, + Phi: 0.200334842323119592691046359, + }, + L3: { + Current: 2.63, + Voltage: 239.8, + Phi: 0, + }, + Frequency: 49.98, + }, + Dc: { + Current: 2.249388753056234718826405868, + Voltage: 818, + Power: 1840.0, + }, + Alarms: [], + }, + }, + { + "TruConvertDc 3214": { + Dc: { + Current: -2.1173594132029339853300733496, + Voltage: 818, + Power: -1732.0, + }, + Dc48: { + Current: -30, + Voltage: 56.0, + Power: -1680.0, + }, + Warnings: [], + Alarms: [], + }, + }, + { + "EmuMeter 123": { + Ac: { + L1: { + Current: 3.212, + Voltage: 239.4, + Phi: 1.1483422646081408626645746948, + }, + L2: { + Current: -2.462, + Voltage: 238.9, + Phi: 1.8441893582623698418074097834, + }, + L3: { + Current: 2.995, + Voltage: 238.8, + Phi: 0.1415394733244272187457893568, + }, + Frequency: 49.9, + }, + }, + }, + { + "EmuMeter 123": { + Ac: { + L1: { + Current: 3.212, + Voltage: 239.4, + Phi: 1.1483422646081408626645746948, + }, + L2: { + Current: -2.462, + Voltage: 238.9, + Phi: 1.8441893582623698418074097834, + }, + L3: { + Current: 2.995, + Voltage: 238.8, + Phi: 0.1415394733244272187457893568, + }, + Frequency: 49.9, + }, + }, + }, + { + Name: "AMPT", + Type: "PvOnDc", + "Current 1": 2.098, + "Current 2": 2.575, + "Voltage 1": 822.989, + "Voltage 2": 823.169, + "Power 1": 1726.630922, + "Power 2": 2119.660175, + }, + { + Name: "48TL Battery", + Type: "Battery", + Dc48: { + Current: 14.17, + Voltage: 53.41, + Power: 756.8197, + }, + Alarms: [], + Warnings: [], + Soc: 77.4, + HeaterOn: true, + EocReached: false, + BatteryCold: false, + Temperature: 265.4, + }, + { + Name: "48TL Battery", + Type: "Battery", + Dc48: { + Current: 11.3, + Voltage: 53.4, + Power: 603.42, + }, + Alarms: [], + Warnings: ["bit44:"], + Soc: 77.6, + HeaterOn: true, + EocReached: false, + BatteryCold: false, + Temperature: 264.9, + }, + ], + }; + + const flattenObject = (obj: any, prefix = "") => + Object.keys(obj).reduce((previous: any, current: any) => { + const pre = prefix.length ? prefix + "/" : ""; + if (Array.isArray(obj) || typeof obj === "string") { + return previous; + } + if (typeof obj[current] === "object") { + Object.assign(previous, flattenObject(obj[current], pre + current)); + } else { + previous[pre + current] = obj[current]; + } + return previous; + }, {}); + + console.log(flattenObject(foo.Devices[0])); + return
log
; +}; + +export default Log; diff --git a/typescript/Frontend/src/util/installation.util.tsx b/typescript/Frontend/src/util/installation.util.tsx index c762d7b3e..94e106b6f 100644 --- a/typescript/Frontend/src/util/installation.util.tsx +++ b/typescript/Frontend/src/util/installation.util.tsx @@ -1,5 +1,5 @@ // TODO add if required or not -export interface Installation { +export interface I_Installation { type: string; title: string; status: number; @@ -8,7 +8,7 @@ export interface Installation { location: string; region: string; country: string; - orderNumbers: string; + orderNumber: string; lat: number; long: number; s3Bucket: string; @@ -17,3 +17,5 @@ export interface Installation { information: string; parentId: number; } + +