fix some bugs

This commit is contained in:
Sina Blattmann 2023-06-06 10:06:22 +02:00
parent ca05fd5b3e
commit ee40a4d544
17 changed files with 326 additions and 162 deletions

View File

@ -0,0 +1,136 @@
{
"Information": {
"defaultMessage": "Information"
},
"addNewChild": {
"defaultMessage": "Add new child"
},
"addNewDialogButton": {
"defaultMessage": "Add new dialog button"
},
"addUser": {
"defaultMessage": "Create user"
},
"alarms": {
"defaultMessage": "Alarms"
},
"applyChanges": {
"defaultMessage": "Apply changes"
},
"country": {
"defaultMessage": "Country"
},
"createNewFolder": {
"defaultMessage": "Create new folder"
},
"createNewUser": {
"defaultMessage": "Create new user"
},
"customerName": {
"defaultMessage": "Customer name"
},
"email": {
"defaultMessage": "Email"
},
"english": {
"defaultMessage": "English"
},
"error": {
},
"folder": {
"defaultMessage": "Folder"
},
"german": {
"defaultMessage": "German"
},
"groupTabs": {
"defaultMessage": "Group tabs"
},
"groupTree": {
"defaultMessage": "Group tree"
},
"information": {
"defaultMessage": "Information"
},
"inheritedAccess": {
"defaultMessage": "Inherited access from"
},
"installation": {
"defaultMessage": "Installation"
},
"installationTabs": {
"defaultMessage": "Installation tabs"
},
"installations": {
"defaultMessage": "Installations"
},
"lastWeek": {
"defaultMessage": "Last week"
},
"location": {
"defaultMessage": "Location"
},
"log": {
"defaultMessage": "Log"
},
"logout": {
"defaultMessage": "Logout"
},
"makeASelection": {
"defaultMessage": "Please make a selection on the left"
},
"manageAccess": {
"defaultMessage": "Manage access"
},
"move": {
"defaultMessage": "Move"
},
"moveTo": {
"defaultMessage": "Move to"
},
"moveTree": {
"defaultMessage": "Move tree"
},
"name": {
"defaultMessage": "Name"
},
"navigationTabs": {
"defaultMessage": "Navigation tabs"
},
"orderNumbers": {
"defaultMessage": "Order number"
},
"region": {
"defaultMessage": "Region"
},
"requiredLocation": {
"defaultMessage": "Location is required"
},
"requiredName": {
"defaultMessage": "Name is required"
},
"requiredRegion": {
"defaultMessage": "Region is required"
},
"search": {
"defaultMessage": "Search"
},
"submit": {
"defaultMessage": "Submit"
},
"updateFolderErrorMessage": {
"defaultMessage": "Couldn't update folder, an error occured"
},
"updatedSuccessfully": {
"defaultMessage": "Updated successfully"
},
"user": {
"defaultMessage": "User"
},
"userTabs": {
"defaultMessage": "user tabs"
},
"users": {
"defaultMessage": "Users"
}
}

View File

