Merge remote-tracking branch 'origin/main'
This commit is contained in:
commit
66da08b407
File diff suppressed because it is too large
Load Diff
|
@ -3,43 +3,30 @@
|
|||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "^3.316.0",
|
||||
"@emotion/styled": "^11.10.5",
|
||||
"@minoru/react-dnd-treeview": "^3.4.1",
|
||||
"@mui/icons-material": "^5.11.0",
|
||||
"@mui/lab": "^5.0.0-alpha.120",
|
||||
"@mui/material": "^5.11.7",
|
||||
"@mui/x-date-pickers": "^6.5.0",
|
||||
"@testing-library/jest-dom": "^5.16.5",
|
||||
"@testing-library/react": "^13.4.0",
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
"@types/jest": "^27.5.2",
|
||||
"@types/node": "^16.18.11",
|
||||
"@types/react": "^18.0.27",
|
||||
"@types/react-dom": "^18.0.10",
|
||||
"@types/react-router-dom": "^5.3.3",
|
||||
"axios": "^1.3.1",
|
||||
"chart.js": "^4.2.1",
|
||||
"css-loader": "^6.7.3",
|
||||
"dayjs": "^1.11.7",
|
||||
"formik": "^2.2.9",
|
||||
"linq-to-typescript": "^11.0.0",
|
||||
"package.json": "^2.0.1",
|
||||
"plotly.js": "^2.20.0",
|
||||
"react": "^18.2.0",
|
||||
"react-chartjs-2": "^5.2.0",
|
||||
"react-dnd": "^16.0.1",
|
||||
"react-dnd-html5-backend": "^16.0.1",
|
||||
"react-dnd-scrolling": "^1.3.3",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-intl": "^6.2.10",
|
||||
"react-plotly.js": "^2.6.0",
|
||||
"react-router-dom": "^6.8.0",
|
||||
"react-scripts": "5.0.1",
|
||||
"reactflow": "^11.5.6",
|
||||
"rxjs": "^7.8.0",
|
||||
"sass": "^1.58.3",
|
||||
"sass-loader": "^13.2.0",
|
||||
"simplytyped": "^3.3.0",
|
||||
"style-loader": "^3.3.1",
|
||||
"testcafe": "^2.4.0",
|
||||
|
@ -83,7 +70,7 @@
|
|||
"devDependencies": {
|
||||
"@formatjs/cli": "^6.0.3",
|
||||
"@types/react-plotly.js": "^2.6.0",
|
||||
"@types/react-window": "^1.8.5",
|
||||
"depcheck": "^1.4.3",
|
||||
"eslint-plugin-formatjs": "^4.10.1",
|
||||
"prettier": "^2.8.8"
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ import useToken from "./hooks/useToken";
|
|||
import Login from "./Login";
|
||||
import { BrowserRouter, Navigate, Route, Routes } from "react-router-dom";
|
||||
|
||||
import { Container, Grid, colors, Box } from "@mui/material";
|
||||
import { Container, Grid, Box } from "@mui/material";
|
||||
import routes from "./routes.json";
|
||||
import { IntlProvider, MessageFormatElement } from "react-intl";
|
||||
import { useContext, useState } from "react";
|
||||
|
@ -17,20 +17,21 @@ import InstallationPage from "./components/Installations/InstallationPage";
|
|||
import { UserContext } from "./components/Context/UserContextProvider";
|
||||
import ResetPassword from "./ResetPassword";
|
||||
import innovenergyLogo from "./resources/innoveng_logo_on_orange.png";
|
||||
import { colors } from "./index";
|
||||
|
||||
const App = () => {
|
||||
const { token, setToken, removeToken } = useToken();
|
||||
const [language, setLanguage] = useState("EN");
|
||||
const [language, setLanguage] = useState("en");
|
||||
|
||||
const { getCurrentUser } = useContext(UserContext);
|
||||
|
||||
const getTranslations = () => {
|
||||
switch (language) {
|
||||
case "DE":
|
||||
case "en":
|
||||
return de;
|
||||
case "EN":
|
||||
case "de":
|
||||
return en;
|
||||
case "FR":
|
||||
case "fr":
|
||||
return fr;
|
||||
}
|
||||
};
|
||||
|
@ -41,12 +42,13 @@ const App = () => {
|
|||
if (token && getCurrentUser()?.mustResetPassword) {
|
||||
return <ResetPassword />;
|
||||
}
|
||||
console.log("lang", language);
|
||||
return (
|
||||
<BrowserRouter>
|
||||
<IntlProvider
|
||||
messages={getTranslations()}
|
||||
locale={language}
|
||||
defaultLocale="EN"
|
||||
defaultLocale="en"
|
||||
>
|
||||
<Container maxWidth="xl" sx={{ pt: 2 }}>
|
||||
<Grid container>
|
||||
|
@ -73,7 +75,7 @@ const App = () => {
|
|||
</Grid>
|
||||
</Container>
|
||||
<Box
|
||||
bgcolor="#eaecf1"
|
||||
bgcolor={colors.greyLight}
|
||||
pt={3}
|
||||
borderTop="2px solid"
|
||||
borderColor=" #a8b0be"
|
||||
|
|
|
@ -1,10 +1,17 @@
|
|||
import React, { useContext, useState } from "react";
|
||||
import { Alert, CircularProgress, Grid, useTheme } from "@mui/material";
|
||||
import {
|
||||
Alert,
|
||||
CircularProgress,
|
||||
Grid,
|
||||
Typography,
|
||||
useTheme,
|
||||
} from "@mui/material";
|
||||
import Container from "@mui/material/Container";
|
||||
import { axiosConfigWithoutToken } from "./config/axiosConfig";
|
||||
import InnovenergyTextfield from "./components/Layout/InnovenergyTextfield";
|
||||
import InnovenergyButton from "./components/Layout/InnovenergyButton";
|
||||
import { UserContext } from "./components/Context/UserContextProvider";
|
||||
import innovenergyLogo from "./resources/innoveng_logo_on_orange.png";
|
||||
|
||||
const loginUser = async (username: string, password: string) => {
|
||||
return axiosConfigWithoutToken.post("/Login", null, {
|
||||
|
@ -59,30 +66,47 @@ const Login = ({ setToken, setLanguage }: I_LoginProps) => {
|
|||
};
|
||||
|
||||
return (
|
||||
<Container maxWidth="sm" sx={{ p: 2, alignContent: "center" }}>
|
||||
<InnovenergyTextfield
|
||||
id="username-textfield"
|
||||
label="Username"
|
||||
name="email"
|
||||
value={username}
|
||||
handleChange={(e) => setUsername(e.target.value)}
|
||||
/>
|
||||
<InnovenergyTextfield
|
||||
id="password-textfield"
|
||||
label="Password"
|
||||
name="password"
|
||||
type="password"
|
||||
value={password}
|
||||
handleChange={(e) => setPassword(e.target.value)}
|
||||
/>
|
||||
{error && <Alert severity="error">Incorrect username or password</Alert>}
|
||||
<Grid container justifyContent="flex-end" sx={{ pt: 1 }}>
|
||||
<InnovenergyButton onClick={handleSubmit} sx={{ my: 1 }}>
|
||||
Login
|
||||
</InnovenergyButton>
|
||||
</Grid>
|
||||
{loading && <CircularProgress sx={{color: theme.palette.secondary.main}} />}
|
||||
</Container>
|
||||
<>
|
||||
<Container maxWidth="xl" sx={{ pt: 2 }}>
|
||||
<Grid container>
|
||||
<Grid item xs={3} container justifyContent="flex-start" mb={2}>
|
||||
<img src={innovenergyLogo} alt="innovenergy logo" height="100" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Container>
|
||||
<Container maxWidth="sm" sx={{ p: 2, alignContent: "center" }}>
|
||||
<InnovenergyTextfield
|
||||
id="username-textfield"
|
||||
label="Username"
|
||||
name="email"
|
||||
value={username}
|
||||
handleChange={(e) => setUsername(e.target.value)}
|
||||
/>
|
||||
<InnovenergyTextfield
|
||||
id="password-textfield"
|
||||
label="Password"
|
||||
name="password"
|
||||
type="password"
|
||||
value={password}
|
||||
handleChange={(e) => setPassword(e.target.value)}
|
||||
/>
|
||||
{error && (
|
||||
<Alert severity="error">Incorrect username or password</Alert>
|
||||
)}
|
||||
<Grid container justifyContent="flex-end" sx={{ pt: 1 }}>
|
||||
<InnovenergyButton
|
||||
onClick={handleSubmit}
|
||||
sx={{ my: 1 }}
|
||||
type="submit"
|
||||
>
|
||||
Login
|
||||
</InnovenergyButton>
|
||||
</Grid>
|
||||
{loading && (
|
||||
<CircularProgress sx={{ color: theme.palette.secondary.main }} />
|
||||
)}
|
||||
</Container>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -27,19 +27,16 @@ const S3CredentialsContextProvider = ({
|
|||
}) => {
|
||||
const [s3Credentials, setS3Credentials] = useState<S3Credentials>();
|
||||
|
||||
console.log("creds", s3Credentials);
|
||||
const saveS3Credentials = (credentials: S3Credentials, id: string) => {
|
||||
//const s3Bucket = id + "-3e5b3069-214a-43ee-8d85-57d72000c19d";
|
||||
/* const s3Bucket = "saliomameiringen";
|
||||
|
||||
setS3Credentials({ s3Bucket, ...credentials });*/
|
||||
setS3Credentials({
|
||||
s3Region: "sos-ch-dk-2",
|
||||
s3Provider: "exo.io",
|
||||
s3Key: "EXO15c0bf710e158e9b83270f0a",
|
||||
s3Secret: "Dd5jYSiZtt_Zt5Ba5mDmaiLCdASUaKLfduSKY-SU-lg",
|
||||
s3Bucket: "saliomameiringen",
|
||||
});
|
||||
const s3Bucket = id + "-3e5b3069-214a-43ee-8d85-57d72000c19d";
|
||||
setS3Credentials({ s3Bucket, ...credentials });
|
||||
/* setS3Credentials({
|
||||
s3Region: "sos-ch-dk-2",
|
||||
s3Provider: "exo.io",
|
||||
s3Key: "EXO15c0bf710e158e9b83270f0a",
|
||||
s3Secret: "Dd5jYSiZtt_Zt5Ba5mDmaiLCdASUaKLfduSKY-SU-lg",
|
||||
s3Bucket: "saliomameiringen",
|
||||
});*/
|
||||
};
|
||||
|
||||
const fetchData = (timestamp: UnixTime): Promise<FetchResult<DataRecord>> => {
|
||||
|
|
|
@ -5,6 +5,7 @@ import {
|
|||
DialogTitle,
|
||||
TextField,
|
||||
colors,
|
||||
Alert,
|
||||
} from "@mui/material";
|
||||
import DialogActions from "@mui/material/DialogActions";
|
||||
import { useContext, useState } from "react";
|
||||
|
@ -19,6 +20,7 @@ import InnovenergyButton from "../../Layout/InnovenergyButton";
|
|||
const AvailableUserDialog = () => {
|
||||
const [selectedUsers, setSelectedUsers] = useState<I_User[]>([]);
|
||||
const [open, setOpen] = useState(false);
|
||||
const [errors, setErrors] = useState<string[]>([]);
|
||||
|
||||
const { currentType } = useContext(GroupContext);
|
||||
const { id } = useParams();
|
||||
|
@ -42,7 +44,8 @@ const AvailableUserDialog = () => {
|
|||
.then(() => {
|
||||
fetchUsersWithDirectAccessForResource();
|
||||
fetchUsersWithInheritedAccessForResource();
|
||||
});
|
||||
})
|
||||
.catch(() => setErrors((errors) => [...errors, user.name]));
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -63,6 +66,17 @@ const AvailableUserDialog = () => {
|
|||
<FormattedMessage id="manageAccess" defaultMessage="Manage access" />
|
||||
</DialogTitle>
|
||||
<DialogContent id="available-user-dialog-content">
|
||||
{errors.length > 0 && (
|
||||
<Alert sx={{ my: 1 }} severity="error">
|
||||
<FormattedMessage
|
||||
id="grantAccessError"
|
||||
defaultMessage="Couldn't grant access to the following users: "
|
||||
/>
|
||||
{errors.map((error, index) =>
|
||||
errors.length === index + 1 ? error : error + ", "
|
||||
)}
|
||||
</Alert>
|
||||
)}
|
||||
<Autocomplete
|
||||
sx={{ width: "500px", pt: 1 }}
|
||||
multiple
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { List } from "@mui/material";
|
||||
import { List, useTheme } from "@mui/material";
|
||||
import { ReactNode } from "react";
|
||||
|
||||
interface InnovenergyListProps {
|
||||
|
@ -7,10 +7,11 @@ interface InnovenergyListProps {
|
|||
}
|
||||
|
||||
const InnovenergyList = (props: InnovenergyListProps) => {
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<List
|
||||
sx={{
|
||||
bgcolor: "white",
|
||||
bgcolor: theme.palette.primary.dark,
|
||||
position: "relative",
|
||||
overflow: "auto",
|
||||
maxHeight: 500,
|
||||
|
|
|
@ -44,7 +44,7 @@ const UsersWithDirectAccess = () => {
|
|||
});
|
||||
};
|
||||
|
||||
if (directAccessUsers && directAccessUsers.length) {
|
||||
if (directAccessUsers && directAccessUsers.length > 0) {
|
||||
return (
|
||||
<>
|
||||
{error && (
|
||||
|
@ -90,7 +90,14 @@ const UsersWithDirectAccess = () => {
|
|||
</>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
return (
|
||||
<Alert severity="info" sx={{ mx: 2 }}>
|
||||
<FormattedMessage
|
||||
id="noDirectAccessUsers"
|
||||
defaultMessage="No direct access users were found"
|
||||
/>
|
||||
</Alert>
|
||||
);
|
||||
};
|
||||
|
||||
export default UsersWithDirectAccess;
|
||||
|
|
|
@ -4,6 +4,7 @@ import {
|
|||
Divider,
|
||||
ListItemAvatar,
|
||||
Avatar,
|
||||
Alert,
|
||||
} from "@mui/material";
|
||||
import { Fragment, useContext, useEffect } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
|
@ -24,7 +25,7 @@ const UsersWithInheritedAccess = () => {
|
|||
fetchUsersWithInheritedAccessForResource();
|
||||
}, [fetchUsersWithInheritedAccessForResource]);
|
||||
|
||||
if (inheritedAccessUsers && inheritedAccessUsers.length) {
|
||||
if (inheritedAccessUsers && inheritedAccessUsers.length > 0) {
|
||||
return (
|
||||
<>
|
||||
{filterDuplicateUsers(inheritedAccessUsers).map(
|
||||
|
@ -73,7 +74,14 @@ const UsersWithInheritedAccess = () => {
|
|||
</>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
return (
|
||||
<Alert severity="info" sx={{ m: 2 }}>
|
||||
<FormattedMessage
|
||||
id="noInheritedAccessUsers"
|
||||
defaultMessage="No inherited access users were found"
|
||||
/>
|
||||
</Alert>
|
||||
);
|
||||
};
|
||||
|
||||
export default UsersWithInheritedAccess;
|
||||
|
|
|
@ -1,43 +1,31 @@
|
|||
import { Dialog, DialogContent, DialogTitle, IconButton } from "@mui/material";
|
||||
import { useState } from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
import axiosConfig from "../../config/axiosConfig";
|
||||
import { I_Folder } from "../../util/types";
|
||||
import FolderForm from "./FolderForm";
|
||||
import CloseIcon from "@mui/icons-material/Close";
|
||||
|
||||
import { ReactNode, useState } from "react";
|
||||
import InnovenergyButton from "../Layout/InnovenergyButton";
|
||||
|
||||
interface AddNewDialogProps {
|
||||
values: I_Folder;
|
||||
form: ReactNode;
|
||||
message: ReactNode;
|
||||
id: number;
|
||||
}
|
||||
|
||||
const AddNewDialog = (props: AddNewDialogProps) => {
|
||||
const [open, setOpen] = useState(false);
|
||||
const { form, id, message } = props;
|
||||
const intl = useIntl();
|
||||
|
||||
const handleSubmit = (data: I_Folder, childData: Partial<I_Folder>) => {
|
||||
return axiosConfig
|
||||
.post("/CreateFolder", {
|
||||
...childData,
|
||||
parentId: data.id,
|
||||
})
|
||||
.then((res) => {
|
||||
setOpen(false);
|
||||
return res;
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<InnovenergyButton
|
||||
id={"add-new-child-dialog-button-" + props.values.id}
|
||||
id={"add-new-child-dialog-button-" + id}
|
||||
sx={{ ml: 2 }}
|
||||
onClick={() => setOpen(true)}
|
||||
>
|
||||
<FormattedMessage id="addNewChild" defaultMessage="Add new child" />
|
||||
{message}
|
||||
</InnovenergyButton>
|
||||
<Dialog
|
||||
id={"add-new-child-dialog-" + props.values.id}
|
||||
id={"add-new-child-dialog-" + id}
|
||||
onClose={() => setOpen(false)}
|
||||
aria-labelledby="customized-dialog-title"
|
||||
open={open}
|
||||
|
@ -50,12 +38,9 @@ const AddNewDialog = (props: AddNewDialogProps) => {
|
|||
maxWidth="sm"
|
||||
>
|
||||
<DialogTitle>
|
||||
<FormattedMessage
|
||||
id="createNewFolder"
|
||||
defaultMessage="Create new folder"
|
||||
/>
|
||||
<FormattedMessage id="createNew" defaultMessage="Create new" />
|
||||
<IconButton
|
||||
id={"add-new-child-dialog-icon-button-" + props.values.id}
|
||||
id={"add-new-child-dialog-icon-button-" + id}
|
||||
edge="start"
|
||||
color="inherit"
|
||||
onClick={() => setOpen(false)}
|
||||
|
@ -69,14 +54,10 @@ const AddNewDialog = (props: AddNewDialogProps) => {
|
|||
top: 8,
|
||||
}}
|
||||
>
|
||||
<CloseIcon
|
||||
id={"add-new-child-dialog-close-icon-" + props.values.id}
|
||||
/>
|
||||
<CloseIcon id={"add-new-child-dialog-close-icon-" + id} />
|
||||
</IconButton>
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<FolderForm values={props.values} handleSubmit={handleSubmit} />
|
||||
</DialogContent>
|
||||
<DialogContent>{form}</DialogContent>
|
||||
</Dialog>
|
||||
</>
|
||||
);
|
|
@ -0,0 +1,83 @@
|
|||
import { Dialog, DialogContent, DialogTitle, IconButton } from "@mui/material";
|
||||
import { useState } from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
import axiosConfig from "../../config/axiosConfig";
|
||||
import { I_Folder, I_Installation } from "../../util/types";
|
||||
import FolderForm from "./FolderForm";
|
||||
import CloseIcon from "@mui/icons-material/Close";
|
||||
import InnovenergyButton from "../Layout/InnovenergyButton";
|
||||
import InstallationForm from "../Installations/InstallationForm";
|
||||
import AddNewDialog from "./AddNew";
|
||||
|
||||
interface AddNewDialogProps {
|
||||
values: I_Folder | I_Installation;
|
||||
isFolder: boolean;
|
||||
}
|
||||
|
||||
const AddNewButtons = (props: AddNewDialogProps) => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const handleFolderSubmit = (data: I_Folder, childData: Partial<I_Folder>) => {
|
||||
return axiosConfig
|
||||
.post("/CreateFolder", {
|
||||
...childData,
|
||||
parentId: data.id,
|
||||
})
|
||||
.then((res) => {
|
||||
setOpen(false);
|
||||
return res;
|
||||
});
|
||||
};
|
||||
|
||||
const handleInstallationSubmit = (
|
||||
data: I_Installation,
|
||||
childData: Partial<I_Installation>
|
||||
) => {
|
||||
return axiosConfig
|
||||
.post("/CreateInstallation", {
|
||||
...childData,
|
||||
parentId: data.id,
|
||||
})
|
||||
.then((res) => {
|
||||
setOpen(false);
|
||||
return res;
|
||||
});
|
||||
};
|
||||
|
||||
const folderForm = (
|
||||
<FolderForm
|
||||
values={props.values as I_Folder}
|
||||
handleSubmit={handleFolderSubmit}
|
||||
/>
|
||||
);
|
||||
const installationForm = (
|
||||
<InstallationForm
|
||||
values={props.values as I_Installation}
|
||||
handleSubmit={handleInstallationSubmit}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<AddNewDialog
|
||||
form={installationForm}
|
||||
message={
|
||||
<FormattedMessage
|
||||
id="addNewInstallation"
|
||||
defaultMessage="Add new installation"
|
||||
/>
|
||||
}
|
||||
id={props.values.id}
|
||||
/>
|
||||
<AddNewDialog
|
||||
form={folderForm}
|
||||
message={
|
||||
<FormattedMessage id="addNewFolder" defaultMessage="Add new folder" />
|
||||
}
|
||||
id={props.values.id}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddNewButtons;
|
|
@ -4,7 +4,7 @@ import { useState, useEffect } from "react";
|
|||
import { useParams } from "react-router-dom";
|
||||
import axiosConfig from "../../config/axiosConfig";
|
||||
import { I_Folder } from "../../util/types";
|
||||
import AddNewDialog from "./AddNewDialog";
|
||||
import AddNewButtons from "./AddNewButtons";
|
||||
import FolderForm from "./FolderForm";
|
||||
import MoveDialog from "./Tree/MoveDialog";
|
||||
import { colors } from "index";
|
||||
|
@ -36,7 +36,7 @@ const Folder = () => {
|
|||
|
||||
if (values && values.id && values.id.toString() === id) {
|
||||
const moveDialog = <MoveDialog values={values} />;
|
||||
const addNewDialog = <AddNewDialog values={values} />;
|
||||
const addNewDialog = <AddNewButtons values={values} isFolder />;
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
|
@ -7,7 +7,7 @@ import { I_Folder } from "../../util/types";
|
|||
import { GroupContext } from "../Context/GroupContextProvider";
|
||||
import InnovenergySnackbar from "../InnovenergySnackbar";
|
||||
import InnovenergyButton from "../Layout/InnovenergyButton";
|
||||
import InnovenergyTextfield, {
|
||||
import {
|
||||
I_InnovenergyTextfieldProps,
|
||||
IePropertyGrid,
|
||||
} from "../Layout/InnovenergyTextfield";
|
||||
|
|
|
@ -11,7 +11,7 @@ import { instanceOfFolder } from "../../../util/group.util";
|
|||
import { Grid, CircularProgress, useTheme } from "@mui/material";
|
||||
import { useIntl } from "react-intl";
|
||||
import { colors } from "../../..";
|
||||
import { color } from "chart.js/helpers";
|
||||
import InnovEnergyTreeItem from "../../Layout/InnovEnergyTreeItem";
|
||||
|
||||
const GroupTree = () => {
|
||||
const { setCurrentType, fetchData, data, loading } = useContext(GroupContext);
|
||||
|
@ -48,27 +48,15 @@ const GroupTree = () => {
|
|||
}}
|
||||
draggable={false}
|
||||
>
|
||||
<TreeItem
|
||||
<InnovEnergyTreeItem
|
||||
id={"group-tree-item-" + element.id}
|
||||
key={element.type + element.id}
|
||||
nodeId={element.type + element.id}
|
||||
label={element.name}
|
||||
onClick={() => setCurrentType(element.type)}
|
||||
sx={{
|
||||
".MuiTreeItem-content": { py: "10px" },
|
||||
".Mui-selected": {
|
||||
backgroundColor: theme.palette.secondary.main,
|
||||
},
|
||||
".Mui-selected:hover": {
|
||||
backgroundColor: theme.palette.secondary.main,
|
||||
},
|
||||
".Mui-focused": { backgroundColor: theme.palette.secondary.main },
|
||||
bgcolor: colors.greyDark,
|
||||
borderRadius: 2,
|
||||
}}
|
||||
>
|
||||
{getNodes(element)}
|
||||
</TreeItem>
|
||||
</InnovEnergyTreeItem>
|
||||
</Link>
|
||||
);
|
||||
});
|
||||
|
@ -94,6 +82,9 @@ const GroupTree = () => {
|
|||
flexGrow: 1,
|
||||
overflow: "auto",
|
||||
overflowX: "hidden",
|
||||
".Mui-selected": {
|
||||
borderRadius: "4px",
|
||||
},
|
||||
}}
|
||||
expanded={openNodes}
|
||||
onNodeToggle={(e, ids) => {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { DialogActions, Dialog, DialogTitle } from "@mui/material";
|
||||
import { DialogActions, Dialog, DialogTitle, Alert } from "@mui/material";
|
||||
import DialogContent from "@mui/material/DialogContent";
|
||||
import { useContext, useState } from "react";
|
||||
import React, { useContext, useState } from "react";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
import { useParams } from "react-router-dom";
|
||||
import axiosConfig from "../../../config/axiosConfig";
|
||||
|
@ -12,15 +12,18 @@ import MoveTree from "./MoveTree";
|
|||
interface MoveDialogProps {
|
||||
values: I_Folder;
|
||||
}
|
||||
|
||||
const MoveDialog = (props: MoveDialogProps) => {
|
||||
const { values } = props;
|
||||
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const [selectedParentId, setSelectedParentId] = useState<number>(
|
||||
values.parentId
|
||||
);
|
||||
const { fetchData, currentType } = useContext(GroupContext);
|
||||
const { id } = useParams();
|
||||
const [error, setError] = useState();
|
||||
|
||||
const handleMove = () => {
|
||||
const route =
|
||||
|
@ -33,9 +36,9 @@ const MoveDialog = (props: MoveDialogProps) => {
|
|||
.then((res) => {
|
||||
setOpen(false);
|
||||
fetchData();
|
||||
});
|
||||
})
|
||||
.catch((err) => setError(err));
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<InnovenergyButton
|
||||
|
@ -59,7 +62,16 @@ const MoveDialog = (props: MoveDialogProps) => {
|
|||
<DialogTitle>
|
||||
<FormattedMessage id="moveTo" defaultMessage="Move to" />
|
||||
</DialogTitle>
|
||||
|
||||
<DialogContent>
|
||||
{error && (
|
||||
<Alert severity={"error"}>
|
||||
<FormattedMessage
|
||||
id="moveInstallationError"
|
||||
defaultMessage="Couldn't move element, an error occured"
|
||||
/>
|
||||
</Alert>
|
||||
)}
|
||||
<MoveTree
|
||||
parentId={selectedParentId}
|
||||
setSelectedParentId={setSelectedParentId}
|
||||
|
|
|
@ -2,11 +2,11 @@ import TreeView from "@mui/lab/TreeView";
|
|||
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
|
||||
import ChevronRightIcon from "@mui/icons-material/ChevronRight";
|
||||
import { ReactNode, useContext } from "react";
|
||||
import { TreeItem } from "@mui/lab";
|
||||
import { I_Folder, I_Installation } from "../../../util/types";
|
||||
import { GroupContext } from "../../Context/GroupContextProvider";
|
||||
import { instanceOfFolder } from "../../../util/group.util";
|
||||
import { useIntl } from "react-intl";
|
||||
import InnovEnergyTreeItem from "../../Layout/InnovEnergyTreeItem";
|
||||
|
||||
interface MoveTreeProps {
|
||||
setSelectedParentId: (value: number) => void;
|
||||
|
@ -29,14 +29,15 @@ const MoveTree = (props: MoveTreeProps) => {
|
|||
.filter((element) => element.type === "Folder")
|
||||
.map((element) => {
|
||||
return (
|
||||
<TreeItem
|
||||
<InnovEnergyTreeItem
|
||||
id={"move-tree-item-" + element.id}
|
||||
key={"move-tree-item-" + element.id}
|
||||
nodeId={element.id.toString()}
|
||||
color="primary"
|
||||
label={element.name}
|
||||
>
|
||||
{getNodes(element)}
|
||||
</TreeItem>
|
||||
</InnovEnergyTreeItem>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
|
|
@ -6,6 +6,7 @@ interface InnovenergySnackbarProps {
|
|||
setOpen: (value: boolean) => void;
|
||||
error?: any;
|
||||
}
|
||||
|
||||
const InnovenergySnackbar = (props: InnovenergySnackbarProps) => {
|
||||
const { open, setOpen, error } = props;
|
||||
|
||||
|
|
|
@ -3,10 +3,11 @@ import { AxiosError } from "axios";
|
|||
import { useContext, useEffect, useState } from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
import axiosConfig from "../../config/axiosConfig";
|
||||
import { I_Installation } from "../../util/types";
|
||||
import { I_Folder, I_Installation } from "../../util/types";
|
||||
import InstallationForm from "./InstallationForm";
|
||||
import { colors } from "../..";
|
||||
import { S3CredentialsContext } from "../Context/S3CredentialsContextProvider";
|
||||
import MoveDialog from "../Groups/Tree/MoveDialog";
|
||||
|
||||
interface I_InstallationProps {
|
||||
loading?: boolean;
|
||||
|
@ -16,11 +17,16 @@ interface I_InstallationProps {
|
|||
}
|
||||
|
||||
const Installation = (props: I_InstallationProps) => {
|
||||
const { hasMoveButton, values, loading, error } = props;
|
||||
const { values, loading, error } = props;
|
||||
const { id } = useParams();
|
||||
const theme = useTheme();
|
||||
|
||||
const handleSubmit = (data: I_Folder, formikValues: Partial<I_Folder>) => {
|
||||
return axiosConfig.put("/UpdateInstallation", { ...data, ...formikValues });
|
||||
};
|
||||
|
||||
if (values && values.id && values.id.toString() === id) {
|
||||
const moveDialog = <MoveDialog values={values} />;
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
|
@ -39,9 +45,15 @@ const Installation = (props: I_InstallationProps) => {
|
|||
>
|
||||
<InstallationForm
|
||||
values={values}
|
||||
id={id}
|
||||
hasMoveButton={hasMoveButton}
|
||||
handleSubmit={handleSubmit}
|
||||
additionalButtons={props.hasMoveButton ? [moveDialog] : []}
|
||||
/>
|
||||
<Box>
|
||||
{values.s3WriteKey && <div>{`Write key: ${values.s3WriteKey}`}</div>}
|
||||
{values.s3WriteSecret && (
|
||||
<div>{`Write secret: ${values.s3WriteSecret}`}</div>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
} else if (loading) {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { Alert, Grid, Snackbar } from "@mui/material";
|
||||
import { useFormik } from "formik";
|
||||
import { useContext, useState } from "react";
|
||||
import { ReactNode, useContext, useState } from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
import axiosConfig from "../../config/axiosConfig";
|
||||
import { I_Installation } from "../../util/types";
|
||||
import { I_Folder, I_Installation } from "../../util/types";
|
||||
import { InstallationsContext } from "../Context/InstallationsContextProvider";
|
||||
import MoveDialog from "../Groups/Tree/MoveDialog";
|
||||
import InnovenergyButton from "../Layout/InnovenergyButton";
|
||||
|
@ -13,15 +13,19 @@ import InnovenergyTextfield, {
|
|||
} from "../Layout/InnovenergyTextfield";
|
||||
import { UserContext } from "../Context/UserContextProvider";
|
||||
import * as Yup from "yup";
|
||||
import { AxiosResponse } from "axios";
|
||||
|
||||
interface I_InstallationFormProps {
|
||||
values: I_Installation;
|
||||
id: string | undefined;
|
||||
hasMoveButton?: boolean;
|
||||
handleSubmit: (
|
||||
data: I_Installation,
|
||||
formikValues: Partial<I_Installation>
|
||||
) => Promise<AxiosResponse>;
|
||||
additionalButtons?: ReactNode[];
|
||||
}
|
||||
|
||||
const InstallationForm = (props: I_InstallationFormProps) => {
|
||||
const { values, id, hasMoveButton } = props;
|
||||
const { values, additionalButtons, handleSubmit } = props;
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const { fetchData } = useContext(InstallationsContext);
|
||||
|
@ -53,22 +57,18 @@ const InstallationForm = (props: I_InstallationFormProps) => {
|
|||
|
||||
const formik = useFormik({
|
||||
initialValues: {
|
||||
name: values.name,
|
||||
name: values.region ? values.name : "",
|
||||
region: values.region,
|
||||
location: values.location,
|
||||
country: values.country,
|
||||
orderNumbers: values.orderNumbers,
|
||||
},
|
||||
onSubmit: (formikValues) => {
|
||||
axiosConfig
|
||||
.put("/UpdateInstallation", {
|
||||
...formikValues,
|
||||
id,
|
||||
})
|
||||
handleSubmit(values, formikValues)
|
||||
.then(() => {
|
||||
setOpen(true);
|
||||
fetchData();
|
||||
});
|
||||
})
|
||||
.catch((err) => {});
|
||||
},
|
||||
validationSchema,
|
||||
});
|
||||
|
@ -152,7 +152,10 @@ const InstallationForm = (props: I_InstallationFormProps) => {
|
|||
<form onSubmit={formik.handleSubmit}>
|
||||
<IePropertyGrid rows={rows} />
|
||||
<Grid container justifyContent="flex-end" sx={{ pt: 1 }}>
|
||||
{hasMoveButton && !readOnly && <MoveDialog values={values} />}
|
||||
{/*{hasMoveButton && !readOnly && <MoveDialog values={values} />}*/}
|
||||
{!readOnly &&
|
||||
additionalButtons &&
|
||||
additionalButtons.map((button) => button)}
|
||||
{!readOnly && (
|
||||
<InnovenergyButton id="installation-form-submit-button" type="submit">
|
||||
<FormattedMessage
|
||||
|
|
|
@ -15,6 +15,7 @@ import { Fragment, useContext, useEffect } from "react";
|
|||
import { I_Installation } from "../../util/types";
|
||||
import { InstallationsContext } from "../Context/InstallationsContextProvider";
|
||||
import { colors } from "../..";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
|
||||
interface InstallationListProps {
|
||||
searchQuery: string;
|
||||
|
@ -65,7 +66,7 @@ const InstallationList = (props: InstallationListProps) => {
|
|||
<CircularProgress sx={{ m: 6, color: theme.palette.secondary.main }} />
|
||||
</Grid>
|
||||
);
|
||||
} else if (data && data.length) {
|
||||
} else if (data && data.length > 0) {
|
||||
return (
|
||||
<List
|
||||
sx={{
|
||||
|
@ -90,7 +91,10 @@ const InstallationList = (props: InstallationListProps) => {
|
|||
<Link
|
||||
id={"installation-list-link-" + installation.id}
|
||||
to={
|
||||
getPathWithoutId(routeMatch?.pattern?.path) + installation.id
|
||||
routes.installations +
|
||||
routes.list +
|
||||
routes.installation +
|
||||
installation.id
|
||||
}
|
||||
style={{ textDecoration: "none", color: colors.black }}
|
||||
>
|
||||
|
@ -131,6 +135,12 @@ const InstallationList = (props: InstallationListProps) => {
|
|||
{error.message}
|
||||
</Alert>
|
||||
);
|
||||
} else if (data.length === 0) {
|
||||
return (
|
||||
<Alert severity="info" sx={{ mt: 1 }}>
|
||||
<FormattedMessage id="noData" defaultMessage="No data found" />
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
|
|
@ -11,22 +11,8 @@ import Installation from "./Installation";
|
|||
import CheckboxTree from "./Log/CheckboxTree";
|
||||
import LogContextProvider from "../Context/LogContextProvider";
|
||||
import useRouteMatch from "../../hooks/useRouteMatch";
|
||||
import { Background } from "reactflow";
|
||||
import { red } from "@mui/material/colors";
|
||||
import S3CredentialsContextProvider, {
|
||||
S3CredentialsContext,
|
||||
} from "../Context/S3CredentialsContextProvider";
|
||||
import { UnixTime } from "../../dataCache/time";
|
||||
import { FetchResult } from "../../dataCache/dataCache";
|
||||
import { DataRecord } from "../../dataCache/data";
|
||||
import { S3Access } from "../../dataCache/S3/S3Access";
|
||||
import { parseCsv } from "../../util/graph.util";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { useContext, useEffect, useState } from "react";
|
||||
import axiosConfig from "../../config/axiosConfig";
|
||||
import { I_Installation } from "../../util/types";
|
||||
import { AxiosError } from "axios/index";
|
||||
import useInstallation from "../../hooks/useInstallation";
|
||||
import { useEffect } from "react";
|
||||
|
||||
const Installations = () => {
|
||||
const routeMatch = useRouteMatch([
|
||||
|
|
|
@ -18,6 +18,7 @@ const LiveView = () => {
|
|||
borderBottomLeftRadius: 4,
|
||||
borderBottomRightRadius: 4,
|
||||
borderColor: theme.palette.text.disabled,
|
||||
overflowX: "auto",
|
||||
}}
|
||||
>
|
||||
<TopologyView />
|
||||
|
|
|
@ -8,6 +8,7 @@ import useRouteMatch from "../../../hooks/useRouteMatch";
|
|||
import routes from "../../../routes.json";
|
||||
import React from "react";
|
||||
import { colors } from "index";
|
||||
import InnovEnergyTreeItem from "../../Layout/InnovEnergyTreeItem";
|
||||
|
||||
export interface ToggleElement {
|
||||
[key: string]: boolean;
|
||||
|
@ -54,7 +55,7 @@ const CheckboxTree = () => {
|
|||
const checked = checkedToggles.find((toggle) => element.id === toggle);
|
||||
const splitName = element.name.split("/");
|
||||
return (
|
||||
<TreeItem
|
||||
<InnovEnergyTreeItem
|
||||
id={"checkbox-tree-" + element.name}
|
||||
key={element.name}
|
||||
nodeId={element.name}
|
||||
|
@ -74,16 +75,9 @@ const CheckboxTree = () => {
|
|||
{splitName[splitName.length - 1]}
|
||||
</>
|
||||
}
|
||||
sx={{
|
||||
"& .MuiTreeItem-content": {
|
||||
paddingY: "5px",
|
||||
minHeight: "52px",
|
||||
},
|
||||
bgcolor: colors.greyDark,
|
||||
}}
|
||||
>
|
||||
{getNodes(element)}
|
||||
</TreeItem>
|
||||
</InnovEnergyTreeItem>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
|
|
@ -28,8 +28,9 @@ const NUMBER_OF_NODES = 100;
|
|||
|
||||
const ScalarGraph = () => {
|
||||
const timeRange = createTimes(
|
||||
UnixTime.now() /* .fromTicks(1682085650) */
|
||||
.rangeBefore(TimeSpan.fromDays(10)),
|
||||
UnixTime.now()
|
||||
/* .fromTicks(1682085650) */
|
||||
.rangeBefore(TimeSpan.fromDays(1)),
|
||||
NUMBER_OF_NODES
|
||||
);
|
||||
const [timeSeries, setTimeSeries] = useState<RecordSeries>([]);
|
||||
|
@ -56,6 +57,8 @@ const ScalarGraph = () => {
|
|||
setTimeSeries(timeSeries);
|
||||
|
||||
const toggleValues = timeSeries.find((timeStamp) => timeStamp.value);
|
||||
console.log("toggles", timeSeries, toggleValues);
|
||||
|
||||
if (toggles === null && toggleValues && toggleValues.value) {
|
||||
const treeElements = getTreeElements(toggleValues.value);
|
||||
setToggles(treeElements);
|
||||
|
@ -123,7 +126,6 @@ const ScalarGraph = () => {
|
|||
|
||||
const handleRelayout = useCallback(
|
||||
(params: PlotRelayoutEvent) => {
|
||||
console.log("relayout");
|
||||
const xaxisRange0 = params["xaxis.range[0]"];
|
||||
const xaxisRange1 = params["xaxis.range[1]"];
|
||||
if (xaxisRange0 && xaxisRange1) {
|
||||
|
@ -138,6 +140,7 @@ const ScalarGraph = () => {
|
|||
|
||||
const theme = useTheme();
|
||||
const renderGraphs = () => {
|
||||
console.log("toggles", toggles);
|
||||
if (checkedToggles.length > 0) {
|
||||
const coordinateTimeSeries = transformToGraphData(timeSeries);
|
||||
const visibleGraphs = Object.keys(coordinateTimeSeries).filter((path) => {
|
||||
|
@ -209,18 +212,22 @@ const ScalarGraph = () => {
|
|||
</div>
|
||||
);
|
||||
}
|
||||
} else if (toggles === null) {
|
||||
return (
|
||||
<Alert sx={{ mt: 2 }} severity="info">
|
||||
<Alert sx={{ mt: 2 }} severity="error">
|
||||
<FormattedMessage
|
||||
id="makeASelection"
|
||||
defaultMessage="Please make a selection on the left"
|
||||
id="noGraphData"
|
||||
defaultMessage="Couldn't find any graph data"
|
||||
/>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Alert sx={{ mt: 2 }} severity="error">
|
||||
<FormattedMessage id="error" defaultMessage="There was an error" />
|
||||
<Alert sx={{ mt: 2 }} severity="info">
|
||||
<FormattedMessage
|
||||
id="makeASelection"
|
||||
defaultMessage="Please make a selection on the left"
|
||||
/>
|
||||
</Alert>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -24,62 +24,68 @@ export const BOX_SIZE = 75;
|
|||
const TopologyBox = (props: TopologyBoxProps) => {
|
||||
const { titleColor, boxColor } = getBoxColor(props.title);
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
visibility: props.title ? "visible" : "hidden",
|
||||
height: BOX_SIZE + "px",
|
||||
width: BOX_SIZE + "px",
|
||||
borderRadius: "4px",
|
||||
color: "white",
|
||||
}}
|
||||
>
|
||||
<p
|
||||
style={{
|
||||
marginBlockStart: "0",
|
||||
marginBlockEnd: "0",
|
||||
backgroundColor: titleColor,
|
||||
padding: "5px",
|
||||
borderTopLeftRadius: "4px",
|
||||
borderTopRightRadius: "4px",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
<div style={{ height: BOX_SIZE, width: BOX_SIZE }}>
|
||||
<Box
|
||||
sx={{
|
||||
visibility: props.title ? "visible" : "hidden",
|
||||
height: BOX_SIZE + "px",
|
||||
width: BOX_SIZE + "px",
|
||||
maxHeight: BOX_SIZE + "px",
|
||||
borderRadius: "4px",
|
||||
color: "white",
|
||||
overflow: "hidden",
|
||||
}}
|
||||
>
|
||||
{props.title}
|
||||
</p>
|
||||
<div
|
||||
style={{
|
||||
backgroundColor: boxColor,
|
||||
borderBottomLeftRadius: "4px",
|
||||
borderBottomRightRadius: "4px",
|
||||
padding: "5px",
|
||||
height: "51px",
|
||||
}}
|
||||
>
|
||||
{props.data && (
|
||||
<>
|
||||
{props.data.values.map((boxData, index) => {
|
||||
console.log("boxData", boxData);
|
||||
return (
|
||||
<p
|
||||
key={props.title + "-" + index}
|
||||
style={{ marginBlockStart: "0", marginBlockEnd: "2px" }}
|
||||
// eslint-disable-next-line formatjs/no-literal-string-in-jsx
|
||||
>{`${
|
||||
props.data && props.data.values.length === 3
|
||||
? "L" + (index + 1) + " "
|
||||
: ""
|
||||
}${
|
||||
!isInt(Number(boxData.value))
|
||||
? Number(boxData.value).toPrecision(4)
|
||||
: boxData.value
|
||||
}${boxData.unit}`}</p>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</Box>
|
||||
<p
|
||||
style={{
|
||||
marginBlockStart: "0",
|
||||
marginBlockEnd: "0",
|
||||
backgroundColor: titleColor,
|
||||
padding: "5px",
|
||||
borderTopLeftRadius: "4px",
|
||||
borderTopRightRadius: "4px",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
textOverflow: "ellipsis",
|
||||
whiteSpace: "nowrap",
|
||||
overflow: "hidden",
|
||||
}}
|
||||
>
|
||||
{props.title}
|
||||
</p>
|
||||
<div
|
||||
style={{
|
||||
backgroundColor: boxColor,
|
||||
borderBottomLeftRadius: "4px",
|
||||
borderBottomRightRadius: "4px",
|
||||
padding: "5px",
|
||||
height: BOX_SIZE - 34,
|
||||
}}
|
||||
>
|
||||
{props.data && (
|
||||
<>
|
||||
{props.data.values.map((boxData, index) => {
|
||||
return (
|
||||
<p
|
||||
key={props.title + "-" + index}
|
||||
style={{ marginBlockStart: "0", marginBlockEnd: "2px" }}
|
||||
// eslint-disable-next-line formatjs/no-literal-string-in-jsx
|
||||
>{`${
|
||||
props.data && props.data.values.length === 3
|
||||
? "L" + (index + 1) + " "
|
||||
: ""
|
||||
}${
|
||||
!isInt(Number(boxData.value))
|
||||
? Number(boxData.value).toPrecision(4)
|
||||
: boxData.value
|
||||
}${boxData.unit}`}</p>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</Box>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ type TopologyColumnProps = {
|
|||
centerConnection?: TopologyFlowProps;
|
||||
bottomBox?: TopologyBoxProps;
|
||||
bottomConnection?: TopologyFlowProps;
|
||||
isLastColumn?: boolean;
|
||||
};
|
||||
|
||||
const TopologyColumn = (props: TopologyColumnProps) => {
|
||||
|
@ -37,13 +38,15 @@ const TopologyColumn = (props: TopologyColumnProps) => {
|
|||
/>
|
||||
<TopologyBox {...props.bottomBox} />
|
||||
</div>
|
||||
<div>
|
||||
<TopologyFlow
|
||||
{...props.centerConnection}
|
||||
orientation="horizontal"
|
||||
hidden={!props.centerBox}
|
||||
/>
|
||||
</div>
|
||||
{!props.isLastColumn && (
|
||||
<div>
|
||||
<TopologyFlow
|
||||
{...props.centerConnection}
|
||||
orientation="horizontal"
|
||||
hidden={!props.centerBox}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -9,7 +9,9 @@ export type TopologyFlowProps = {
|
|||
hidden?: boolean;
|
||||
};
|
||||
const TopologyFlow = (props: TopologyFlowProps) => {
|
||||
const length = Math.abs((props.amount ?? 1) * BOX_SIZE);
|
||||
const length = Math.abs(
|
||||
((props.amount && props.amount < 0.1 ? 0.1 : props.amount) ?? 0) * BOX_SIZE
|
||||
);
|
||||
const values = props.data?.values;
|
||||
return (
|
||||
<div
|
||||
|
@ -45,7 +47,8 @@ const TopologyFlow = (props: TopologyFlowProps) => {
|
|||
{values
|
||||
?.filter((boxData) => (boxData.value as number) !== 0)
|
||||
.map(
|
||||
(boxData) => `${Math.round(boxData.value as number)} ${boxData.unit}`
|
||||
(boxData) =>
|
||||
`${Math.round(boxData.value as number)} ${boxData.unit}`
|
||||
)}
|
||||
</p>
|
||||
<div
|
||||
|
|
|
@ -15,6 +15,7 @@ import { S3CredentialsContext } from "../../Context/S3CredentialsContextProvider
|
|||
const TopologyView = () => {
|
||||
const [values, setValues] = useState<TopologyValues | null>(null);
|
||||
const { fetchData } = useContext(S3CredentialsContext);
|
||||
const theme = useTheme();
|
||||
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
|
@ -37,8 +38,7 @@ const TopologyView = () => {
|
|||
}, 2000);
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
const theme = useTheme();
|
||||
}, [fetchData]);
|
||||
|
||||
if (values) {
|
||||
const highestConnectionValue = getHighestConnectionValue(values);
|
||||
|
@ -47,19 +47,13 @@ const TopologyView = () => {
|
|||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
paddingTop: 3,
|
||||
paddingBottom: 3,
|
||||
padding: 3,
|
||||
bgcolor: "white",
|
||||
px: 3 / 8,
|
||||
border: 2,
|
||||
borderTop: 0,
|
||||
borderBottomLeftRadius: 4,
|
||||
borderBottomRightRadius: 4,
|
||||
borderColor: theme.palette.text.disabled,
|
||||
borderTopColor: "white",
|
||||
marginTop: 0.05,
|
||||
fontFamily: `"Ubuntu", sans-serif`,
|
||||
fontSize: "12px",
|
||||
justifyContent: "center",
|
||||
width: "fit-content",
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
|
@ -217,6 +211,7 @@ const TopologyView = () => {
|
|||
title: "Battery",
|
||||
data: values.battery,
|
||||
}}
|
||||
isLastColumn
|
||||
/>
|
||||
</div>
|
||||
</Box>
|
||||
|
|
|
@ -1,57 +0,0 @@
|
|||
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<Node[]>(initialNodes);
|
||||
const [edges, setEdges] = useState<Edge[]>(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 (
|
||||
<ReactFlow
|
||||
nodes={nodes}
|
||||
edges={edges}
|
||||
onNodesChange={onNodesChange}
|
||||
onEdgesChange={onEdgesChange}
|
||||
onConnect={onConnect}
|
||||
fitView
|
||||
fitViewOptions={fitViewOptions}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default Flow;
|
|
@ -0,0 +1,40 @@
|
|||
import { TreeItem, TreeItemProps } from "@mui/lab";
|
||||
import { colors } from "../../index";
|
||||
import { useTheme } from "@mui/material";
|
||||
|
||||
const InnovEnergyTreeItem = (props: TreeItemProps) => {
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<TreeItem
|
||||
id={props.id}
|
||||
nodeId={props.nodeId}
|
||||
label={props.label}
|
||||
onClick={() => props.onClick}
|
||||
sx={{
|
||||
".MuiTreeItem-content": {
|
||||
py: "10px",
|
||||
borderRadius: "4px",
|
||||
width: "inherit",
|
||||
},
|
||||
".Mui-selected": {
|
||||
backgroundColor: theme.palette.secondary.main + "!important",
|
||||
borderRadius: "4px",
|
||||
},
|
||||
".MuiTreeItem-content:hover": {
|
||||
borderRadius: "4px",
|
||||
backgroundColor: theme.palette.secondary.light + "!important",
|
||||
},
|
||||
".MuiTreeItem-content.Mui-selected:hover": {
|
||||
borderRadius: "4px",
|
||||
backgroundColor: theme.palette.secondary.main + "!important",
|
||||
},
|
||||
borderRadius: "4px",
|
||||
bgcolor: colors.greyDark,
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
</TreeItem>
|
||||
);
|
||||
};
|
||||
|
||||
export default InnovEnergyTreeItem;
|
|
@ -1,12 +1,11 @@
|
|||
import { Button, SxProps, Theme, colors, useTheme } from "@mui/material";
|
||||
import { DAY_MARGIN } from "@mui/x-date-pickers/internals";
|
||||
import { ReactNode } from "react";
|
||||
import { Background } from "reactflow";
|
||||
|
||||
interface I_InnovenergyButtonProps {
|
||||
children?: ReactNode;
|
||||
type?: "button" | "submit" | "reset" | undefined;
|
||||
onClick?: React.MouseEventHandler<HTMLButtonElement>;
|
||||
onKeyDown?: React.KeyboardEventHandler<HTMLButtonElement>;
|
||||
sx?: SxProps<Theme> | undefined;
|
||||
id?: string;
|
||||
}
|
||||
|
@ -19,13 +18,14 @@ const InnovenergyButton = (props: I_InnovenergyButtonProps) => {
|
|||
variant="contained"
|
||||
type={props.type}
|
||||
onClick={props.onClick}
|
||||
sx={{...props.sx,
|
||||
onKeyDown={props.onKeyDown}
|
||||
sx={{
|
||||
...props.sx,
|
||||
textTransform: "none",
|
||||
bgcolor: theme.palette.secondary.main,
|
||||
":hover":{
|
||||
":hover": {
|
||||
bgcolor: theme.palette.secondary.dark,
|
||||
}
|
||||
|
||||
},
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
|
@ -33,6 +33,4 @@ const InnovenergyButton = (props: I_InnovenergyButtonProps) => {
|
|||
);
|
||||
};
|
||||
|
||||
|
||||
export default InnovenergyButton;
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { styled, SxProps, Tab, Tabs, Theme, useTheme } from "@mui/material";
|
||||
import { TabProps } from "@mui/material/Tab/Tab";
|
||||
import { Tab, useTheme } from "@mui/material";
|
||||
import { colors } from "index";
|
||||
|
||||
const InnovenergyTab = (props: any) => {
|
||||
|
@ -21,43 +20,8 @@ const InnovenergyTab = (props: any) => {
|
|||
borderTopRightRadius: "0.3rem",
|
||||
padding: ".5rem 1rem",
|
||||
textDecoration: "none",
|
||||
transition: `color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out`,
|
||||
"&.Mui-selected": {
|
||||
backgroundColor: "#eaecf1",
|
||||
color: colors.black,
|
||||
borderColor: theme.palette.text.disabled,
|
||||
marginTop: "1px",
|
||||
bottom: -1,
|
||||
...(props.sx ? props.sx["&.Mui-selected"] : {}),
|
||||
},
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const InnovenergyTabBorder = (props: any) => {
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<Tab
|
||||
{...props}
|
||||
disableRipple
|
||||
sx={{
|
||||
textTransform: "none",
|
||||
...props.sx,
|
||||
bottom: 0,
|
||||
fontWeight: theme.typography.fontWeightRegular,
|
||||
fontSize: theme.typography.pxToRem(14),
|
||||
marginRight: theme.spacing(1),
|
||||
background: "0 0",
|
||||
border: "2px solid",
|
||||
borderColor: "#c0c6d0",
|
||||
bgcolor: theme.palette.primary.light,
|
||||
borderTopLeftRadius: "0.3rem",
|
||||
borderTopRightRadius: "0.3rem",
|
||||
padding: ".5rem 1rem",
|
||||
textDecoration: "none",
|
||||
transition: `color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out`,
|
||||
"&.Mui-selected": {
|
||||
backgroundColor: theme.palette.primary.light,
|
||||
color: colors.black,
|
||||
borderColor: theme.palette.text.disabled,
|
||||
marginTop: "1px",
|
||||
|
|
|
@ -24,7 +24,7 @@ const InnovenergyTabs = (props: AntTabsProps) => {
|
|||
},
|
||||
"&.Mui-selected": {
|
||||
color: colors.black,
|
||||
backgroundColor: "#eaecf1",
|
||||
backgroundColor: theme.palette.primary.light,
|
||||
borderColor: `#90A7c5 #90A7c5 #fff`,
|
||||
},
|
||||
"& .MuiTabs-indicator": {
|
||||
|
|
|
@ -15,13 +15,13 @@ const LanguageSelect = (props: LanguageSelectProps) => {
|
|||
label="Age"
|
||||
onChange={(e) => props.setLanguage(e.target.value)}
|
||||
>
|
||||
<MenuItem id="english-menu-item" value="EN">
|
||||
<MenuItem id="english-menu-item" value="en">
|
||||
<FormattedMessage id="english" defaultMessage="English" />
|
||||
</MenuItem>
|
||||
<MenuItem id="german-menu-item" value="DE">
|
||||
<MenuItem id="german-menu-item" value="de">
|
||||
<FormattedMessage id="german" defaultMessage="German" />
|
||||
</MenuItem>
|
||||
<MenuItem id="german-menu-item" value="FR">
|
||||
<MenuItem id="german-menu-item" value="fr">
|
||||
<FormattedMessage id="french" defaultMessage="French" />
|
||||
</MenuItem>
|
||||
</Select>
|
||||
|
|
|
@ -29,7 +29,6 @@ const NavigationTabs = () => {
|
|||
sx={{
|
||||
bgcolor: theme.palette.primary.main,
|
||||
"&.Mui-selected": {
|
||||
backgroundColor: "#eaecf1",
|
||||
borderColor: theme.palette.text.disabled,
|
||||
borderBottom: 0,
|
||||
mb: -1 / 8,
|
||||
|
@ -50,7 +49,6 @@ const NavigationTabs = () => {
|
|||
sx={{
|
||||
bgcolor: theme.palette.primary.main,
|
||||
"&.Mui-selected": {
|
||||
backgroundColor: "#eaecf1",
|
||||
borderColor: theme.palette.text.disabled,
|
||||
borderBottom: 0,
|
||||
mb: -1 / 8,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import List from "@mui/material/List";
|
||||
import ListItemButton from "@mui/material/ListItemButton";
|
||||
import ListItemText from "@mui/material/ListItemText";
|
||||
import { Divider, useTheme } from "@mui/material";
|
||||
import { Alert, Divider, useTheme } from "@mui/material";
|
||||
import { Link } from "react-router-dom";
|
||||
import useRouteMatch from "../../hooks/useRouteMatch";
|
||||
import routes from "../../routes.json";
|
||||
|
@ -9,6 +9,7 @@ import { Fragment, useContext } from "react";
|
|||
import { UsersContext } from "../Context/UsersContextProvider";
|
||||
import { I_User } from "../../util/user.util";
|
||||
import { colors } from "../..";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
|
||||
const getPathWithoutId = (path?: string) => {
|
||||
if (path) {
|
||||
|
@ -38,7 +39,7 @@ const UserList = (props: UserListProps) => {
|
|||
const routeMatch = useRouteMatch([routes.users + routes.user + ":id"]);
|
||||
const theme = useTheme();
|
||||
|
||||
if (filteredData && filteredData.length) {
|
||||
if (filteredData && filteredData.length > 0) {
|
||||
return (
|
||||
<List
|
||||
sx={{
|
||||
|
@ -87,6 +88,12 @@ const UserList = (props: UserListProps) => {
|
|||
})}
|
||||
</List>
|
||||
);
|
||||
} else if (filteredData?.length === 0) {
|
||||
return (
|
||||
<Alert severity="info" sx={{ mt: 1 }}>
|
||||
<FormattedMessage id="noData" defaultMessage="No data found" />
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
|
|
@ -1,77 +1,78 @@
|
|||
import {sha1Hmac} from "./Sha1";
|
||||
import {Utf8} from "./Utf8";
|
||||
import {toBase64} from "./UInt8Utils";
|
||||
import { sha1Hmac } from "./Sha1";
|
||||
import { Utf8 } from "./Utf8";
|
||||
import { toBase64 } from "./UInt8Utils";
|
||||
|
||||
export class S3Access
|
||||
{
|
||||
constructor
|
||||
(
|
||||
readonly bucket: string,
|
||||
readonly region: string,
|
||||
readonly provider: string,
|
||||
readonly key: string,
|
||||
readonly secret: string
|
||||
)
|
||||
{}
|
||||
export class S3Access {
|
||||
constructor(
|
||||
readonly bucket: string,
|
||||
readonly region: string,
|
||||
readonly provider: string,
|
||||
readonly key: string,
|
||||
readonly secret: string
|
||||
) {}
|
||||
|
||||
get host() : string { return `${this.bucket}.${this.region}.${this.provider}` }
|
||||
get url() : string { return `https://${this.host}` }
|
||||
get host(): string {
|
||||
return `${this.region}.${this.provider}`;
|
||||
}
|
||||
|
||||
public get(s3Path : string): Promise<Response>
|
||||
{
|
||||
const method = "GET";
|
||||
const auth = this.createAuthorizationHeader(method, s3Path, "");
|
||||
const url = this.url + "/" + s3Path
|
||||
const headers = {"Host": this.host, "Authorization": auth};
|
||||
get url(): string {
|
||||
return `https://${this.host}`;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return fetch(url, {method: method, mode: "cors", headers: headers})
|
||||
}
|
||||
catch
|
||||
{
|
||||
return Promise.reject()
|
||||
}
|
||||
public get(s3Path: string): Promise<Response> {
|
||||
const method = "GET";
|
||||
const auth = this.createAuthorizationHeader(method, s3Path, "");
|
||||
const url = this.url + "/" + this.bucket + "/" + s3Path;
|
||||
const headers = { Host: this.host, Authorization: auth };
|
||||
|
||||
try {
|
||||
return fetch(url, { method: method, mode: "cors", headers: headers });
|
||||
} catch {
|
||||
return Promise.reject();
|
||||
}
|
||||
}
|
||||
|
||||
private createAuthorizationHeader(method: string,
|
||||
s3Path: string,
|
||||
date: string)
|
||||
{
|
||||
return createAuthorizationHeader
|
||||
(
|
||||
method,
|
||||
this.bucket,
|
||||
s3Path,
|
||||
date,
|
||||
this.key,
|
||||
this.secret
|
||||
);
|
||||
}
|
||||
private createAuthorizationHeader(
|
||||
method: string,
|
||||
s3Path: string,
|
||||
date: string
|
||||
) {
|
||||
return createAuthorizationHeader(
|
||||
method,
|
||||
this.bucket,
|
||||
s3Path,
|
||||
date,
|
||||
this.key,
|
||||
this.secret
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function createAuthorizationHeader(method: string,
|
||||
bucket: string,
|
||||
s3Path: string,
|
||||
date: string,
|
||||
s3Key: string,
|
||||
s3Secret: string,
|
||||
contentType: string = "",
|
||||
md5Hash: string = "")
|
||||
{
|
||||
// StringToSign = HTTP-Verb + "\n" +
|
||||
// Content-MD5 + "\n" +
|
||||
// Content-Type + "\n" +
|
||||
// Date + "\n" +
|
||||
// CanonicalizedAmzHeaders +
|
||||
// CanonicalizedResource;
|
||||
function createAuthorizationHeader(
|
||||
method: string,
|
||||
bucket: string,
|
||||
s3Path: string,
|
||||
date: string,
|
||||
s3Key: string,
|
||||
s3Secret: string,
|
||||
contentType: string = "",
|
||||
md5Hash: string = ""
|
||||
) {
|
||||
// StringToSign = HTTP-Verb + "\n" +
|
||||
// Content-MD5 + "\n" +
|
||||
// Content-Type + "\n" +
|
||||
// Date + "\n" +
|
||||
// CanonicalizedAmzHeaders +
|
||||
// CanonicalizedResource;
|
||||
|
||||
const payload = Utf8.encode(`${method}\n${md5Hash}\n${contentType}\n${date}\n/${bucket}/${s3Path}`)
|
||||
const payload = Utf8.encode(
|
||||
`${method}\n${md5Hash}\n${contentType}\n${date}\n/${bucket}/${s3Path}`
|
||||
);
|
||||
|
||||
//console.log(`${method}\n${md5Hash}\n${contentType}\n${date}\n/${bucket}/${s3Path}`)
|
||||
//console.log(`${method}\n${md5Hash}\n${contentType}\n${date}\n/${bucket}/${s3Path}`)
|
||||
|
||||
const secret = Utf8.encode(s3Secret)
|
||||
const signature = toBase64(sha1Hmac(payload, secret));
|
||||
const secret = Utf8.encode(s3Secret);
|
||||
const signature = toBase64(sha1Hmac(payload, secret));
|
||||
|
||||
return `AWS ${s3Key}:${signature}`
|
||||
return `AWS ${s3Key}:${signature}`;
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ export const colors = {
|
|||
//change this color in index.css too
|
||||
|
||||
greyDark: "#d1d7de",
|
||||
greyLight: "#e1e4e7",
|
||||
greyLight: "#eaecf1",
|
||||
|
||||
orangeSelected: "#f7b34d",
|
||||
orangeHover: "#fad399",
|
||||
|
|
|
@ -197,12 +197,6 @@ export const getAmount = (
|
|||
highestConnectionValue: number,
|
||||
values: BoxDataValue[]
|
||||
) => {
|
||||
console.log(
|
||||
"getamount",
|
||||
Math.abs(values[0].value as number) / highestConnectionValue,
|
||||
Math.abs(values[0].value as number),
|
||||
highestConnectionValue
|
||||
);
|
||||
return Math.abs(values[0].value as number) / highestConnectionValue;
|
||||
};
|
||||
|
||||
|
|
|
@ -1,142 +0,0 @@
|
|||
import { styled, Tab, Tabs } from "@mui/material";
|
||||
import { colors } from "index";
|
||||
|
||||
|
||||
export const StyledTab = styled((props: any) => (
|
||||
<Tab disableRipple {...props} />
|
||||
))(({ theme }) => ({
|
||||
bottom: -1,
|
||||
fontWeight: theme.typography.fontWeightRegular,
|
||||
fontSize: theme.typography.pxToRem(14),
|
||||
marginRight: theme.spacing(1),
|
||||
background: "0 0",
|
||||
border: "1px solid transparent",
|
||||
borderTopLeftRadius: "0.3rem",
|
||||
borderTopRightRadius: "0.3rem",
|
||||
padding: ".5rem 1rem",
|
||||
textDecoration: "none",
|
||||
transition: `color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out`,
|
||||
"&.Mui-selected": {
|
||||
color: colors.black,
|
||||
backgroundColor: "#F2F4F8",
|
||||
borderColor: "#90A7c5 #90A7c5 #F2F4F8",
|
||||
marginTop: "1px",
|
||||
bottom: -1,
|
||||
},
|
||||
}));
|
||||
|
||||
export const StyledTabBlue = styled((props: any) => (
|
||||
<Tab disableRipple {...props} />
|
||||
))(({ theme }) => ({
|
||||
bottom: -1,
|
||||
fontWeight: theme.typography.fontWeightRegular,
|
||||
fontSize: theme.typography.pxToRem(14),
|
||||
marginRight: theme.spacing(1),
|
||||
background: "0 0",
|
||||
border: "1px solid transparent",
|
||||
borderBottom: "0px",
|
||||
borderTopLeftRadius: "0.3rem",
|
||||
borderTopRightRadius: "0.3rem",
|
||||
padding: ".5rem 1rem",
|
||||
textDecoration: "none",
|
||||
transition: `color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out`,
|
||||
"&.Mui-selected": {
|
||||
bottom: -1,
|
||||
color: colors.black,
|
||||
backgroundColor: "#CCD6E4",
|
||||
borderColor: "#90A7c5 #90A7c5 #CCD6E4",
|
||||
marginTop: "1px",
|
||||
},
|
||||
|
||||
}));
|
||||
|
||||
export const StyledTabBig = styled((props: any) => (
|
||||
<Tab disableRipple {...props} />
|
||||
))(({ theme }) => ({
|
||||
bottom: -2,
|
||||
|
||||
fontWeight: theme.typography.fontWeightRegular,
|
||||
fontSize: theme.typography.pxToRem(14),
|
||||
marginRight: theme.spacing(1),
|
||||
background: "0 0",
|
||||
border: "2px solid transparent",
|
||||
borderTopLeftRadius: "0.3rem",
|
||||
borderTopRightRadius: "0.3rem",
|
||||
padding: ".5rem 1rem",
|
||||
textDecoration: "none",
|
||||
transition: `color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out`,
|
||||
"&.Mui-selected": {
|
||||
color: colors.black,
|
||||
backgroundColor: "#F2F4F8",
|
||||
borderColor: "#90A7c5 #90A7c5 #F2F4F8",
|
||||
marginTop: "1px",
|
||||
bottom: -2,
|
||||
},
|
||||
|
||||
}));
|
||||
|
||||
export const StyledTabWhite = styled((props: any) => (
|
||||
<Tab disableRipple {...props} />
|
||||
))(({ theme }) => ({
|
||||
bottom: -1,
|
||||
fontWeight: theme.typography.fontWeightRegular,
|
||||
fontSize: theme.typography.pxToRem(14),
|
||||
marginRight: theme.spacing(1),
|
||||
background: "0 0",
|
||||
border: "1px solid transparent",
|
||||
borderTopLeftRadius: "0.3rem",
|
||||
borderTopRightRadius: "0.3rem",
|
||||
padding: ".5rem 1rem",
|
||||
textDecoration: "none",
|
||||
transition: `color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out`,
|
||||
"&.Mui-selected": {
|
||||
color: colors.black,
|
||||
backgroundColor: "White",
|
||||
borderColor: "#90A7c5 #90A7c5 White",
|
||||
marginTop: "1px",
|
||||
bottom: -1,
|
||||
},
|
||||
|
||||
}));
|
||||
|
||||
export const AntTabsBig = styled(Tabs)({
|
||||
borderBottom: "2px solid #90A7c5",
|
||||
overflow: "visible!important",
|
||||
"& div.MuiTabs-scroller": {
|
||||
overflow: "visible!important",
|
||||
},
|
||||
"&.Mui-selected": {
|
||||
color: colors.black,
|
||||
backgroundColor: "red",
|
||||
borderColor: `#90A7c5 #90A7c5 #fff`,
|
||||
},
|
||||
"& .MuiTabs-indicator": {
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
backgroundColor: "transparent",
|
||||
},
|
||||
"&.MuiTabs-root": {
|
||||
width: "100%",
|
||||
},
|
||||
});
|
||||
|
||||
export const AntTabs = styled(Tabs)({
|
||||
borderBottom: "1px solid #90A7c5",
|
||||
overflow: "visible!important",
|
||||
"& div.MuiTabs-scroller": {
|
||||
overflow: "visible!important",
|
||||
},
|
||||
"&.Mui-selected": {
|
||||
color: colors.black,
|
||||
backgroundColor: "red",
|
||||
borderColor: `#90A7c5 #90A7c5 #fff`,
|
||||
},
|
||||
"& .MuiTabs-indicator": {
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
backgroundColor: "transparent",
|
||||
},
|
||||
"&.MuiTabs-root": {
|
||||
width: "100%",
|
||||
},
|
||||
});
|
|
@ -23,6 +23,8 @@ export interface I_Installation extends S3Credentials {
|
|||
name: string;
|
||||
information: string;
|
||||
parentId: number;
|
||||
s3WriteKey: string;
|
||||
s3WriteSecret: string;
|
||||
}
|
||||
|
||||
export interface I_Folder {
|
||||
|
|
Loading…
Reference in New Issue