@ -5,8 +5,9 @@ import {
IconButton,
Avatar,
ListItemAvatar,
Alert,
} from "@mui/material";
import { Fragment, useContext, useEffect } from "react";
import { Fragment, useContext, useEffect, useState } from "react";
import axiosConfig from "../../../config/axiosConfig";
import PersonRemoveIcon from "@mui/icons-material/PersonRemove";
import PersonIcon from "@mui/icons-material/Person";
@ -14,10 +15,12 @@ import { UsersContext } from "../../Context/UsersContextProvider";
import { useParams } from "react-router-dom";
import { GroupContext } from "../../Context/GroupContextProvider";
import { UserContext } from "../../Context/UserContextProvider";
import { FormattedMessage } from "react-intl";
const UsersWithDirectAccess = () => {
const { fetchUsersWithDirectAccessForResource, directAccessUsers } =
useContext(UsersContext);
const [error, setError] = useState<any>();
const { currentType } = useContext(GroupContext);
const { id } = useParams();
const { getCurrentUser } = useContext(UserContext);
@ -35,12 +38,20 @@ const UsersWithDirectAccess = () => {
})
.then((res) => {
fetchUsersWithDirectAccessForResource();
})
.catch((err) => {
setError(err);
});
};
if (directAccessUsers && directAccessUsers.length) {
return (
<>
{error && (
<Alert severity="error">
<FormattedMessage id="error" defaultMessage={error.title} />
</Alert>
)}
{directAccessUsers.map((user) => {
return (
<Fragment key={user.id}>

View File

@ -14,6 +14,7 @@ import {
import routes from "../../../routes.json";
import PersonIcon from "@mui/icons-material/Person";
import { UsersContext } from "../../Context/UsersContextProvider";
import { FormattedMessage } from "react-intl";
const UsersWithInheritedAccess = () => {
const { fetchUsersWithInheritedAccessForResource, inheritedAccessUsers } =
@ -45,7 +46,10 @@ const UsersWithInheritedAccess = () => {
primary={user.name}
secondary={
<>
Inherited access from{" "}
<FormattedMessage
id="inheritedAccess"
defaultMessage="Inherited access from"
/>
<Link
id={"inherited-access-user-link-" + user.id}
to={

View File

@ -1,6 +1,6 @@
import { Dialog, DialogContent, DialogTitle, IconButton } from "@mui/material";
import { useState } from "react";
import { FormattedMessage } from "react-intl";
import { FormattedMessage, useIntl } from "react-intl";
import axiosConfig from "../../config/axiosConfig";
import { I_Folder } from "../../util/types";
import FolderForm from "./FolderForm";
@ -13,6 +13,7 @@ interface AddNewDialogProps {
const AddNewDialog = (props: AddNewDialogProps) => {
const [open, setOpen] = useState(false);
const intl = useIntl();
const handleSubmit = (data: I_Folder, childData: Partial<I_Folder>) => {
return axiosConfig
@ -49,13 +50,19 @@ const AddNewDialog = (props: AddNewDialogProps) => {
maxWidth="sm"
>
<DialogTitle>
Create new folder
<FormattedMessage
id="createNewFolder"
defaultMessage="Create new folder"
/>
<IconButton
id={"add-new-child-dialog-icon-button-" + props.values.id}
edge="start"
color="inherit"
onClick={() => setOpen(false)}
aria-label="close"
aria-label={intl.formatMessage({
id: "addNewDialogButton",
defaultMessage: "Add new dialog button",
})}
sx={{
position: "absolute",
right: 8,

View File

@ -24,7 +24,10 @@ const GroupTabs = () => {
<Box sx={{ borderBottom: 1, borderColor: "divider" }}>
<AntTabs
value={routeMatch?.pattern?.path}
aria-label="basic tabs example"
aria-label={intl.formatMessage({
id: "groupTabs",
defaultMessage: "Group tabs",
})}
>
{currentType === "Folder" ? (
<StyledTab

View File

@ -9,11 +9,13 @@ import routes from "../../../routes.json";
import { GroupContext } from "../../Context/GroupContextProvider";
import { instanceOfFolder } from "../../../util/group.util";
import { Grid, CircularProgress } from "@mui/material";
import { useIntl } from "react-intl";
const GroupTree = () => {
const { setCurrentType, fetchData, data, loading } = useContext(GroupContext);
const [openNodes, setOpenNodes] = useState<string[]>([]);
const [selected, setSelected] = useState<string>("");
const intl = useIntl();
useEffect(() => {
fetchData();
@ -69,7 +71,10 @@ const GroupTree = () => {
if (data) {
return (
<TreeView
aria-label="rich object"
aria-label={intl.formatMessage({
id: "groupTree",
defaultMessage: "Group tree",
})}
defaultCollapseIcon={<ExpandMoreIcon />}
defaultExpandIcon={<ChevronRightIcon />}
sx={{

View File

@ -56,7 +56,9 @@ const MoveDialog = (props: MoveDialogProps) => {
}}
scroll="paper"
>
<DialogTitle>Move to</DialogTitle>
<DialogTitle>
<FormattedMessage id="moveTo" defaultMessage="Move to" />
</DialogTitle>
<DialogContent>
<MoveTree
parentId={selectedParentId}
@ -68,7 +70,7 @@ const MoveDialog = (props: MoveDialogProps) => {
id={"move-dialog-move-button-" + id}
onClick={handleMove}
>
Move
<FormattedMessage id="move" defaultMessage="Move" />
</InnovenergyButton>
</DialogActions>
</Dialog>

View File

@ -6,6 +6,7 @@ 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";
interface MoveTreeProps {
setSelectedParentId: (value: number) => void;
@ -14,6 +15,7 @@ interface MoveTreeProps {
const MoveTree = (props: MoveTreeProps) => {
const { data } = useContext(GroupContext);
const intl = useIntl();
const getNodes = (element: I_Folder | I_Installation): null | ReactNode => {
if (instanceOfFolder(element)) {
@ -42,7 +44,10 @@ const MoveTree = (props: MoveTreeProps) => {
if (data) {
return (
<TreeView
aria-label="rich object"
aria-label={intl.formatMessage({
id: "moveTree",
defaultMessage: "Move tree",
})}
defaultCollapseIcon={<ExpandMoreIcon />}
defaultExpandIcon={<ChevronRightIcon />}
sx={{

View File

@ -12,6 +12,7 @@ const InnovenergySnackbar = (props: InnovenergySnackbarProps) => {
const handleClose = () => {
setOpen(false);
};
return (
<Snackbar
open={open}

View File

@ -1,5 +1,11 @@
import { FormattedMessage } from "react-intl";
const Alarms = () => {
return <div>alarms</div>;
return (
<div>
<FormattedMessage id="alarms" defaultMessage="Alarms" />
</div>
);
};
export default Alarms;

View File

@ -22,7 +22,10 @@ const InstallationTabs = () => {
<Box sx={{ borderBottom: 1, borderColor: "divider" }}>
<AntTabs
value={routeMatch?.pattern?.path ?? routes.installation + ":id"}
aria-label="installation tabs"
aria-label={intl.formatMessage({
id: "installationTabs",
defaultMessage: "Installation tabs",
})}
>
<StyledTab
id={"installation-tab-installation"}

View File

@ -1,12 +1,16 @@
import * as React from "react";
import { Button, Dialog, DialogContent, DialogTitle } from "@mui/material";
import { useState } from "react";
import { Button } from "@mui/material";
import { UnixTime, TimeSpan } from "../../../dataCache/time";
import { createTimes } from "../../../util/graph.util";
import { DatePicker, LocalizationProvider } from "@mui/x-date-pickers";
import {
DateTimePicker,
DateTimeValidationError,
LocalizationProvider,
} from "@mui/x-date-pickers";
import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
import dayjs from "dayjs";
import InnovenergyButton from "../../Layout/InnovenergyButton";
import { FormattedMessage } from "react-intl";
import { useState } from "react";
interface DateRangePickerProps {
setRange: (value: Date[]) => void;
@ -15,16 +19,18 @@ interface DateRangePickerProps {
}
const DateRangePicker = (props: DateRangePickerProps) => {
const { setRange, range, getCacheSeries } = props;
const [open, setOpen] = useState(false);
const [fromDateError, setFromDateError] =
useState<DateTimeValidationError | null>(null);
const [toDateError, setToDateError] =
useState<DateTimeValidationError | null>(null);
const handleChange = (fromDate: Date, toDate: Date) => {
setRange([fromDate, toDate]);
getCacheSeries(fromDate.getMilliseconds(), toDate.getMilliseconds());
};
return (
<>
<InnovenergyButton onClick={() => setOpen(true)}>
Set range
</InnovenergyButton>
<Dialog open={open} maxWidth="lg" onClose={() => setOpen(false)}>
<DialogTitle id="dialog-title">Set range</DialogTitle>
<DialogContent>
<div>
<Button
onClick={() => {
@ -42,37 +48,58 @@ const DateRangePicker = (props: DateRangePickerProps) => {
);
}}
>
Last week
<FormattedMessage id="lastWeek" defaultMessage="Last week" />
</Button>
</div>
<LocalizationProvider dateAdapter={AdapterDayjs}>
<DatePicker
<DateTimePicker
disableFuture
maxDateTime={dayjs(range[1])}
label="From date"
value={dayjs(range[0])}
onChange={(newValue) => {
if (newValue) {
const fromDate = newValue.toDate();
const toDate = range[1];
setRange([fromDate, toDate]);
getCacheSeries(+fromDate, +toDate);
handleChange(newValue.toDate(), range[1]);
}
}}
onError={(err) => setFromDateError(err)}
slotProps={{
textField: {
variant: "outlined",
error: !!fromDateError,
helperText: fromDateError
? "From date needs to be before to date"
: "",
},
}}
/>
<DatePicker
<DateTimePicker
disableFuture
minDateTime={dayjs(range[0])}
label="To date"
value={dayjs(range[1])}
sx={{
".Mui-disabled": {
color: "red",
},
}}
onError={(err) => setToDateError(err)}
onChange={(newValue) => {
if (newValue) {
const fromDate = newValue.toDate();
const toDate = range[1];
setRange([fromDate, toDate]);
getCacheSeries(+fromDate, +toDate);
handleChange(range[0], newValue.toDate());
}
}}
slotProps={{
textField: {
variant: "outlined",
error: !!toDateError,
helperText: toDateError
? "To date needs to be after from date"
: "",
},
}}
/>
</LocalizationProvider>
</DialogContent>
</Dialog>
</>
);
};

View File

@ -4,7 +4,7 @@ import {
GraphData,
createTimes,
flattenToggles,
insertTreeElements,
getTreeElements,
isNumeric,
parseCsv,
stringToColor,
@ -16,20 +16,20 @@ import { BehaviorSubject, startWith, throttleTime, withLatestFrom } from "rxjs";
import { S3Access } from "../../../dataCache/S3/S3Access";
import DataCache, { FetchResult } from "../../../dataCache/dataCache";
import { LogContext } from "../../Context/LogContextProvider";
import { TreeElement } from "./CheckboxTree";
import { isDefined } from "../../../dataCache/utils/maybe";
import { Data, Layout } from "plotly.js";
import { Alert, Button, ToggleButton, ToggleButtonGroup } from "@mui/material";
import { Alert } from "@mui/material";
import DateRangePicker from "./DateRangePicker";
import { LocalizationProvider } from "@mui/x-date-pickers";
import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
import { FormattedMessage } from "react-intl";
const NUMBER_OF_NODES = 100;
const ScalarGraph = () => {
const timeRange = createTimes(
UnixTime.now() /* .fromTicks(1682085650) */
.earlier(TimeSpan.fromDays(7))
.earlier(TimeSpan.fromDays(14))
.rangeBefore(TimeSpan.fromDays(4)),
NUMBER_OF_NODES
);
@ -38,6 +38,7 @@ const ScalarGraph = () => {
timeRange[0].toDate(),
timeRange[timeRange.length - 1].toDate(),
]);
const [uiRevision, setUiRevision] = useState(Math.random());
const [plotTitles, setPlotTitles] = useState<string[]>([]);
@ -67,22 +68,11 @@ const ScalarGraph = () => {
setTimeSeries(timeSeries);
const toggleValues = timeSeries.find((timeStamp) => timeStamp.value);
if (toggles === null && toggleValues && toggleValues.value) {
const treeElements = Object.keys(toggleValues.value)
.map((path) =>
path
.split("/")
.map((_, i, arr) => `/${arr.slice(1, i + 1).join("/")}`)
.slice(1)
)
.reduce(
(children, path) => insertTreeElements(children, path),
[] as TreeElement[]
);
const treeElements = getTreeElements(toggleValues.value);
setToggles(treeElements);
setCheckedToggles(flattenToggles(treeElements));
}
});
return () => subscription.unsubscribe();
}, [toggles]);
@ -199,7 +189,6 @@ const ScalarGraph = () => {
barnorm: "percent",
}
: {};
return (
<Plot
key={path}
@ -228,19 +217,15 @@ const ScalarGraph = () => {
],
}}
onRelayout={(params) => {
console.log("relayout");
const xaxisRange0 = params["xaxis.range[0]"];
const xaxisRange1 = params["xaxis.range[1]"];
if (xaxisRange0 && xaxisRange1) {
setRange([new Date(xaxisRange0), new Date(xaxisRange1)]);
setUiRevision(Math.random());
console.log("ranges", xaxisRange0);
getCacheSeries(xaxisRange0, xaxisRange1);
}
}}
onRestyle={() => "event restyle"}
onRedraw={() => "event restyle"}
/>
);
})}
@ -249,7 +234,10 @@ const ScalarGraph = () => {
}
return (
<Alert sx={{ mt: 2 }} severity="info">
Please make a selection on the left
<FormattedMessage
id="makeASelection"
defaultMessage="Please make a selection on the left"
/>
</Alert>
);
}

View File

@ -1,4 +1,4 @@
import { FormattedMessage } from "react-intl";
import { FormattedMessage, useIntl } from "react-intl";
import { Link } from "react-router-dom";
import useRouteMatch from "../../hooks/useRouteMatch";
import routes from "../../routes.json";
@ -9,6 +9,7 @@ const NavigationButtons = () => {
routes.installations + "*",
routes.users + "*",
]);
const intl = useIntl();
return (
<>
@ -37,7 +38,10 @@ const NavigationButtons = () => {
<AntTabs
id="navigation-buttons-group"
value={routeMatch?.pattern?.path}
aria-label="basic tabs example"
aria-label={intl.formatMessage({
id: "navigationTabs",
defaultMessage: "Navigation tabs",
})}
>
<StyledTab
id="navigation-tab-installations"

View File

@ -1,65 +0,0 @@
import * as React from "react";
import Table from "@mui/material/Table";
import TableBody from "@mui/material/TableBody";
import TableCell from "@mui/material/TableCell";
import TableContainer from "@mui/material/TableContainer";
import TableHead from "@mui/material/TableHead";
import TableRow from "@mui/material/TableRow";
import Paper from "@mui/material/Paper";
const rows = [
{
device: "48TL200",
code: "MSWE",
message: "Main switch error",
severity: "Alarm",
},
{
device: "48TL200",
code: "MSWE",
message: "Main switch error",
severity: "Alarm",
},
{
device: "48TL200",
code: "MSWE",
message: "Main switch error",
severity: "Alarm",
},
{
device: "48TL200",
code: "MSWE",
message: "Main switch error",
severity: "Alarm",
},
];
const BasicTable = () => {
return (
<TableContainer component={Paper}>
{/* <Table aria-label="simple table">
<TableHead>
<TableRow>
<TableCell>Device</TableCell>
<TableCell>Code</TableCell>
<TableCell>Message</TableCell>
<TableCell>Severity</TableCell>
</TableRow>
</TableHead>
<TableBody>
{rows.map((row, i) => (
<TableRow
key={i}
>
<TableCell>{row.device}</TableCell>
<TableCell>{row.code}</TableCell>
<TableCell>{row.message}</TableCell>
<TableCell>{row.severity}</TableCell>
</TableRow>
))}
</TableBody>
</Table> */}
</TableContainer>
);
};
export default BasicTable;

View File

@ -1,5 +1,4 @@
import * as React from "react";
import Box from "@mui/material/Box";
import { Link } from "react-router-dom";
import routes from "../../routes.json";
import useRouteMatch from "../../hooks/useRouteMatch";

View File

@ -18,5 +18,33 @@
"groups": "Groups",
"group": "Group",
"folder": "Folder",
"updateFolderErrorMessage": "Couldn't update folder, an error occured"
"updateFolderErrorMessage": "Couldn't update folder, an error occured",
"Information": "Information",
"addNewChild": "Add new child",
"addNewDialogButton": "Add new dialog button",
"addUser": "Create user",
"createNewFolder": "Create new folder",
"createNewUser": "Create new user",
"email": "Email",
"error": "",
"groupTabs": "Group tabs",
"groupTree": "Group tree",
"information": "Information",
"inheritedAccess": "Inherited access from",
"installationTabs": "Installation tabs",
"installations": "Installations",
"lastWeek": "Last week",
"makeASelection": "Please make a selection on the left",
"manageAccess": "Manage access",
"move": "Move",
"moveTo": "Move to",
"moveTree": "Move tree",
"name": "Name",
"navigationTabs": "Navigation tabs",
"requiredLocation": "Location is required",
"requiredName": "Name is required",
"requiredRegion": "Region is required",
"submit": "Submit",
"user": "User",
"userTabs": "user tabs"
}