Merge remote-tracking branch 'origin/marios'
This commit is contained in:
commit
ec1681311a
|
@ -1,21 +0,0 @@
|
||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2022 BloomUI
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
File diff suppressed because it is too large
Load Diff
|
@ -14,7 +14,9 @@
|
||||||
"@types/react-dom": "17.0.13",
|
"@types/react-dom": "17.0.13",
|
||||||
"apexcharts": "3.35.3",
|
"apexcharts": "3.35.3",
|
||||||
"axios": "^1.5.0",
|
"axios": "^1.5.0",
|
||||||
|
"chart.js": "^4.4.0",
|
||||||
"clsx": "1.1.1",
|
"clsx": "1.1.1",
|
||||||
|
"cytoscape": "^3.26.0",
|
||||||
"date-fns": "2.28.0",
|
"date-fns": "2.28.0",
|
||||||
"history": "5.3.0",
|
"history": "5.3.0",
|
||||||
"linq-to-typescript": "^11.0.0",
|
"linq-to-typescript": "^11.0.0",
|
||||||
|
@ -23,9 +25,14 @@
|
||||||
"prop-types": "15.8.1",
|
"prop-types": "15.8.1",
|
||||||
"react": "17.0.2",
|
"react": "17.0.2",
|
||||||
"react-apexcharts": "1.4.0",
|
"react-apexcharts": "1.4.0",
|
||||||
|
"react-chartjs-2": "^5.2.0",
|
||||||
"react-custom-scrollbars-2": "4.4.0",
|
"react-custom-scrollbars-2": "4.4.0",
|
||||||
|
"react-cytoscapejs": "^2.0.0",
|
||||||
"react-dom": "17.0.2",
|
"react-dom": "17.0.2",
|
||||||
|
"react-flow-renderer": "^10.3.17",
|
||||||
"react-helmet-async": "1.3.0",
|
"react-helmet-async": "1.3.0",
|
||||||
|
"react-icons": "^4.11.0",
|
||||||
|
"react-icons-converter": "^1.1.4",
|
||||||
"react-intl": "^6.4.4",
|
"react-intl": "^6.4.4",
|
||||||
"react-router": "6.3.0",
|
"react-router": "6.3.0",
|
||||||
"react-router-dom": "6.3.0",
|
"react-router-dom": "6.3.0",
|
||||||
|
|
|
@ -15,13 +15,13 @@ import SidebarLayout from './layouts/SidebarLayout';
|
||||||
import { TokenContext } from './contexts/tokenContext';
|
import { TokenContext } from './contexts/tokenContext';
|
||||||
import ResetPassword from './components/ResetPassword';
|
import ResetPassword from './components/ResetPassword';
|
||||||
import ForgotPassword from './components/ForgotPassword';
|
import ForgotPassword from './components/ForgotPassword';
|
||||||
|
import InstallationTabs from './content/dashboards/Installations/index';
|
||||||
|
import routes from 'src/Resources/routes.json';
|
||||||
|
import './App.css';
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
//const content = useRoutes(router);
|
|
||||||
|
|
||||||
const context = useContext(UserContext);
|
const context = useContext(UserContext);
|
||||||
const { currentUser, setUser } = context;
|
const { currentUser, setUser } = context;
|
||||||
|
|
||||||
const tokencontext = useContext(TokenContext);
|
const tokencontext = useContext(TokenContext);
|
||||||
const { token, setNewToken, removeToken } = tokencontext;
|
const { token, setNewToken, removeToken } = tokencontext;
|
||||||
const [forgotPassword, setForgotPassword] = useState(false);
|
const [forgotPassword, setForgotPassword] = useState(false);
|
||||||
|
@ -79,14 +79,14 @@ function App() {
|
||||||
lazy(() => import('src/content/pages/Status/Maintenance'))
|
lazy(() => import('src/content/pages/Status/Maintenance'))
|
||||||
);
|
);
|
||||||
|
|
||||||
const routes: RouteObject[] = [
|
const routesArray: RouteObject[] = [
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
element: <BaseLayout />,
|
element: <BaseLayout />,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: '/',
|
path: '/',
|
||||||
element: <Navigate to="installations" replace />
|
element: <Navigate to="installations/" replace />
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'status',
|
path: 'status',
|
||||||
|
@ -118,39 +118,6 @@ function App() {
|
||||||
element: <Status404 />
|
element: <Status404 />
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'ResetPassword',
|
|
||||||
element: <ResetPassword></ResetPassword>
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'Login',
|
|
||||||
element: <Login></Login>
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'installations',
|
|
||||||
element: (
|
|
||||||
<SidebarLayout language={language} onSelectLanguage={setLanguage} />
|
|
||||||
),
|
|
||||||
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
path: '',
|
|
||||||
element: <Installations />
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'users',
|
|
||||||
element: (
|
|
||||||
<SidebarLayout language={language} onSelectLanguage={setLanguage} />
|
|
||||||
),
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
path: '',
|
|
||||||
element: <Users />
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
if (forgotPassword) {
|
if (forgotPassword) {
|
||||||
|
@ -189,7 +156,7 @@ function App() {
|
||||||
>
|
>
|
||||||
<CssBaseline />
|
<CssBaseline />
|
||||||
<Routes>
|
<Routes>
|
||||||
{routes.map((route, index) => (
|
{routesArray.map((route, index) => (
|
||||||
<Route key={index} path={route.path} element={route.element}>
|
<Route key={index} path={route.path} element={route.element}>
|
||||||
{route.children &&
|
{route.children &&
|
||||||
route.children.map((childRoute, childIndex) => (
|
route.children.map((childRoute, childIndex) => (
|
||||||
|
@ -201,6 +168,23 @@ function App() {
|
||||||
))}
|
))}
|
||||||
</Route>
|
</Route>
|
||||||
))}
|
))}
|
||||||
|
<Route
|
||||||
|
path="/"
|
||||||
|
element={
|
||||||
|
<SidebarLayout
|
||||||
|
language={language}
|
||||||
|
onSelectLanguage={setLanguage}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Route
|
||||||
|
path={routes.installations + '*'}
|
||||||
|
element={<InstallationTabs />}
|
||||||
|
/>
|
||||||
|
<Route path={routes.users + '*'} element={<Users />} />
|
||||||
|
<Route path="ResetPassword" element={<ResetPassword />}></Route>
|
||||||
|
<Route path="Login" element={<Login />}></Route>
|
||||||
|
</Route>
|
||||||
</Routes>
|
</Routes>
|
||||||
</IntlProvider>
|
</IntlProvider>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
import React, { useContext, useState } from 'react';
|
import React, { useContext, useEffect, useState } from 'react';
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
CircularProgress,
|
CircularProgress,
|
||||||
Divider,
|
|
||||||
Grid,
|
Grid,
|
||||||
Table,
|
Table,
|
||||||
TableBody,
|
TableBody,
|
||||||
|
@ -18,26 +17,44 @@ import Installation from './Installation';
|
||||||
import CancelIcon from '@mui/icons-material/Cancel';
|
import CancelIcon from '@mui/icons-material/Cancel';
|
||||||
import { LogContext } from 'src/contexts/LogContextProvider';
|
import { LogContext } from 'src/contexts/LogContextProvider';
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import routes from 'src/Resources/routes.json';
|
||||||
|
|
||||||
interface FlatInstallationViewProps {
|
interface FlatInstallationViewProps {
|
||||||
installations: I_Installation[];
|
installations: I_Installation[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const FlatInstallationView = (props: FlatInstallationViewProps) => {
|
const FlatInstallationView = (props: FlatInstallationViewProps) => {
|
||||||
const [selectedInstallation, setSelectedInstallation] = useState<number>(-1);
|
|
||||||
const selectedBulkActions = selectedInstallation === -1 ? false : true;
|
|
||||||
const [isRowHovered, setHoveredRow] = useState(-1);
|
const [isRowHovered, setHoveredRow] = useState(-1);
|
||||||
const logContext = useContext(LogContext);
|
const logContext = useContext(LogContext);
|
||||||
const { getStatus } = logContext;
|
const { getStatus } = logContext;
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const searchParams = new URLSearchParams(location.search);
|
||||||
|
const installationId = parseInt(searchParams.get('installation'));
|
||||||
|
const [selectedInstallation, setSelectedInstallation] = useState<number>(-1);
|
||||||
|
|
||||||
const handleSelectOneInstallation = (installationID: number): void => {
|
const handleSelectOneInstallation = (installationID: number): void => {
|
||||||
if (selectedInstallation != installationID) {
|
if (selectedInstallation != installationID) {
|
||||||
setSelectedInstallation(installationID);
|
setSelectedInstallation(installationID);
|
||||||
|
navigate(
|
||||||
|
routes.installations +
|
||||||
|
routes.list +
|
||||||
|
'?installation=' +
|
||||||
|
installationID.toString(),
|
||||||
|
{
|
||||||
|
replace: true
|
||||||
|
}
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
setSelectedInstallation(-1);
|
setSelectedInstallation(-1);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setSelectedInstallation(installationId);
|
||||||
|
}, [installationId]);
|
||||||
|
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
const findInstallation = (id: number) => {
|
const findInstallation = (id: number) => {
|
||||||
|
@ -54,20 +71,27 @@ const FlatInstallationView = (props: FlatInstallationViewProps) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Grid container spacing={1} sx={{ marginTop: 0.1 }}>
|
<Grid container spacing={1} sx={{ marginTop: 0.1 }}>
|
||||||
<Grid item xs={12} md={3}>
|
<Grid item sx={{ display: !installationId ? 'block' : 'none' }}>
|
||||||
<Card>
|
<Card>
|
||||||
<Divider />
|
|
||||||
<TableContainer>
|
<TableContainer>
|
||||||
<Table>
|
<Table>
|
||||||
<TableHead>
|
<TableHead>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell padding="checkbox"></TableCell>
|
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<FormattedMessage id="name" defaultMessage="Name" />
|
<FormattedMessage id="name" defaultMessage="Name" />
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<FormattedMessage id="location" defaultMessage="Location" />
|
<FormattedMessage id="location" defaultMessage="Location" />
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<FormattedMessage id="country" defaultMessage="Country" />
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<FormattedMessage
|
||||||
|
id="order"
|
||||||
|
defaultMessage="Order Values"
|
||||||
|
/>
|
||||||
|
</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<FormattedMessage id="status" defaultMessage="Status" />
|
<FormattedMessage id="status" defaultMessage="Status" />
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
@ -76,7 +100,7 @@ const FlatInstallationView = (props: FlatInstallationViewProps) => {
|
||||||
<TableBody>
|
<TableBody>
|
||||||
{props.installations.map((installation) => {
|
{props.installations.map((installation) => {
|
||||||
const isInstallationSelected =
|
const isInstallationSelected =
|
||||||
installation.id === selectedInstallation ? true : false;
|
installation.id === selectedInstallation;
|
||||||
|
|
||||||
const status = getStatus(installation.id);
|
const status = getStatus(installation.id);
|
||||||
|
|
||||||
|
@ -100,31 +124,58 @@ const FlatInstallationView = (props: FlatInstallationViewProps) => {
|
||||||
onMouseEnter={() => handleRowMouseEnter(installation.id)}
|
onMouseEnter={() => handleRowMouseEnter(installation.id)}
|
||||||
onMouseLeave={() => handleRowMouseLeave()}
|
onMouseLeave={() => handleRowMouseLeave()}
|
||||||
>
|
>
|
||||||
<TableCell padding="checkbox"></TableCell>
|
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<Typography
|
<Typography
|
||||||
variant="body1"
|
variant="body2"
|
||||||
fontWeight="bold"
|
fontWeight="bold"
|
||||||
color="text.primary"
|
color="text.primary"
|
||||||
gutterBottom
|
gutterBottom
|
||||||
noWrap
|
noWrap
|
||||||
sx={{ marginTop: '10px' }}
|
sx={{ marginTop: '10px', fontSize: 'small' }}
|
||||||
>
|
>
|
||||||
{installation.name}
|
{installation.name}
|
||||||
</Typography>
|
</Typography>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<Typography
|
<Typography
|
||||||
variant="body1"
|
variant="body2"
|
||||||
fontWeight="bold"
|
fontWeight="bold"
|
||||||
color="text.primary"
|
color="text.primary"
|
||||||
gutterBottom
|
gutterBottom
|
||||||
noWrap
|
noWrap
|
||||||
sx={{ marginTop: '10px' }}
|
sx={{ marginTop: '10px', fontSize: 'small' }}
|
||||||
>
|
>
|
||||||
{installation.location}
|
{installation.location}
|
||||||
</Typography>
|
</Typography>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
|
||||||
|
<TableCell>
|
||||||
|
<Typography
|
||||||
|
variant="body2"
|
||||||
|
fontWeight="bold"
|
||||||
|
color="text.primary"
|
||||||
|
gutterBottom
|
||||||
|
noWrap
|
||||||
|
sx={{ marginTop: '10px', fontSize: 'small' }}
|
||||||
|
>
|
||||||
|
{installation.country}
|
||||||
|
</Typography>
|
||||||
|
</TableCell>
|
||||||
|
|
||||||
|
<TableCell>
|
||||||
|
<Typography
|
||||||
|
variant="body2"
|
||||||
|
fontWeight="bold"
|
||||||
|
color="text.primary"
|
||||||
|
gutterBottom
|
||||||
|
noWrap
|
||||||
|
sx={{ marginTop: '10px', fontSize: 'small' }}
|
||||||
|
>
|
||||||
|
{installation.orderNumbers}
|
||||||
|
</Typography>
|
||||||
|
</TableCell>
|
||||||
|
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
|
@ -161,6 +212,7 @@ const FlatInstallationView = (props: FlatInstallationViewProps) => {
|
||||||
style={{
|
style={{
|
||||||
width: '20px',
|
width: '20px',
|
||||||
height: '20px',
|
height: '20px',
|
||||||
|
marginLeft: '2px',
|
||||||
borderRadius: '50%',
|
borderRadius: '50%',
|
||||||
backgroundColor:
|
backgroundColor:
|
||||||
status === 2
|
status === 2
|
||||||
|
@ -182,15 +234,11 @@ const FlatInstallationView = (props: FlatInstallationViewProps) => {
|
||||||
</TableContainer>
|
</TableContainer>
|
||||||
</Card>
|
</Card>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
{props.installations.map((installation) => (
|
{props.installations.map((installation) => (
|
||||||
<Installation
|
<Installation
|
||||||
key={installation.id}
|
key={installation.id}
|
||||||
current_installation={findInstallation(installation.id)}
|
current_installation={findInstallation(installation.id)}
|
||||||
type="installation"
|
type="installation"
|
||||||
style={{
|
|
||||||
display: installation.id === selectedInstallation ? 'block' : 'none'
|
|
||||||
}}
|
|
||||||
></Installation>
|
></Installation>
|
||||||
))}
|
))}
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
|
@ -25,45 +25,56 @@ import Access from '../ManageAccess/Access';
|
||||||
import Log from 'src/content/dashboards/Log/Log';
|
import Log from 'src/content/dashboards/Log/Log';
|
||||||
import { TimeSpan, UnixTime } from 'src/dataCache/time';
|
import { TimeSpan, UnixTime } from 'src/dataCache/time';
|
||||||
import { FetchResult } from 'src/dataCache/dataCache';
|
import { FetchResult } from 'src/dataCache/dataCache';
|
||||||
import { DataRecord } from 'src/dataCache/data';
|
import {
|
||||||
import { S3Access } from 'src/dataCache/S3/S3Access';
|
extractValues,
|
||||||
import { parseCsv } from 'src/content/dashboards/Log/graph.util';
|
TopologyValues
|
||||||
import { I_S3Credentials, Notification } from 'src/interfaces/S3Types';
|
} from 'src/content/dashboards/Log/graph.util';
|
||||||
|
import { Notification } from 'src/interfaces/S3Types';
|
||||||
import { LogContext } from 'src/contexts/LogContextProvider';
|
import { LogContext } from 'src/contexts/LogContextProvider';
|
||||||
import LiveView from '../LiveView/LiveView';
|
import Topology from '../Topology/Topology';
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
import Overview from '../Overview/overview';
|
||||||
|
import Configuration from '../Configuration/Configuration';
|
||||||
|
import { fetchData } from 'src/content/dashboards/Installations/fetchData';
|
||||||
|
|
||||||
interface singleInstallationProps {
|
interface singleInstallationProps {
|
||||||
current_installation: I_Installation;
|
current_installation: I_Installation;
|
||||||
type: string;
|
type: string;
|
||||||
style?: React.CSSProperties;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function Installation(props: singleInstallationProps) {
|
function Installation(props: singleInstallationProps) {
|
||||||
const tabs = [
|
const tabs = [
|
||||||
{
|
{
|
||||||
value: 'installation',
|
value: 'live',
|
||||||
label: (
|
label: <FormattedMessage id="live" defaultMessage="Live" />
|
||||||
<FormattedMessage id="installation" defaultMessage="Installation" />
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
value: 'overview',
|
||||||
|
label: <FormattedMessage id="overview" defaultMessage="Overview" />
|
||||||
|
},
|
||||||
|
,
|
||||||
{
|
{
|
||||||
value: 'manage',
|
value: 'manage',
|
||||||
label: (
|
label: <FormattedMessage id="manage" defaultMessage="Access Management" />
|
||||||
<FormattedMessage id="manageAccess" defaultMessage="Manage Access" />
|
|
||||||
)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'live',
|
|
||||||
label: <FormattedMessage id="live" defaultMessage="Live View" />
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: 'log',
|
value: 'log',
|
||||||
label: <FormattedMessage id="log" defaultMessage="Log" />
|
label: <FormattedMessage id="log" defaultMessage="Log" />
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'information',
|
||||||
|
label: <FormattedMessage id="information" defaultMessage="Information" />
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
value: 'configuration',
|
||||||
|
label: (
|
||||||
|
<FormattedMessage id="configuration" defaultMessage="Configuration" />
|
||||||
|
)
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const [currentTab, setCurrentTab] = useState<string>(tabs[0].value);
|
const [currentTab, setCurrentTab] = useState<string>('live');
|
||||||
const [formValues, setFormValues] = useState(props.current_installation);
|
const [formValues, setFormValues] = useState(props.current_installation);
|
||||||
const requiredFields = ['name', 'region', 'location', 'country'];
|
const requiredFields = ['name', 'region', 'location', 'country'];
|
||||||
const context = useContext(UserContext);
|
const context = useContext(UserContext);
|
||||||
|
@ -87,37 +98,9 @@ function Installation(props: singleInstallationProps) {
|
||||||
const [errorLoadingS3Data, setErrorLoadingS3Data] = useState(false);
|
const [errorLoadingS3Data, setErrorLoadingS3Data] = useState(false);
|
||||||
const logContext = useContext(LogContext);
|
const logContext = useContext(LogContext);
|
||||||
const { installationStatus, handleLogWarningOrError, getStatus } = logContext;
|
const { installationStatus, handleLogWarningOrError, getStatus } = logContext;
|
||||||
|
const searchParams = new URLSearchParams(location.search);
|
||||||
const fetchData = (
|
const installationId = parseInt(searchParams.get('installation'));
|
||||||
timestamp: UnixTime,
|
const [values, setValues] = useState<TopologyValues | null>(null);
|
||||||
s3Credentials: I_S3Credentials
|
|
||||||
): Promise<FetchResult<DataRecord>> => {
|
|
||||||
const s3Path = `${timestamp.ticks}.csv`;
|
|
||||||
if (s3Credentials && s3Credentials.s3Bucket) {
|
|
||||||
const s3Access = new S3Access(
|
|
||||||
s3Credentials.s3Bucket,
|
|
||||||
s3Credentials.s3Region,
|
|
||||||
s3Credentials.s3Provider,
|
|
||||||
s3Credentials.s3Key,
|
|
||||||
s3Credentials.s3Secret
|
|
||||||
);
|
|
||||||
return s3Access
|
|
||||||
.get(s3Path)
|
|
||||||
.then(async (r) => {
|
|
||||||
if (r.status === 404) {
|
|
||||||
return Promise.resolve(FetchResult.notAvailable);
|
|
||||||
} else if (r.status === 200) {
|
|
||||||
const text = await r.text();
|
|
||||||
return parseCsv(text);
|
|
||||||
} else {
|
|
||||||
return Promise.resolve(FetchResult.notAvailable);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((e) => {
|
|
||||||
return Promise.resolve(FetchResult.tryLater);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (formValues == undefined) {
|
if (formValues == undefined) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -156,9 +139,6 @@ function Installation(props: singleInstallationProps) {
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setFormValues(props.current_installation);
|
|
||||||
|
|
||||||
const S3data = {
|
const S3data = {
|
||||||
s3Region: props.current_installation.s3Region,
|
s3Region: props.current_installation.s3Region,
|
||||||
s3Provider: props.current_installation.s3Provider,
|
s3Provider: props.current_installation.s3Provider,
|
||||||
|
@ -169,43 +149,89 @@ function Installation(props: singleInstallationProps) {
|
||||||
const s3Bucket =
|
const s3Bucket =
|
||||||
props.current_installation.id.toString() +
|
props.current_installation.id.toString() +
|
||||||
'-3e5b3069-214a-43ee-8d85-57d72000c19d';
|
'-3e5b3069-214a-43ee-8d85-57d72000c19d';
|
||||||
|
|
||||||
const s3Credentials = { s3Bucket, ...S3data };
|
const s3Credentials = { s3Bucket, ...S3data };
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let isMounted = true;
|
||||||
|
setFormValues(props.current_installation);
|
||||||
|
|
||||||
setErrorLoadingS3Data(false);
|
setErrorLoadingS3Data(false);
|
||||||
|
|
||||||
const interval = setInterval(() => {
|
const fetchDataPeriodically = async () => {
|
||||||
const now = UnixTime.now().earlier(TimeSpan.fromSeconds(20));
|
const now = UnixTime.now().earlier(TimeSpan.fromSeconds(20));
|
||||||
|
const date = now.toDate();
|
||||||
|
|
||||||
fetchData(now, s3Credentials)
|
try {
|
||||||
.then((res) => {
|
const res = await fetchData(now, s3Credentials);
|
||||||
|
|
||||||
|
if (installationId == 2) {
|
||||||
console.log('Fetched data from unix timestamp ' + now);
|
console.log('Fetched data from unix timestamp ' + now);
|
||||||
|
}
|
||||||
|
if (!isMounted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const newWarnings: Notification[] = [];
|
const newWarnings: Notification[] = [];
|
||||||
const newErrors: Notification[] = [];
|
const newErrors: Notification[] = [];
|
||||||
if (res === FetchResult.notAvailable || res == FetchResult.tryLater) {
|
|
||||||
|
if (res === FetchResult.notAvailable || res === FetchResult.tryLater) {
|
||||||
setErrorLoadingS3Data(true);
|
setErrorLoadingS3Data(true);
|
||||||
handleLogWarningOrError(props.current_installation.id, -1);
|
handleLogWarningOrError(props.current_installation.id, -1);
|
||||||
} else {
|
} else {
|
||||||
setErrorLoadingS3Data(false);
|
setErrorLoadingS3Data(false);
|
||||||
|
setValues(
|
||||||
|
extractValues({
|
||||||
|
time: now,
|
||||||
|
value: res
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
for (const key in res) {
|
for (const key in res) {
|
||||||
if (
|
if (
|
||||||
(res.hasOwnProperty(key) &&
|
(res.hasOwnProperty(key) &&
|
||||||
key.includes('/Alarms') &&
|
key.includes('/Alarms') &&
|
||||||
res[key].value != '') ||
|
res[key].value !== '') ||
|
||||||
(key.includes('/Warnings') && res[key].value != '')
|
(key.includes('/Warnings') && res[key].value !== '')
|
||||||
) {
|
) {
|
||||||
if (key.includes('/Warnings')) {
|
if (key.includes('/Warnings')) {
|
||||||
newWarnings.push({
|
newWarnings.push({
|
||||||
key,
|
device: key.substring(1, key.lastIndexOf('/')),
|
||||||
value: res[key].value.toString()
|
description: res[key].value.toString(),
|
||||||
|
date:
|
||||||
|
date.getFullYear() +
|
||||||
|
'-' +
|
||||||
|
date.getMonth() +
|
||||||
|
'-' +
|
||||||
|
date.getDay(),
|
||||||
|
time:
|
||||||
|
date.getHours() +
|
||||||
|
':' +
|
||||||
|
date.getMinutes() +
|
||||||
|
':' +
|
||||||
|
date.getSeconds()
|
||||||
});
|
});
|
||||||
} else if (key.includes('/Alarms')) {
|
} else if (key.includes('/Alarms')) {
|
||||||
newErrors.push({
|
newErrors.push({
|
||||||
key,
|
device: key.substring(1, key.lastIndexOf('/')),
|
||||||
value: res[key].value.toString()
|
description: res[key].value.toString(),
|
||||||
|
date:
|
||||||
|
date.getFullYear() +
|
||||||
|
'-' +
|
||||||
|
date.getMonth() +
|
||||||
|
'-' +
|
||||||
|
date.getDay(),
|
||||||
|
time:
|
||||||
|
date.getHours() +
|
||||||
|
':' +
|
||||||
|
date.getMinutes() +
|
||||||
|
':' +
|
||||||
|
date.getSeconds()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setWarnings(newWarnings);
|
setWarnings(newWarnings);
|
||||||
setErrors(newErrors);
|
setErrors(newErrors);
|
||||||
|
|
||||||
|
@ -217,16 +243,23 @@ function Installation(props: singleInstallationProps) {
|
||||||
handleLogWarningOrError(props.current_installation.id, 0);
|
handleLogWarningOrError(props.current_installation.id, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
} catch (err) {
|
||||||
.catch((err) => {
|
|
||||||
setErrorLoadingS3Data(true);
|
setErrorLoadingS3Data(true);
|
||||||
});
|
}
|
||||||
}, 2000);
|
};
|
||||||
|
|
||||||
|
const interval = setInterval(fetchDataPeriodically, 2000);
|
||||||
|
|
||||||
|
// Cleanup function to cancel interval and update isMounted when unmounted
|
||||||
|
return () => {
|
||||||
|
isMounted = false;
|
||||||
|
clearInterval(interval);
|
||||||
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
if (installationId == props.current_installation.id) {
|
||||||
return (
|
return (
|
||||||
<>
|
<Grid item xs={12} md={12}>
|
||||||
<Grid item xs={12} md={9} style={props.style}>
|
|
||||||
<TabsContainerWrapper>
|
<TabsContainerWrapper>
|
||||||
<Tabs
|
<Tabs
|
||||||
onChange={handleTabsChange}
|
onChange={handleTabsChange}
|
||||||
|
@ -249,7 +282,7 @@ function Installation(props: singleInstallationProps) {
|
||||||
alignItems="stretch"
|
alignItems="stretch"
|
||||||
spacing={0}
|
spacing={0}
|
||||||
>
|
>
|
||||||
{currentTab === 'installation' && (
|
{currentTab === 'information' && (
|
||||||
<Container maxWidth="xl">
|
<Container maxWidth="xl">
|
||||||
<Grid
|
<Grid
|
||||||
container
|
container
|
||||||
|
@ -350,6 +383,44 @@ function Installation(props: singleInstallationProps) {
|
||||||
fullWidth
|
fullWidth
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{currentUser.hasWriteAccess && (
|
||||||
|
<>
|
||||||
|
<div>
|
||||||
|
<TextField
|
||||||
|
label="S3 Write Key"
|
||||||
|
name="s3writekey"
|
||||||
|
value={formValues.s3WriteKey}
|
||||||
|
variant="outlined"
|
||||||
|
fullWidth
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<TextField
|
||||||
|
label="S3 Write Secret Key"
|
||||||
|
name="s3writesecretkey"
|
||||||
|
value={formValues.s3WriteSecret}
|
||||||
|
variant="outlined"
|
||||||
|
fullWidth
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<TextField
|
||||||
|
label="S3 Bucket Name"
|
||||||
|
name="s3writesecretkey"
|
||||||
|
value={
|
||||||
|
formValues.id +
|
||||||
|
'-3e5b3069-214a-43ee-8d85-57d72000c19d'
|
||||||
|
}
|
||||||
|
variant="outlined"
|
||||||
|
fullWidth
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
|
@ -410,7 +481,7 @@ function Installation(props: singleInstallationProps) {
|
||||||
<IconButton
|
<IconButton
|
||||||
color="inherit"
|
color="inherit"
|
||||||
size="small"
|
size="small"
|
||||||
onClick={() => setError(false)} // Set error state to false on click
|
onClick={() => setError(false)}
|
||||||
sx={{ marginLeft: '4px' }}
|
sx={{ marginLeft: '4px' }}
|
||||||
>
|
>
|
||||||
<CloseIcon fontSize="small" />
|
<CloseIcon fontSize="small" />
|
||||||
|
@ -448,6 +519,12 @@ function Installation(props: singleInstallationProps) {
|
||||||
</Grid>
|
</Grid>
|
||||||
</Container>
|
</Container>
|
||||||
)}
|
)}
|
||||||
|
{currentTab === 'overview' && (
|
||||||
|
<Overview s3Credentials={s3Credentials}></Overview>
|
||||||
|
)}
|
||||||
|
{currentTab === 'configuration' && currentUser.hasWriteAccess && (
|
||||||
|
<Configuration values={values}></Configuration>
|
||||||
|
)}
|
||||||
{currentTab === 'manage' && currentUser.hasWriteAccess && (
|
{currentTab === 'manage' && currentUser.hasWriteAccess && (
|
||||||
<AccessContextProvider>
|
<AccessContextProvider>
|
||||||
<Access
|
<Access
|
||||||
|
@ -456,13 +533,7 @@ function Installation(props: singleInstallationProps) {
|
||||||
></Access>
|
></Access>
|
||||||
</AccessContextProvider>
|
</AccessContextProvider>
|
||||||
)}
|
)}
|
||||||
{currentTab === 'live' && (
|
{currentTab === 'live' && <Topology values={values}></Topology>}
|
||||||
<LiveView
|
|
||||||
warnings={warnings}
|
|
||||||
errors={errors}
|
|
||||||
errorLoadingS3Data={errorLoadingS3Data}
|
|
||||||
></LiveView>
|
|
||||||
)}
|
|
||||||
{currentTab === 'log' && (
|
{currentTab === 'log' && (
|
||||||
<Log
|
<Log
|
||||||
warnings={warnings}
|
warnings={warnings}
|
||||||
|
@ -473,8 +544,10 @@ function Installation(props: singleInstallationProps) {
|
||||||
</Grid>
|
</Grid>
|
||||||
</Card>
|
</Card>
|
||||||
</Grid>
|
</Grid>
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Installation;
|
export default Installation;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { useContext, useEffect, useState } from 'react';
|
import React, { useContext, useEffect, useState } from 'react';
|
||||||
import {
|
import {
|
||||||
FormControl,
|
FormControl,
|
||||||
Grid,
|
Grid,
|
||||||
|
@ -9,32 +9,42 @@ import {
|
||||||
import { InstallationsContext } from 'src/contexts/InstallationsContextProvider';
|
import { InstallationsContext } from 'src/contexts/InstallationsContextProvider';
|
||||||
import SearchTwoToneIcon from '@mui/icons-material/SearchTwoTone';
|
import SearchTwoToneIcon from '@mui/icons-material/SearchTwoTone';
|
||||||
import FlatInstallationView from 'src/content/dashboards/Installations/FlatInstallationView';
|
import FlatInstallationView from 'src/content/dashboards/Installations/FlatInstallationView';
|
||||||
|
import LogContextProvider from 'src/contexts/LogContextProvider';
|
||||||
|
|
||||||
function InstallationSearch() {
|
function InstallationSearch() {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const [searchTerm, setSearchTerm] = useState('');
|
const [searchTerm, setSearchTerm] = useState('');
|
||||||
const { data, fetchAllInstallations } = useContext(InstallationsContext);
|
const { installations, fetchAllInstallations } =
|
||||||
|
useContext(InstallationsContext);
|
||||||
|
|
||||||
|
const searchParams = new URLSearchParams(location.search);
|
||||||
|
const installationId = parseInt(searchParams.get('installation'));
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchAllInstallations();
|
fetchAllInstallations();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const [filteredData, setFilteredData] = useState(data);
|
const [filteredData, setFilteredData] = useState(installations);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const filtered = data.filter(
|
const filtered = installations.filter(
|
||||||
(item) =>
|
(item) =>
|
||||||
item.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
item.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||||
item.location.toLowerCase().includes(searchTerm.toLowerCase())
|
item.location.toLowerCase().includes(searchTerm.toLowerCase())
|
||||||
);
|
);
|
||||||
setFilteredData(filtered);
|
setFilteredData(filtered);
|
||||||
}, [searchTerm, data]);
|
}, [searchTerm, installations]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Grid container spacing={4}>
|
<Grid container>
|
||||||
<Grid item xs={12} md={3}>
|
<Grid
|
||||||
<FormControl variant="outlined" fullWidth>
|
item
|
||||||
|
xs={12}
|
||||||
|
md={4}
|
||||||
|
sx={{ display: !installationId ? 'block' : 'none' }}
|
||||||
|
>
|
||||||
|
<FormControl variant="outlined">
|
||||||
<TextField
|
<TextField
|
||||||
placeholder="Search"
|
placeholder="Search"
|
||||||
value={searchTerm}
|
value={searchTerm}
|
||||||
|
@ -51,7 +61,9 @@ function InstallationSearch() {
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
<LogContextProvider>
|
||||||
<FlatInstallationView installations={filteredData} />
|
<FlatInstallationView installations={filteredData} />
|
||||||
|
</LogContextProvider>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,25 +1,30 @@
|
||||||
import { ChangeEvent, useState } from 'react';
|
import React, { ChangeEvent, useEffect, useState } from 'react';
|
||||||
import Footer from 'src/components/Footer';
|
import Footer from 'src/components/Footer';
|
||||||
import { Box, Card, Container, Grid, Tab, Tabs, useTheme } from '@mui/material';
|
import { Card, Container, Grid, Tab, Tabs, useTheme } from '@mui/material';
|
||||||
import InstallationsContextProvider from 'src/contexts/InstallationsContextProvider';
|
|
||||||
import InstallationSearch from './InstallationSearch';
|
|
||||||
import ListIcon from '@mui/icons-material/List';
|
import ListIcon from '@mui/icons-material/List';
|
||||||
import AccountTreeIcon from '@mui/icons-material/AccountTree';
|
import AccountTreeIcon from '@mui/icons-material/AccountTree';
|
||||||
import InstallationTree from '../Tree/InstallationTree';
|
|
||||||
import { TabsContainerWrapper } from 'src/layouts/TabsContainerWrapper';
|
import { TabsContainerWrapper } from 'src/layouts/TabsContainerWrapper';
|
||||||
import UsersContextProvider from 'src/contexts/UsersContextProvider';
|
import UsersContextProvider from 'src/contexts/UsersContextProvider';
|
||||||
import LogContextProvider from '../../../contexts/LogContextProvider';
|
import {
|
||||||
|
Link,
|
||||||
|
Route,
|
||||||
|
Routes,
|
||||||
|
useLocation,
|
||||||
|
useNavigate
|
||||||
|
} from 'react-router-dom';
|
||||||
|
import FlatView from './flatView';
|
||||||
|
import TreeView from '../Tree/treeView';
|
||||||
|
import routes from 'src/Resources/routes.json';
|
||||||
|
|
||||||
function InstallationTabs() {
|
function InstallationTabs() {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
const location = useLocation();
|
||||||
const [currentTab, setCurrentTab] = useState<string>('flat');
|
const navigate = useNavigate();
|
||||||
const tabs = [
|
const tabs = [
|
||||||
{
|
{
|
||||||
value: 'flat',
|
value: 'list',
|
||||||
label: 'Flat view',
|
label: 'Flat view',
|
||||||
icon: <ListIcon id="mode-toggle-button-list-icon" />,
|
icon: <ListIcon id="mode-toggle-button-list-icon" />
|
||||||
component: {}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: 'tree',
|
value: 'tree',
|
||||||
|
@ -27,9 +32,25 @@ function InstallationTabs() {
|
||||||
icon: <AccountTreeIcon id="mode-toggle-button-tree-icon" />
|
icon: <AccountTreeIcon id="mode-toggle-button-tree-icon" />
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
const [currentTab, setCurrentTab] = useState<string>('list');
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
//console.log(location.pathname);
|
||||||
|
if (
|
||||||
|
location.pathname === '/installations' ||
|
||||||
|
location.pathname === '/installations/'
|
||||||
|
) {
|
||||||
|
navigate(routes.installations + routes.list, {
|
||||||
|
replace: true
|
||||||
|
});
|
||||||
|
} else if (location.pathname === '/installations/tree') {
|
||||||
|
setCurrentTab('tree');
|
||||||
|
}
|
||||||
|
}, [location.pathname, navigate]);
|
||||||
|
|
||||||
const handleTabsChange = (_event: ChangeEvent<{}>, value: string): void => {
|
const handleTabsChange = (_event: ChangeEvent<{}>, value: string): void => {
|
||||||
setCurrentTab(value);
|
setCurrentTab(value);
|
||||||
|
navigate(value);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -45,12 +66,16 @@ function InstallationTabs() {
|
||||||
indicatorColor="primary"
|
indicatorColor="primary"
|
||||||
>
|
>
|
||||||
{tabs.map((tab) => (
|
{tabs.map((tab) => (
|
||||||
<Tab key={tab.value} value={tab.value} icon={tab.icon} />
|
<Tab
|
||||||
|
key={tab.value}
|
||||||
|
value={tab.value}
|
||||||
|
icon={tab.icon}
|
||||||
|
component={Link}
|
||||||
|
to={routes[tab.value]}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</TabsContainerWrapper>
|
</TabsContainerWrapper>
|
||||||
<InstallationsContextProvider>
|
|
||||||
<LogContextProvider>
|
|
||||||
<Card variant="outlined">
|
<Card variant="outlined">
|
||||||
<Grid
|
<Grid
|
||||||
container
|
container
|
||||||
|
@ -59,26 +84,12 @@ function InstallationTabs() {
|
||||||
alignItems="stretch"
|
alignItems="stretch"
|
||||||
spacing={0}
|
spacing={0}
|
||||||
>
|
>
|
||||||
{currentTab === 'tree' && (
|
<Routes>
|
||||||
<>
|
<Route path={routes.list + '*'} element={<FlatView />} />
|
||||||
<Grid item xs={12}>
|
<Route path={routes.tree + '*'} element={<TreeView />} />
|
||||||
<Box p={4}>
|
</Routes>
|
||||||
<InstallationTree />
|
|
||||||
</Box>
|
|
||||||
</Grid>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{currentTab === 'flat' && (
|
|
||||||
<Grid item xs={12}>
|
|
||||||
<Box p={4}>
|
|
||||||
<InstallationSearch />
|
|
||||||
</Box>
|
|
||||||
</Grid>
|
|
||||||
)}
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</Card>
|
</Card>
|
||||||
</LogContextProvider>
|
|
||||||
</InstallationsContextProvider>
|
|
||||||
</Container>
|
</Container>
|
||||||
<Footer />
|
<Footer />
|
||||||
</UsersContextProvider>
|
</UsersContextProvider>
|
||||||
|
|
|
@ -13,6 +13,7 @@ import { Close as CloseIcon } from '@mui/icons-material';
|
||||||
import { I_Installation } from 'src/interfaces/InstallationTypes';
|
import { I_Installation } from 'src/interfaces/InstallationTypes';
|
||||||
import { TokenContext } from 'src/contexts/tokenContext';
|
import { TokenContext } from 'src/contexts/tokenContext';
|
||||||
import { InstallationsContext } from 'src/contexts/InstallationsContextProvider';
|
import { InstallationsContext } from 'src/contexts/InstallationsContextProvider';
|
||||||
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
interface installationFormProps {
|
interface installationFormProps {
|
||||||
cancel: () => void;
|
cancel: () => void;
|
||||||
|
@ -96,7 +97,12 @@ function installationForm(props: installationFormProps) {
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<TextField
|
<TextField
|
||||||
label="Customer Name"
|
label={
|
||||||
|
<FormattedMessage
|
||||||
|
id="customerName"
|
||||||
|
defaultMessage="Customer Name"
|
||||||
|
/>
|
||||||
|
}
|
||||||
name="name"
|
name="name"
|
||||||
value={formValues.name}
|
value={formValues.name}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
|
@ -107,7 +113,7 @@ function installationForm(props: installationFormProps) {
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<TextField
|
<TextField
|
||||||
label="Region"
|
label={<FormattedMessage id="region" defaultMessage="Region" />}
|
||||||
name="region"
|
name="region"
|
||||||
value={formValues.region}
|
value={formValues.region}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
|
@ -118,7 +124,9 @@ function installationForm(props: installationFormProps) {
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<TextField
|
<TextField
|
||||||
label="Location"
|
label={
|
||||||
|
<FormattedMessage id="location" defaultMessage="Location" />
|
||||||
|
}
|
||||||
name="location"
|
name="location"
|
||||||
value={formValues.location}
|
value={formValues.location}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
|
@ -130,7 +138,9 @@ function installationForm(props: installationFormProps) {
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<TextField
|
<TextField
|
||||||
label="Country"
|
label={
|
||||||
|
<FormattedMessage id="country" defaultMessage="Country" />
|
||||||
|
}
|
||||||
name="country"
|
name="country"
|
||||||
value={formValues.country}
|
value={formValues.country}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
|
@ -141,7 +151,12 @@ function installationForm(props: installationFormProps) {
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<TextField
|
<TextField
|
||||||
label="Order Numbers"
|
label={
|
||||||
|
<FormattedMessage
|
||||||
|
id="orderNumbers"
|
||||||
|
defaultMessage="Order Numbers"
|
||||||
|
/>
|
||||||
|
}
|
||||||
name="orderNumbers"
|
name="orderNumbers"
|
||||||
value={formValues.orderNumbers}
|
value={formValues.orderNumbers}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
|
@ -164,7 +179,7 @@ function installationForm(props: installationFormProps) {
|
||||||
}}
|
}}
|
||||||
disabled={!areRequiredFieldsFilled()}
|
disabled={!areRequiredFieldsFilled()}
|
||||||
>
|
>
|
||||||
Submit
|
<FormattedMessage id="submit" defaultMessage="Submit" />
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
|
@ -174,7 +189,7 @@ function installationForm(props: installationFormProps) {
|
||||||
marginLeft: '10px'
|
marginLeft: '10px'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Cancel
|
<FormattedMessage id="cancel" defaultMessage="Cancel" />
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
{loading && (
|
{loading && (
|
||||||
|
@ -195,11 +210,14 @@ function installationForm(props: installationFormProps) {
|
||||||
alignItems: 'center'
|
alignItems: 'center'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
An error has occurred
|
<FormattedMessage
|
||||||
|
id="errorOccured"
|
||||||
|
defaultMessage="An error has occured"
|
||||||
|
/>
|
||||||
<IconButton
|
<IconButton
|
||||||
color="inherit"
|
color="inherit"
|
||||||
size="small"
|
size="small"
|
||||||
onClick={() => setError(false)} // Set error state to false on click
|
onClick={() => setError(false)}
|
||||||
sx={{ marginLeft: '4px' }}
|
sx={{ marginLeft: '4px' }}
|
||||||
>
|
>
|
||||||
<CloseIcon fontSize="small" />
|
<CloseIcon fontSize="small" />
|
||||||
|
|
|
@ -1,184 +0,0 @@
|
||||||
import React, { Fragment } from 'react';
|
|
||||||
import {
|
|
||||||
Alert,
|
|
||||||
Container,
|
|
||||||
Divider,
|
|
||||||
Grid,
|
|
||||||
IconButton,
|
|
||||||
ListItem,
|
|
||||||
useTheme
|
|
||||||
} from '@mui/material';
|
|
||||||
import ListItemAvatar from '@mui/material/ListItemAvatar';
|
|
||||||
import Avatar from '@mui/material/Avatar';
|
|
||||||
import ListItemText from '@mui/material/ListItemText';
|
|
||||||
import WarningIcon from '@mui/icons-material/Warning';
|
|
||||||
import ErrorIcon from '@mui/icons-material/Error';
|
|
||||||
import Typography from '@mui/material/Typography';
|
|
||||||
import { Notification } from 'src/interfaces/S3Types';
|
|
||||||
|
|
||||||
interface LiveViewProps {
|
|
||||||
warnings: Notification[];
|
|
||||||
errors: Notification[];
|
|
||||||
errorLoadingS3Data: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
function LiveView(props: LiveViewProps) {
|
|
||||||
const theme = useTheme();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Container maxWidth="xl">
|
|
||||||
<Grid container>
|
|
||||||
<Grid item xs={12} md={12}>
|
|
||||||
{props.errors.length > 0 &&
|
|
||||||
props.errors.map((error) => {
|
|
||||||
return (
|
|
||||||
<Fragment key={error.key + error.value}>
|
|
||||||
<ListItem>
|
|
||||||
<ListItemAvatar>
|
|
||||||
<Avatar
|
|
||||||
sx={{
|
|
||||||
backgroundColor: 'red',
|
|
||||||
width: 28,
|
|
||||||
height: 28
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ErrorIcon
|
|
||||||
sx={{
|
|
||||||
color: 'white',
|
|
||||||
width: 16,
|
|
||||||
height: 16
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Avatar>
|
|
||||||
</ListItemAvatar>
|
|
||||||
<ListItemText
|
|
||||||
primary={
|
|
||||||
<Typography variant="body1" sx={{ fontWeight: 'bold' }}>
|
|
||||||
{'Error from: ' +
|
|
||||||
error.key +
|
|
||||||
' device: ' +
|
|
||||||
error.value}
|
|
||||||
</Typography>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</ListItem>
|
|
||||||
<Divider />
|
|
||||||
</Fragment>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
{!props.errorLoadingS3Data && props.errors.length == 0 && (
|
|
||||||
<Alert
|
|
||||||
severity="error"
|
|
||||||
sx={{
|
|
||||||
ml: 1,
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
marginTop: '20px',
|
|
||||||
marginBottom: '20px'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
There are no errors
|
|
||||||
<IconButton
|
|
||||||
color="inherit"
|
|
||||||
size="small"
|
|
||||||
sx={{ marginLeft: '4px' }}
|
|
||||||
></IconButton>
|
|
||||||
</Alert>
|
|
||||||
)}
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={12} md={12}>
|
|
||||||
{props.errors.length > 0 && props.warnings.length > 0 && (
|
|
||||||
<Divider
|
|
||||||
sx={{
|
|
||||||
borderBottomWidth: '2px',
|
|
||||||
borderColor: '#ffffff',
|
|
||||||
'&.MuiDivider-root': {
|
|
||||||
backgroundColor: theme.palette.secondary.light // Change the color as needed
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={12} md={12} style={{ marginBottom: '20px' }}>
|
|
||||||
{props.errorLoadingS3Data && (
|
|
||||||
<Alert
|
|
||||||
severity="error"
|
|
||||||
sx={{
|
|
||||||
ml: 1,
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
marginTop: '20px'
|
|
||||||
//marginBottom: '20px'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Cannot load logging data
|
|
||||||
<IconButton
|
|
||||||
color="inherit"
|
|
||||||
size="small"
|
|
||||||
sx={{ marginLeft: '4px' }}
|
|
||||||
></IconButton>
|
|
||||||
</Alert>
|
|
||||||
)}
|
|
||||||
{props.warnings.length > 0 &&
|
|
||||||
props.warnings.map((warning) => {
|
|
||||||
return (
|
|
||||||
<Fragment key={warning.key + warning.value}>
|
|
||||||
<ListItem>
|
|
||||||
<ListItemAvatar>
|
|
||||||
<Avatar
|
|
||||||
sx={{
|
|
||||||
backgroundColor: '#ffc04d',
|
|
||||||
width: 28,
|
|
||||||
height: 28
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<WarningIcon
|
|
||||||
sx={{
|
|
||||||
color: '#ffffff',
|
|
||||||
width: 16,
|
|
||||||
height: 16
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Avatar>
|
|
||||||
</ListItemAvatar>
|
|
||||||
<ListItemText
|
|
||||||
primary={
|
|
||||||
<Typography variant="body1" sx={{ fontWeight: 'bold' }}>
|
|
||||||
{'Warning from: ' +
|
|
||||||
warning.key +
|
|
||||||
' device: ' +
|
|
||||||
warning.value}
|
|
||||||
</Typography>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</ListItem>
|
|
||||||
<Divider />
|
|
||||||
</Fragment>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
|
|
||||||
{!props.errorLoadingS3Data && props.warnings.length == 0 && (
|
|
||||||
<Alert
|
|
||||||
severity="error"
|
|
||||||
sx={{
|
|
||||||
ml: 1,
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
marginBottom: '20px'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
There are no warnings
|
|
||||||
<IconButton
|
|
||||||
color="inherit"
|
|
||||||
size="small"
|
|
||||||
sx={{ marginLeft: '4px' }}
|
|
||||||
></IconButton>
|
|
||||||
</Alert>
|
|
||||||
)}
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</Container>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default LiveView;
|
|
|
@ -1,20 +1,24 @@
|
||||||
import React, { Fragment } from 'react';
|
import React from 'react';
|
||||||
import {
|
import {
|
||||||
Alert,
|
Alert,
|
||||||
|
Card,
|
||||||
Container,
|
Container,
|
||||||
Divider,
|
Divider,
|
||||||
Grid,
|
Grid,
|
||||||
IconButton,
|
IconButton,
|
||||||
ListItem,
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableContainer,
|
||||||
|
TableHead,
|
||||||
|
TableRow,
|
||||||
useTheme
|
useTheme
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import ListItemAvatar from '@mui/material/ListItemAvatar';
|
|
||||||
import Avatar from '@mui/material/Avatar';
|
|
||||||
import ListItemText from '@mui/material/ListItemText';
|
|
||||||
import WarningIcon from '@mui/icons-material/Warning';
|
import WarningIcon from '@mui/icons-material/Warning';
|
||||||
import ErrorIcon from '@mui/icons-material/Error';
|
|
||||||
import Typography from '@mui/material/Typography';
|
import Typography from '@mui/material/Typography';
|
||||||
import { Notification } from 'src/interfaces/S3Types';
|
import { Notification } from 'src/interfaces/S3Types';
|
||||||
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
import ErrorIcon from '@mui/icons-material/Error';
|
||||||
|
|
||||||
interface LogProps {
|
interface LogProps {
|
||||||
warnings: Notification[];
|
warnings: Notification[];
|
||||||
|
@ -29,43 +33,171 @@ function Log(props: LogProps) {
|
||||||
<Container maxWidth="xl">
|
<Container maxWidth="xl">
|
||||||
<Grid container>
|
<Grid container>
|
||||||
<Grid item xs={12} md={12}>
|
<Grid item xs={12} md={12}>
|
||||||
{props.errors.length > 0 &&
|
{(props.errors.length > 0 || props.warnings.length > 0) && (
|
||||||
props.errors.map((error) => {
|
<Card>
|
||||||
|
<Divider />
|
||||||
|
<TableContainer>
|
||||||
|
<Table sx={{ height: 10 }}>
|
||||||
|
<TableHead>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell>
|
||||||
|
<FormattedMessage id="type" defaultMessage="Type" />
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<FormattedMessage id="device" defaultMessage="Device" />
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<FormattedMessage
|
||||||
|
id="description"
|
||||||
|
defaultMessage="Description"
|
||||||
|
/>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<FormattedMessage id="date" defaultMessage="Date" />
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<FormattedMessage id="time" defaultMessage="Time" />
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
</TableHead>
|
||||||
|
<TableBody>
|
||||||
|
{props.errors.map((error, index) => {
|
||||||
return (
|
return (
|
||||||
<Fragment key={error.key + error.value}>
|
<TableRow hover key={index}>
|
||||||
<ListItem>
|
<TableCell>
|
||||||
<ListItemAvatar>
|
|
||||||
<Avatar
|
|
||||||
sx={{
|
|
||||||
backgroundColor: 'red',
|
|
||||||
width: 28,
|
|
||||||
height: 28
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ErrorIcon
|
<ErrorIcon
|
||||||
sx={{
|
sx={{
|
||||||
color: 'white',
|
color: 'red',
|
||||||
width: 16,
|
width: 25,
|
||||||
height: 16
|
height: 25,
|
||||||
|
marginLeft: '5px',
|
||||||
|
marginTop: '8px'
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Avatar>
|
</TableCell>
|
||||||
</ListItemAvatar>
|
<TableCell>
|
||||||
<ListItemText
|
<Typography
|
||||||
primary={
|
variant="body1"
|
||||||
<Typography variant="body1" sx={{ fontWeight: 'bold' }}>
|
fontWeight="bold"
|
||||||
{'Error from: ' +
|
color="text.primary"
|
||||||
error.key +
|
gutterBottom
|
||||||
' device: ' +
|
noWrap
|
||||||
error.value}
|
sx={{ marginTop: '10px' }}
|
||||||
|
>
|
||||||
|
{error.device}
|
||||||
</Typography>
|
</Typography>
|
||||||
}
|
</TableCell>
|
||||||
/>
|
<TableCell>
|
||||||
</ListItem>
|
<Typography
|
||||||
<Divider />
|
variant="body1"
|
||||||
</Fragment>
|
fontWeight="bold"
|
||||||
|
color="text.primary"
|
||||||
|
gutterBottom
|
||||||
|
noWrap
|
||||||
|
sx={{ marginTop: '10px' }}
|
||||||
|
>
|
||||||
|
{error.description}
|
||||||
|
</Typography>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<Typography
|
||||||
|
variant="body1"
|
||||||
|
fontWeight="bold"
|
||||||
|
color="text.primary"
|
||||||
|
gutterBottom
|
||||||
|
noWrap
|
||||||
|
sx={{ marginTop: '10px' }}
|
||||||
|
>
|
||||||
|
{error.date}
|
||||||
|
</Typography>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<Typography
|
||||||
|
variant="body1"
|
||||||
|
fontWeight="bold"
|
||||||
|
color="text.primary"
|
||||||
|
gutterBottom
|
||||||
|
noWrap
|
||||||
|
sx={{ marginTop: '10px' }}
|
||||||
|
>
|
||||||
|
{error.time}
|
||||||
|
</Typography>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
||||||
|
{props.warnings.map((warning, index) => {
|
||||||
|
return (
|
||||||
|
<TableRow hover key={index}>
|
||||||
|
<TableCell>
|
||||||
|
<WarningIcon
|
||||||
|
sx={{
|
||||||
|
color: '#ffc04d',
|
||||||
|
width: 25,
|
||||||
|
height: 25,
|
||||||
|
marginLeft: '5px',
|
||||||
|
marginTop: '8px'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<Typography
|
||||||
|
variant="body1"
|
||||||
|
fontWeight="bold"
|
||||||
|
color="text.primary"
|
||||||
|
gutterBottom
|
||||||
|
noWrap
|
||||||
|
sx={{ marginTop: '10px' }}
|
||||||
|
>
|
||||||
|
{warning.device}
|
||||||
|
</Typography>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<Typography
|
||||||
|
variant="body1"
|
||||||
|
fontWeight="bold"
|
||||||
|
color="text.primary"
|
||||||
|
gutterBottom
|
||||||
|
noWrap
|
||||||
|
sx={{ marginTop: '10px' }}
|
||||||
|
>
|
||||||
|
{warning.description}
|
||||||
|
</Typography>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<Typography
|
||||||
|
variant="body1"
|
||||||
|
fontWeight="bold"
|
||||||
|
color="text.primary"
|
||||||
|
gutterBottom
|
||||||
|
noWrap
|
||||||
|
sx={{ marginTop: '10px' }}
|
||||||
|
>
|
||||||
|
{warning.date}
|
||||||
|
</Typography>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<Typography
|
||||||
|
variant="body1"
|
||||||
|
fontWeight="bold"
|
||||||
|
color="text.primary"
|
||||||
|
gutterBottom
|
||||||
|
noWrap
|
||||||
|
sx={{ marginTop: '10px' }}
|
||||||
|
>
|
||||||
|
{warning.time}
|
||||||
|
</Typography>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</TableContainer>
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
|
|
||||||
{!props.errorLoadingS3Data && props.errors.length == 0 && (
|
{!props.errorLoadingS3Data && props.errors.length == 0 && (
|
||||||
<Alert
|
<Alert
|
||||||
severity="error"
|
severity="error"
|
||||||
|
@ -77,7 +209,10 @@ function Log(props: LogProps) {
|
||||||
marginBottom: '20px'
|
marginBottom: '20px'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
There are no errors
|
<FormattedMessage
|
||||||
|
id="noerrors"
|
||||||
|
defaultMessage="There are no errors"
|
||||||
|
/>
|
||||||
<IconButton
|
<IconButton
|
||||||
color="inherit"
|
color="inherit"
|
||||||
size="small"
|
size="small"
|
||||||
|
@ -86,19 +221,7 @@ function Log(props: LogProps) {
|
||||||
</Alert>
|
</Alert>
|
||||||
)}
|
)}
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={12} md={12}>
|
|
||||||
{props.errors.length > 0 && props.warnings.length > 0 && (
|
|
||||||
<Divider
|
|
||||||
sx={{
|
|
||||||
borderBottomWidth: '2px',
|
|
||||||
borderColor: '#ffffff',
|
|
||||||
'&.MuiDivider-root': {
|
|
||||||
backgroundColor: theme.palette.secondary.light // Change the color as needed
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={12} md={12} style={{ marginBottom: '20px' }}>
|
<Grid item xs={12} md={12} style={{ marginBottom: '20px' }}>
|
||||||
{props.errorLoadingS3Data && (
|
{props.errorLoadingS3Data && (
|
||||||
<Alert
|
<Alert
|
||||||
|
@ -108,10 +231,12 @@ function Log(props: LogProps) {
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
marginTop: '20px'
|
marginTop: '20px'
|
||||||
//marginBottom: '20px'
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Cannot load logging data
|
<FormattedMessage
|
||||||
|
id="cannotloadloggingdata"
|
||||||
|
defaultMessage="Cannot load logging data"
|
||||||
|
/>
|
||||||
<IconButton
|
<IconButton
|
||||||
color="inherit"
|
color="inherit"
|
||||||
size="small"
|
size="small"
|
||||||
|
@ -119,43 +244,6 @@ function Log(props: LogProps) {
|
||||||
></IconButton>
|
></IconButton>
|
||||||
</Alert>
|
</Alert>
|
||||||
)}
|
)}
|
||||||
{props.warnings.length > 0 &&
|
|
||||||
props.warnings.map((warning) => {
|
|
||||||
return (
|
|
||||||
<Fragment key={warning.key + warning.value}>
|
|
||||||
<ListItem>
|
|
||||||
<ListItemAvatar>
|
|
||||||
<Avatar
|
|
||||||
sx={{
|
|
||||||
backgroundColor: '#ffc04d',
|
|
||||||
width: 28,
|
|
||||||
height: 28
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<WarningIcon
|
|
||||||
sx={{
|
|
||||||
color: '#ffffff',
|
|
||||||
width: 16,
|
|
||||||
height: 16
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Avatar>
|
|
||||||
</ListItemAvatar>
|
|
||||||
<ListItemText
|
|
||||||
primary={
|
|
||||||
<Typography variant="body1" sx={{ fontWeight: 'bold' }}>
|
|
||||||
{'Warning from: ' +
|
|
||||||
warning.key +
|
|
||||||
' device: ' +
|
|
||||||
warning.value}
|
|
||||||
</Typography>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</ListItem>
|
|
||||||
<Divider />
|
|
||||||
</Fragment>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
|
|
||||||
{!props.errorLoadingS3Data && props.warnings.length == 0 && (
|
{!props.errorLoadingS3Data && props.warnings.length == 0 && (
|
||||||
<Alert
|
<Alert
|
||||||
|
@ -163,11 +251,14 @@ function Log(props: LogProps) {
|
||||||
sx={{
|
sx={{
|
||||||
ml: 1,
|
ml: 1,
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center'
|
||||||
marginBottom: '20px'
|
//marginBottom: '20px'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
There are no warnings
|
<FormattedMessage
|
||||||
|
id="nowarnings"
|
||||||
|
defaultMessage="There are no warnings"
|
||||||
|
/>
|
||||||
<IconButton
|
<IconButton
|
||||||
color="inherit"
|
color="inherit"
|
||||||
size="small"
|
size="small"
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { DataRecord } from 'src/dataCache/data';
|
import { DataPoint, DataRecord } from 'src/dataCache/data';
|
||||||
|
import { TimeRange, UnixTime } from 'src/dataCache/time';
|
||||||
|
|
||||||
export interface I_CsvEntry {
|
export interface I_CsvEntry {
|
||||||
value: string | number;
|
value: string | number;
|
||||||
|
@ -22,3 +23,173 @@ export const parseCsv = (text: string): DataRecord => {
|
||||||
})
|
})
|
||||||
.reduce((acc, current) => ({ ...acc, ...current }), {} as DataRecord);
|
.reduce((acc, current) => ({ ...acc, ...current }), {} as DataRecord);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export interface I_BoxDataValue {
|
||||||
|
unit: string;
|
||||||
|
value: string | number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type BoxData = {
|
||||||
|
label: string;
|
||||||
|
values: I_BoxDataValue[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TopologyValues = {
|
||||||
|
grid: BoxData;
|
||||||
|
gridToAcInConnection: BoxData;
|
||||||
|
gridBus: BoxData;
|
||||||
|
islandBus: BoxData;
|
||||||
|
dcBus: BoxData;
|
||||||
|
dcBusToDcDcConnection: BoxData;
|
||||||
|
dcDCToBatteryConnection: BoxData;
|
||||||
|
battery: BoxData;
|
||||||
|
dcBusToLoadOnDcConnection: BoxData;
|
||||||
|
islandBusToLoadOnIslandBusConnection: BoxData;
|
||||||
|
gridBusToPvOnGridbusConnection: BoxData;
|
||||||
|
gridBusToLoadOnGridBusConnection: BoxData;
|
||||||
|
inverter: BoxData;
|
||||||
|
dcDc: BoxData;
|
||||||
|
islandBusToInverter: BoxData;
|
||||||
|
inverterToDcBus: BoxData;
|
||||||
|
gridBusToIslandBusConnection: BoxData;
|
||||||
|
pvOnDcBusToDcBusConnection: BoxData;
|
||||||
|
pvOnIslandBusToIslandBusConnection: BoxData;
|
||||||
|
minimumSoC: BoxData;
|
||||||
|
installedDcDcPower: BoxData;
|
||||||
|
gridSetPoint: BoxData;
|
||||||
|
maximumDischargePower: BoxData;
|
||||||
|
calibrationChargeForced: BoxData;
|
||||||
|
};
|
||||||
|
|
||||||
|
type TopologyPaths = { [key in keyof TopologyValues]: string[] };
|
||||||
|
|
||||||
|
export const topologyPaths: TopologyPaths = {
|
||||||
|
grid: [
|
||||||
|
'/GridMeter/Ac/L1/Power/Active',
|
||||||
|
'/GridMeter/Ac/L2/Power/Active',
|
||||||
|
'/GridMeter/Ac/L3/Power/Active'
|
||||||
|
],
|
||||||
|
gridToAcInConnection: ['/GridMeter/Ac/Power/Active'],
|
||||||
|
|
||||||
|
gridBus: [
|
||||||
|
'/GridMeter/Ac/L1/Power/Active',
|
||||||
|
'/GridMeter/Ac/L2/Power/Active',
|
||||||
|
'/GridMeter/Ac/L3/Power/Active'
|
||||||
|
],
|
||||||
|
gridBusToPvOnGridbusConnection: ['/PvOnAcGrid/Power/Active'],
|
||||||
|
|
||||||
|
gridBusToLoadOnGridBusConnection: ['/LoadOnAcGrid/Power/Active'],
|
||||||
|
gridBusToIslandBusConnection: ['/AcGridToAcIsland/Power/Active'],
|
||||||
|
|
||||||
|
islandBus: [
|
||||||
|
'/AcDc/Ac/L1/Power/Active',
|
||||||
|
'/AcDc/Ac/L2/Power/Active',
|
||||||
|
'/AcDc/Ac/L3/Power/Active'
|
||||||
|
],
|
||||||
|
islandBusToLoadOnIslandBusConnection: ['/LoadOnAcIsland/Power/Active'],
|
||||||
|
islandBusToInverter: ['/AcDc/Dc/Power'],
|
||||||
|
pvOnIslandBusToIslandBusConnection: ['/PvOnAcIsland/Power/Active'],
|
||||||
|
|
||||||
|
inverter: [
|
||||||
|
'/AcDc/Devices/1/Status/Ac/Power/Active',
|
||||||
|
'/AcDc/Devices/2/Status/Ac/Power/Active',
|
||||||
|
'/AcDc/Devices/3/Status/Ac/Power/Active',
|
||||||
|
'/AcDc/Devices/4/Status/Ac/Power/Active'
|
||||||
|
],
|
||||||
|
inverterToDcBus: ['/AcDc/Dc/Power'],
|
||||||
|
|
||||||
|
dcBus: ['/DcDc/Dc/Link/Voltage'],
|
||||||
|
dcBusToDcDcConnection: ['/DcDc/Dc/Link/Power'],
|
||||||
|
pvOnDcBusToDcBusConnection: ['/PvOnDc/Dc/Power'],
|
||||||
|
dcBusToLoadOnDcConnection: ['/LoadOnDc/Power'],
|
||||||
|
|
||||||
|
dcDc: ['/DcDc/Dc/Battery/Voltage'],
|
||||||
|
|
||||||
|
dcDCToBatteryConnection: ['/DcDc/Dc/Link/Power'],
|
||||||
|
battery: [
|
||||||
|
'/Battery/Soc',
|
||||||
|
'/Battery/Dc/Voltage',
|
||||||
|
'/Battery/Dc/Current',
|
||||||
|
'/Battery/Temperature',
|
||||||
|
'/Battery/Devices/1/Dc/Voltage',
|
||||||
|
'/Battery/Devices/2/Dc/Voltage',
|
||||||
|
'/Battery/Devices/3/Dc/Voltage',
|
||||||
|
'/Battery/Devices/4/Dc/Voltage',
|
||||||
|
'/Battery/Devices/5/Dc/Voltage',
|
||||||
|
'/Battery/Devices/6/Dc/Voltage',
|
||||||
|
'/Battery/Devices/7/Dc/Voltage',
|
||||||
|
'/Battery/Devices/8/Dc/Voltage',
|
||||||
|
'/Battery/Devices/9/Dc/Voltage',
|
||||||
|
'/Battery/Devices/10/Dc/Voltage'
|
||||||
|
],
|
||||||
|
|
||||||
|
minimumSoC: ['/Config/MinSoc'],
|
||||||
|
installedDcDcPower: ['/DcDc/SystemControl/TargetSlave'],
|
||||||
|
gridSetPoint: ['/Config/GridSetPoint'],
|
||||||
|
maximumDischargePower: ['/Config/MaxBatteryDischargingCurrent'],
|
||||||
|
calibrationChargeForced: ['/Config/ForceCalibrationCharge']
|
||||||
|
};
|
||||||
|
|
||||||
|
export const extractValues = (
|
||||||
|
timeSeriesData: DataPoint
|
||||||
|
): TopologyValues | null => {
|
||||||
|
const extractedValues: TopologyValues = {} as TopologyValues;
|
||||||
|
|
||||||
|
for (const topologyKey of Object.keys(topologyPaths)) {
|
||||||
|
const paths = topologyPaths[topologyKey];
|
||||||
|
let topologyValues: { unit: string; value: string | number }[] = [];
|
||||||
|
//console.log('paths is ', paths);
|
||||||
|
|
||||||
|
// Check if any of the specified paths exist in the dataRecord
|
||||||
|
for (const path of paths) {
|
||||||
|
//console.log(' path is ', path);
|
||||||
|
if (timeSeriesData.value.hasOwnProperty(path)) {
|
||||||
|
//console.log('matching path is ', path);
|
||||||
|
//console.log(timeSeriesData.value[path]);
|
||||||
|
topologyValues.push({
|
||||||
|
unit: timeSeriesData.value[path].unit,
|
||||||
|
value: timeSeriesData.value[path].value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (topologyValues.length > 0) {
|
||||||
|
extractedValues[topologyKey] = {
|
||||||
|
label: topologyPaths[topologyKey as keyof TopologyValues][0]
|
||||||
|
.split('/')
|
||||||
|
.pop(),
|
||||||
|
values: topologyValues
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return extractedValues;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getHighestConnectionValue = (values: TopologyValues) =>
|
||||||
|
Object.keys(values)
|
||||||
|
.filter((value) => value.includes('Connection'))
|
||||||
|
.reduce((acc, curr) => {
|
||||||
|
const value = Math.abs(
|
||||||
|
values[curr as keyof TopologyValues].values[0].value as number
|
||||||
|
);
|
||||||
|
return value > acc ? value : acc;
|
||||||
|
}, 0);
|
||||||
|
|
||||||
|
export const getAmount = (
|
||||||
|
highestConnectionValue: number,
|
||||||
|
values: I_BoxDataValue[]
|
||||||
|
) => {
|
||||||
|
return Math.abs(values[0].value as number) / highestConnectionValue;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createTimes = (
|
||||||
|
range: TimeRange,
|
||||||
|
numberOfNodes: number
|
||||||
|
): UnixTime[] => {
|
||||||
|
const oneSpan = range.duration.divide(numberOfNodes);
|
||||||
|
const roundedRange = TimeRange.fromTimes(
|
||||||
|
range.start.round(oneSpan),
|
||||||
|
range.end.round(oneSpan)
|
||||||
|
);
|
||||||
|
return roundedRange.sample(oneSpan);
|
||||||
|
};
|
||||||
|
|
|
@ -17,7 +17,7 @@ import {
|
||||||
import { TokenContext } from 'src/contexts/tokenContext';
|
import { TokenContext } from 'src/contexts/tokenContext';
|
||||||
import { UserContext } from 'src/contexts/userContext';
|
import { UserContext } from 'src/contexts/userContext';
|
||||||
import { I_Folder, I_Installation } from 'src/interfaces/InstallationTypes';
|
import { I_Folder, I_Installation } from 'src/interfaces/InstallationTypes';
|
||||||
import axiosConfig from '../../../Resources/axiosConfig';
|
import axiosConfig from 'src/Resources/axiosConfig';
|
||||||
import ListItemAvatar from '@mui/material/ListItemAvatar';
|
import ListItemAvatar from '@mui/material/ListItemAvatar';
|
||||||
import Avatar from '@mui/material/Avatar';
|
import Avatar from '@mui/material/Avatar';
|
||||||
import ListItemText from '@mui/material/ListItemText';
|
import ListItemText from '@mui/material/ListItemText';
|
||||||
|
@ -26,7 +26,8 @@ import PersonIcon from '@mui/icons-material/Person';
|
||||||
import Button from '@mui/material/Button';
|
import Button from '@mui/material/Button';
|
||||||
import { Close as CloseIcon } from '@mui/icons-material';
|
import { Close as CloseIcon } from '@mui/icons-material';
|
||||||
import { UsersContext } from 'src/contexts/UsersContextProvider';
|
import { UsersContext } from 'src/contexts/UsersContextProvider';
|
||||||
import { AccessContext } from '../../../contexts/AccessContextProvider';
|
import { AccessContext } from 'src/contexts/AccessContextProvider';
|
||||||
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
interface AccessProps {
|
interface AccessProps {
|
||||||
currentResource: I_Folder | I_Installation;
|
currentResource: I_Folder | I_Installation;
|
||||||
|
@ -157,15 +158,32 @@ function Access(props: AccessProps) {
|
||||||
|
|
||||||
if (NotGrantedAccessUsers.length > 0) {
|
if (NotGrantedAccessUsers.length > 0) {
|
||||||
setError(true);
|
setError(true);
|
||||||
setErrorMessage(
|
|
||||||
'Unable to grant access to: ' + NotGrantedAccessUsers.join(', ')
|
const message =
|
||||||
);
|
(
|
||||||
|
<FormattedMessage
|
||||||
|
id="unableToGrantAccess"
|
||||||
|
defaultMessage="Unable to grant access to: "
|
||||||
|
/>
|
||||||
|
).props.defaultMessage +
|
||||||
|
' ' +
|
||||||
|
NotGrantedAccessUsers.join(', ');
|
||||||
|
|
||||||
|
setErrorMessage(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (grantedAccessUsers.length > 0) {
|
if (grantedAccessUsers.length > 0) {
|
||||||
setUpdatedMessage(
|
const message =
|
||||||
'Granted access to users: ' + grantedAccessUsers.join(', ')
|
(
|
||||||
);
|
<FormattedMessage
|
||||||
|
id="grantedAccessToUsers"
|
||||||
|
defaultMessage="Granted access to users: "
|
||||||
|
/>
|
||||||
|
).props.defaultMessage +
|
||||||
|
' ' +
|
||||||
|
grantedAccessUsers.join(', ');
|
||||||
|
setUpdatedMessage(message);
|
||||||
|
|
||||||
setUpdated(true);
|
setUpdated(true);
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
@ -248,7 +266,10 @@ function Access(props: AccessProps) {
|
||||||
//backgroundColor: 'white'
|
//backgroundColor: 'white'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Select users
|
<FormattedMessage
|
||||||
|
id="selectUsers"
|
||||||
|
defaultMessage="Select Users"
|
||||||
|
/>
|
||||||
</InputLabel>
|
</InputLabel>
|
||||||
<Select
|
<Select
|
||||||
multiple
|
multiple
|
||||||
|
@ -301,7 +322,7 @@ function Access(props: AccessProps) {
|
||||||
}}
|
}}
|
||||||
onClick={handleSubmit}
|
onClick={handleSubmit}
|
||||||
>
|
>
|
||||||
Submit
|
<FormattedMessage id="submit" defaultMessage="Submit" />
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
variant="contained"
|
variant="contained"
|
||||||
|
@ -317,7 +338,7 @@ function Access(props: AccessProps) {
|
||||||
padding: '6px 8px'
|
padding: '6px 8px'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Cancel
|
<FormattedMessage id="cancel" defaultMessage="Cancel" />
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
@ -333,7 +354,7 @@ function Access(props: AccessProps) {
|
||||||
'&:hover': { bgcolor: '#f7b34d' }
|
'&:hover': { bgcolor: '#f7b34d' }
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Grant Access
|
<FormattedMessage id="grantAccess" defaultMessage="Grant Access" />
|
||||||
</Button>
|
</Button>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={12} md={12}>
|
<Grid item xs={12} md={12}>
|
||||||
|
@ -342,7 +363,10 @@ function Access(props: AccessProps) {
|
||||||
onClick={handleDirectButtonPressed}
|
onClick={handleDirectButtonPressed}
|
||||||
sx={{ marginTop: '20px' }}
|
sx={{ marginTop: '20px' }}
|
||||||
>
|
>
|
||||||
Users with Direct Access
|
<FormattedMessage
|
||||||
|
id="UserswithDirectAccess"
|
||||||
|
defaultMessage="Users with Direct Access"
|
||||||
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
{directButtonPressed &&
|
{directButtonPressed &&
|
||||||
|
@ -382,8 +406,11 @@ function Access(props: AccessProps) {
|
||||||
marginTop: '20px'
|
marginTop: '20px'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
There are no users with direct access to this {props.resourceType}
|
<FormattedMessage
|
||||||
.
|
id="noUsersWithDirectAccessToThis"
|
||||||
|
defaultMessage="No users with direct access to this "
|
||||||
|
/>
|
||||||
|
{props.resourceType}
|
||||||
<IconButton
|
<IconButton
|
||||||
color="inherit"
|
color="inherit"
|
||||||
size="small"
|
size="small"
|
||||||
|
@ -398,7 +425,10 @@ function Access(props: AccessProps) {
|
||||||
onClick={handleInheritedButtonPressed}
|
onClick={handleInheritedButtonPressed}
|
||||||
sx={{ marginTop: '20px', marginBottom: '20px' }}
|
sx={{ marginTop: '20px', marginBottom: '20px' }}
|
||||||
>
|
>
|
||||||
Users with Inherited Access
|
<FormattedMessage
|
||||||
|
id="UserswithInheritedAccess"
|
||||||
|
defaultMessage="Users with Inherited Access"
|
||||||
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
{inheritedButtonPressed && usersWithInheritedAccess.length == 0 && (
|
{inheritedButtonPressed && usersWithInheritedAccess.length == 0 && (
|
||||||
<Alert
|
<Alert
|
||||||
|
@ -410,7 +440,10 @@ function Access(props: AccessProps) {
|
||||||
marginBottom: '20px'
|
marginBottom: '20px'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
There are no users with inherited access to this{' '}
|
<FormattedMessage
|
||||||
|
id="noUsersWithDirectAccessToThis"
|
||||||
|
defaultMessage="No users with direct access to this "
|
||||||
|
/>
|
||||||
{props.resourceType}.
|
{props.resourceType}.
|
||||||
<IconButton
|
<IconButton
|
||||||
color="inherit"
|
color="inherit"
|
||||||
|
|
|
@ -0,0 +1,290 @@
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import { Container, Grid, Switch } from '@mui/material';
|
||||||
|
import TopologyColumn from './topologyColumn';
|
||||||
|
import {
|
||||||
|
getAmount,
|
||||||
|
getHighestConnectionValue,
|
||||||
|
TopologyValues
|
||||||
|
} from '../Log/graph.util';
|
||||||
|
|
||||||
|
interface TopologyProps {
|
||||||
|
values: TopologyValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
function Topology(props: TopologyProps) {
|
||||||
|
if (props.values === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const highestConnectionValue = getHighestConnectionValue(props.values);
|
||||||
|
const [showValues, setShowValues] = useState(false);
|
||||||
|
|
||||||
|
const handleSwitch = () => () => {
|
||||||
|
setShowValues(!showValues);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container maxWidth="xl" style={{ backgroundColor: 'white' }}>
|
||||||
|
<Grid container>
|
||||||
|
<Grid
|
||||||
|
item
|
||||||
|
xs={12}
|
||||||
|
md={12}
|
||||||
|
style={{
|
||||||
|
marginTop: '10px',
|
||||||
|
height: '20px',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'right',
|
||||||
|
justifyContent: 'right'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Switch
|
||||||
|
edge="start"
|
||||||
|
color="secondary"
|
||||||
|
onChange={handleSwitch()}
|
||||||
|
sx={{
|
||||||
|
'& .MuiSwitch-thumb': {
|
||||||
|
backgroundColor: 'orange'
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Grid
|
||||||
|
item
|
||||||
|
xs={12}
|
||||||
|
md={12}
|
||||||
|
style={{
|
||||||
|
height: '600px',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<TopologyColumn
|
||||||
|
centerBox={{
|
||||||
|
title: 'Grid',
|
||||||
|
data: props.values.grid
|
||||||
|
}}
|
||||||
|
centerConnection={{
|
||||||
|
orientation: 'horizontal',
|
||||||
|
data: props.values.gridToAcInConnection,
|
||||||
|
amount: props.values.gridToAcInConnection
|
||||||
|
? getAmount(
|
||||||
|
highestConnectionValue,
|
||||||
|
props.values.gridToAcInConnection.values
|
||||||
|
)
|
||||||
|
: 0,
|
||||||
|
showValues: showValues
|
||||||
|
}}
|
||||||
|
isLast={false}
|
||||||
|
isFirst={true}
|
||||||
|
/>
|
||||||
|
<TopologyColumn
|
||||||
|
topBox={{
|
||||||
|
title: 'Pv Inverter',
|
||||||
|
data: props.values.gridBusToPvOnGridbusConnection
|
||||||
|
}}
|
||||||
|
topConnection={{
|
||||||
|
orientation: 'vertical',
|
||||||
|
position: 'top',
|
||||||
|
data: props.values.gridBusToPvOnGridbusConnection,
|
||||||
|
amount: props.values.gridBusToPvOnGridbusConnection
|
||||||
|
? getAmount(
|
||||||
|
highestConnectionValue,
|
||||||
|
props.values.gridBusToPvOnGridbusConnection.values
|
||||||
|
)
|
||||||
|
: 0,
|
||||||
|
showValues: showValues
|
||||||
|
}}
|
||||||
|
centerBox={{
|
||||||
|
title: 'Grid Bus',
|
||||||
|
data: props.values.gridBus
|
||||||
|
}}
|
||||||
|
centerConnection={{
|
||||||
|
orientation: 'horizontal',
|
||||||
|
data: props.values.gridBusToIslandBusConnection,
|
||||||
|
amount: props.values.gridBusToIslandBusConnection
|
||||||
|
? getAmount(
|
||||||
|
highestConnectionValue,
|
||||||
|
props.values.gridBusToIslandBusConnection.values
|
||||||
|
)
|
||||||
|
: 0,
|
||||||
|
showValues: showValues
|
||||||
|
}}
|
||||||
|
bottomBox={{
|
||||||
|
title: 'AC Loads',
|
||||||
|
data: props.values.gridBusToLoadOnGridBusConnection
|
||||||
|
}}
|
||||||
|
bottomConnection={{
|
||||||
|
orientation: 'vertical',
|
||||||
|
position: 'bottom',
|
||||||
|
data: props.values.gridBusToLoadOnGridBusConnection,
|
||||||
|
amount: props.values.gridBusToLoadOnGridBusConnection
|
||||||
|
? getAmount(
|
||||||
|
highestConnectionValue,
|
||||||
|
props.values.gridBusToLoadOnGridBusConnection.values
|
||||||
|
)
|
||||||
|
: 0,
|
||||||
|
showValues: showValues
|
||||||
|
}}
|
||||||
|
isLast={false}
|
||||||
|
isFirst={false}
|
||||||
|
/>
|
||||||
|
<TopologyColumn
|
||||||
|
topBox={{
|
||||||
|
title: 'Pv Inverter',
|
||||||
|
data: props.values.pvOnIslandBusToIslandBusConnection
|
||||||
|
}}
|
||||||
|
topConnection={{
|
||||||
|
orientation: 'vertical',
|
||||||
|
position: 'top',
|
||||||
|
data: props.values.pvOnIslandBusToIslandBusConnection,
|
||||||
|
amount: props.values.pvOnIslandBusToIslandBusConnection
|
||||||
|
? getAmount(
|
||||||
|
highestConnectionValue,
|
||||||
|
props.values.pvOnIslandBusToIslandBusConnection.values
|
||||||
|
)
|
||||||
|
: 0,
|
||||||
|
showValues: showValues
|
||||||
|
}}
|
||||||
|
centerBox={{
|
||||||
|
title: 'Island Bus',
|
||||||
|
data: props.values.islandBus
|
||||||
|
}}
|
||||||
|
centerConnection={{
|
||||||
|
orientation: 'horizontal',
|
||||||
|
data: props.values.islandBusToInverter,
|
||||||
|
amount: props.values.islandBusToInverter
|
||||||
|
? getAmount(
|
||||||
|
highestConnectionValue,
|
||||||
|
props.values.islandBusToInverter.values
|
||||||
|
)
|
||||||
|
: 0,
|
||||||
|
showValues: showValues
|
||||||
|
}}
|
||||||
|
bottomBox={{
|
||||||
|
title: 'AC Loads',
|
||||||
|
data: props.values.islandBusToLoadOnIslandBusConnection
|
||||||
|
}}
|
||||||
|
bottomConnection={{
|
||||||
|
orientation: 'vertical',
|
||||||
|
position: 'bottom',
|
||||||
|
data: props.values.islandBusToLoadOnIslandBusConnection,
|
||||||
|
amount: props.values.islandBusToLoadOnIslandBusConnection
|
||||||
|
? getAmount(
|
||||||
|
highestConnectionValue,
|
||||||
|
props.values.islandBusToLoadOnIslandBusConnection.values
|
||||||
|
)
|
||||||
|
: 0,
|
||||||
|
showValues: showValues
|
||||||
|
}}
|
||||||
|
isLast={false}
|
||||||
|
isFirst={false}
|
||||||
|
/>
|
||||||
|
<TopologyColumn
|
||||||
|
centerBox={{
|
||||||
|
title: 'AC-DC',
|
||||||
|
data: props.values.inverter
|
||||||
|
}}
|
||||||
|
centerConnection={{
|
||||||
|
orientation: 'horizontal',
|
||||||
|
data: props.values.inverterToDcBus,
|
||||||
|
amount: props.values.inverterToDcBus
|
||||||
|
? getAmount(
|
||||||
|
highestConnectionValue,
|
||||||
|
props.values.inverterToDcBus.values
|
||||||
|
)
|
||||||
|
: 0,
|
||||||
|
showValues: showValues
|
||||||
|
}}
|
||||||
|
isLast={false}
|
||||||
|
isFirst={false}
|
||||||
|
/>
|
||||||
|
<TopologyColumn
|
||||||
|
topBox={{
|
||||||
|
title: 'Pv DcDc',
|
||||||
|
data: props.values.pvOnDcBusToDcBusConnection
|
||||||
|
}}
|
||||||
|
topConnection={{
|
||||||
|
orientation: 'vertical',
|
||||||
|
position: 'top',
|
||||||
|
data: props.values.pvOnDcBusToDcBusConnection,
|
||||||
|
amount: props.values.pvOnDcBusToDcBusConnection
|
||||||
|
? getAmount(
|
||||||
|
highestConnectionValue,
|
||||||
|
props.values.pvOnDcBusToDcBusConnection.values
|
||||||
|
)
|
||||||
|
: 0,
|
||||||
|
showValues: showValues
|
||||||
|
}}
|
||||||
|
centerBox={{
|
||||||
|
title: 'DC Link',
|
||||||
|
data: props.values.dcBus
|
||||||
|
}}
|
||||||
|
centerConnection={{
|
||||||
|
orientation: 'horizontal',
|
||||||
|
data: props.values.dcBusToDcDcConnection,
|
||||||
|
amount: props.values.dcBusToDcDcConnection
|
||||||
|
? getAmount(
|
||||||
|
highestConnectionValue,
|
||||||
|
props.values.dcBusToDcDcConnection.values
|
||||||
|
)
|
||||||
|
: 0,
|
||||||
|
showValues: showValues
|
||||||
|
}}
|
||||||
|
bottomBox={{
|
||||||
|
title: 'DC Loads',
|
||||||
|
data: props.values.dcBusToLoadOnDcConnection
|
||||||
|
}}
|
||||||
|
bottomConnection={{
|
||||||
|
orientation: 'vertical',
|
||||||
|
position: 'bottom',
|
||||||
|
data: props.values.dcBusToLoadOnDcConnection,
|
||||||
|
amount: props.values.dcBusToLoadOnDcConnection
|
||||||
|
? getAmount(
|
||||||
|
highestConnectionValue,
|
||||||
|
props.values.dcBusToLoadOnDcConnection.values
|
||||||
|
)
|
||||||
|
: 0,
|
||||||
|
showValues: showValues
|
||||||
|
}}
|
||||||
|
isLast={false}
|
||||||
|
isFirst={false}
|
||||||
|
/>
|
||||||
|
<TopologyColumn
|
||||||
|
centerBox={{
|
||||||
|
title: 'DC-DC',
|
||||||
|
data: props.values.dcDc
|
||||||
|
}}
|
||||||
|
centerConnection={{
|
||||||
|
orientation: 'horizontal',
|
||||||
|
data: props.values.dcDCToBatteryConnection,
|
||||||
|
amount: props.values.dcDCToBatteryConnection
|
||||||
|
? getAmount(
|
||||||
|
highestConnectionValue,
|
||||||
|
props.values.dcDCToBatteryConnection.values
|
||||||
|
)
|
||||||
|
: 0,
|
||||||
|
showValues: showValues
|
||||||
|
}}
|
||||||
|
isLast={false}
|
||||||
|
isFirst={false}
|
||||||
|
/>
|
||||||
|
<TopologyColumn
|
||||||
|
centerBox={{
|
||||||
|
title: 'Battery',
|
||||||
|
data: props.values.battery
|
||||||
|
}}
|
||||||
|
isLast={true}
|
||||||
|
isFirst={false}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Topology;
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { ReactNode } from 'react';
|
import React, { ReactNode, useContext, useState } from 'react';
|
||||||
import { CircularProgress, ListItemIcon, useTheme } from '@mui/material';
|
import { CircularProgress, ListItemIcon, useTheme } from '@mui/material';
|
||||||
import { TreeItem } from '@mui/lab';
|
import { TreeItem } from '@mui/lab';
|
||||||
import { I_Folder, I_Installation } from 'src/interfaces/InstallationTypes';
|
import { I_Folder, I_Installation } from 'src/interfaces/InstallationTypes';
|
||||||
|
@ -7,13 +7,14 @@ import InsertDriveFileIcon from '@mui/icons-material/InsertDriveFile';
|
||||||
import Typography from '@mui/material/Typography';
|
import Typography from '@mui/material/Typography';
|
||||||
import { makeStyles } from '@mui/styles';
|
import { makeStyles } from '@mui/styles';
|
||||||
import CancelIcon from '@mui/icons-material/Cancel';
|
import CancelIcon from '@mui/icons-material/Cancel';
|
||||||
|
import { LogContext } from 'src/contexts/LogContextProvider';
|
||||||
|
import routes from 'src/Resources/routes.json';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
interface CustomTreeItemProps {
|
interface CustomTreeItemProps {
|
||||||
node: I_Installation | I_Folder;
|
node: I_Installation | I_Folder;
|
||||||
parent_id: number;
|
parent_id: number;
|
||||||
children?: ReactNode;
|
children?: ReactNode;
|
||||||
handleSelectedInstallation: () => void;
|
|
||||||
status: number;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const useTreeItemStyles = makeStyles((theme) => ({
|
const useTreeItemStyles = makeStyles((theme) => ({
|
||||||
|
@ -37,6 +38,41 @@ const useTreeItemStyles = makeStyles((theme) => ({
|
||||||
function CustomTreeItem(props: CustomTreeItemProps) {
|
function CustomTreeItem(props: CustomTreeItemProps) {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const classes = useTreeItemStyles();
|
const classes = useTreeItemStyles();
|
||||||
|
const logContext = useContext(LogContext);
|
||||||
|
const { getStatus } = logContext;
|
||||||
|
const status = getStatus(props.node.id);
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const [selected, setSelected] = useState(false);
|
||||||
|
const searchParams = new URLSearchParams(location.search);
|
||||||
|
const installationId = parseInt(searchParams.get('installation'));
|
||||||
|
|
||||||
|
const handleSelectOneInstallation = (): void => {
|
||||||
|
let installation = props.node;
|
||||||
|
if (installation.type != 'Folder') {
|
||||||
|
navigate(
|
||||||
|
routes.installations +
|
||||||
|
routes.tree +
|
||||||
|
'?installation=' +
|
||||||
|
installation.id.toString(),
|
||||||
|
{
|
||||||
|
replace: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
setSelected(!selected);
|
||||||
|
} else {
|
||||||
|
navigate(
|
||||||
|
routes.installations +
|
||||||
|
routes.tree +
|
||||||
|
'?folder=' +
|
||||||
|
installation.id.toString(),
|
||||||
|
{
|
||||||
|
replace: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
setSelected(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const renderIcon = () => {
|
const renderIcon = () => {
|
||||||
if (props.node.type === 'Folder') {
|
if (props.node.type === 'Folder') {
|
||||||
return <FolderIcon />;
|
return <FolderIcon />;
|
||||||
|
@ -45,13 +81,14 @@ function CustomTreeItem(props: CustomTreeItemProps) {
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const itemClasses = [classes.labelRoot];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TreeItem
|
<TreeItem
|
||||||
nodeId={
|
nodeId={props.node.id.toString() + props.node.type}
|
||||||
props.node.id.toString() + props.parent_id.toString() + props.node.type
|
|
||||||
}
|
|
||||||
label={
|
label={
|
||||||
<div className={classes.labelRoot}>
|
<div className={itemClasses.join(' ')}>
|
||||||
<ListItemIcon color="inherit" className={classes.labelIcon}>
|
<ListItemIcon color="inherit" className={classes.labelIcon}>
|
||||||
{renderIcon()}
|
{renderIcon()}
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
|
@ -63,16 +100,17 @@ function CustomTreeItem(props: CustomTreeItemProps) {
|
||||||
>
|
>
|
||||||
{props.node.name}
|
{props.node.name}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
{props.node.type === 'Installation' && (
|
{props.node.type === 'Installation' && (
|
||||||
<div>
|
<div>
|
||||||
{props.status === -1 ? (
|
{status === -1 ? (
|
||||||
<CancelIcon
|
<CancelIcon
|
||||||
style={{
|
style={{
|
||||||
width: '23px',
|
width: '23px',
|
||||||
height: '23px',
|
height: '23px',
|
||||||
color: 'red',
|
color: 'red',
|
||||||
borderRadius: '50%',
|
borderRadius: '50%',
|
||||||
marginRight: '40px',
|
marginLeft: '21px',
|
||||||
marginTop: '30px'
|
marginTop: '30px'
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
@ -80,12 +118,12 @@ function CustomTreeItem(props: CustomTreeItemProps) {
|
||||||
''
|
''
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{props.status === -2 ? (
|
{status === -2 ? (
|
||||||
<CircularProgress
|
<CircularProgress
|
||||||
size={20}
|
size={20}
|
||||||
sx={{
|
sx={{
|
||||||
color: '#f7b34d',
|
color: '#f7b34d',
|
||||||
marginRight: '40px',
|
marginLeft: '20px',
|
||||||
marginTop: '30px'
|
marginTop: '30px'
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
@ -98,13 +136,13 @@ function CustomTreeItem(props: CustomTreeItemProps) {
|
||||||
width: '20px',
|
width: '20px',
|
||||||
height: '20px',
|
height: '20px',
|
||||||
borderRadius: '50%',
|
borderRadius: '50%',
|
||||||
marginRight: '40px',
|
marginLeft: '20px',
|
||||||
backgroundColor:
|
backgroundColor:
|
||||||
props.status === 2
|
status === 2
|
||||||
? 'red'
|
? 'red'
|
||||||
: props.status === 1
|
: status === 1
|
||||||
? 'orange'
|
? 'orange'
|
||||||
: props.status === -1 || props.status === -2
|
: status === -1 || status === -2
|
||||||
? 'transparent'
|
? 'transparent'
|
||||||
: 'green'
|
: 'green'
|
||||||
}}
|
}}
|
||||||
|
@ -114,6 +152,7 @@ function CustomTreeItem(props: CustomTreeItemProps) {
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
sx={{
|
sx={{
|
||||||
|
display: !installationId ? 'block' : 'none',
|
||||||
'.MuiTreeItem-content': {
|
'.MuiTreeItem-content': {
|
||||||
width: 'inherit',
|
width: 'inherit',
|
||||||
|
|
||||||
|
@ -123,9 +162,10 @@ function CustomTreeItem(props: CustomTreeItemProps) {
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
backgroundColor: theme.colors.primary.lighter
|
backgroundColor: theme.colors.primary.lighter
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
backgroundColor: selected ? '#111111' : '#ffffff'
|
||||||
}}
|
}}
|
||||||
onClick={() => props.handleSelectedInstallation()}
|
onClick={() => handleSelectOneInstallation()}
|
||||||
>
|
>
|
||||||
{props.children}
|
{props.children}
|
||||||
</TreeItem>
|
</TreeItem>
|
||||||
|
|
|
@ -24,6 +24,7 @@ import { TabsContainerWrapper } from 'src/layouts/TabsContainerWrapper';
|
||||||
import AccessContextProvider from 'src/contexts/AccessContextProvider';
|
import AccessContextProvider from 'src/contexts/AccessContextProvider';
|
||||||
import { InstallationsContext } from 'src/contexts/InstallationsContextProvider';
|
import { InstallationsContext } from 'src/contexts/InstallationsContextProvider';
|
||||||
import Access from '../ManageAccess/Access';
|
import Access from '../ManageAccess/Access';
|
||||||
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
interface singleFolderProps {
|
interface singleFolderProps {
|
||||||
current_folder: I_Folder;
|
current_folder: I_Folder;
|
||||||
|
@ -43,6 +44,8 @@ function Folder(props: singleFolderProps) {
|
||||||
const [isRowHovered, setHoveredRow] = useState(-1);
|
const [isRowHovered, setHoveredRow] = useState(-1);
|
||||||
const [selectedUser, setSelectedUser] = useState<number>(-1);
|
const [selectedUser, setSelectedUser] = useState<number>(-1);
|
||||||
const selectedBulkActions = selectedUser !== -1;
|
const selectedBulkActions = selectedUser !== -1;
|
||||||
|
const searchParams = new URLSearchParams(location.search);
|
||||||
|
const folderId = parseInt(searchParams.get('folder'));
|
||||||
|
|
||||||
const installationContext = useContext(InstallationsContext);
|
const installationContext = useContext(InstallationsContext);
|
||||||
const {
|
const {
|
||||||
|
@ -65,8 +68,16 @@ function Folder(props: singleFolderProps) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const tabs = [
|
const tabs = [
|
||||||
{ value: 'folder', label: 'Folder' },
|
{
|
||||||
{ value: 'manage', label: 'Manage Access' }
|
value: 'folder',
|
||||||
|
label: <FormattedMessage id="folder" defaultMessage="Folder" />
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'manage',
|
||||||
|
label: (
|
||||||
|
<FormattedMessage id="manageAccess" defaultMessage="Manage Access" />
|
||||||
|
)
|
||||||
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
const handleTabsChange = (_event: ChangeEvent<{}>, value: string): void => {
|
const handleTabsChange = (_event: ChangeEvent<{}>, value: string): void => {
|
||||||
|
@ -140,6 +151,8 @@ function Folder(props: singleFolderProps) {
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (folderId == props.current_folder.id) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{openModalFolder && (
|
{openModalFolder && (
|
||||||
|
@ -200,7 +213,12 @@ function Folder(props: singleFolderProps) {
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<TextField
|
<TextField
|
||||||
label="Name"
|
label={
|
||||||
|
<FormattedMessage
|
||||||
|
id="name"
|
||||||
|
defaultMessage="Name"
|
||||||
|
/>
|
||||||
|
}
|
||||||
name="name"
|
name="name"
|
||||||
value={formValues.name}
|
value={formValues.name}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
|
@ -211,7 +229,12 @@ function Folder(props: singleFolderProps) {
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<TextField
|
<TextField
|
||||||
label="Information"
|
label={
|
||||||
|
<FormattedMessage
|
||||||
|
id="information"
|
||||||
|
defaultMessage="Information"
|
||||||
|
/>
|
||||||
|
}
|
||||||
name="information"
|
name="information"
|
||||||
value={formValues.information}
|
value={formValues.information}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
|
@ -236,7 +259,10 @@ function Folder(props: singleFolderProps) {
|
||||||
}}
|
}}
|
||||||
disabled={!areRequiredFieldsFilled()}
|
disabled={!areRequiredFieldsFilled()}
|
||||||
>
|
>
|
||||||
Apply Changes
|
<FormattedMessage
|
||||||
|
id="applyChanges"
|
||||||
|
defaultMessage="Apply Changes"
|
||||||
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
@ -248,7 +274,10 @@ function Folder(props: singleFolderProps) {
|
||||||
marginLeft: '10px'
|
marginLeft: '10px'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Add new installation
|
<FormattedMessage
|
||||||
|
id="addNewInstallation"
|
||||||
|
defaultMessage="Add new installation"
|
||||||
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
@ -260,7 +289,10 @@ function Folder(props: singleFolderProps) {
|
||||||
marginLeft: '10px'
|
marginLeft: '10px'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Add new folder
|
<FormattedMessage
|
||||||
|
id="addNewFolder"
|
||||||
|
defaultMessage="Add new folder"
|
||||||
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
@ -272,7 +304,10 @@ function Folder(props: singleFolderProps) {
|
||||||
marginLeft: '10px'
|
marginLeft: '10px'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Delete Folder
|
<FormattedMessage
|
||||||
|
id="deleteFolder"
|
||||||
|
defaultMessage="Delete Folder"
|
||||||
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
@ -293,7 +328,10 @@ function Folder(props: singleFolderProps) {
|
||||||
alignItems: 'center'
|
alignItems: 'center'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
An error has occurred
|
<FormattedMessage
|
||||||
|
id="errorOccured"
|
||||||
|
defaultMessage="An error has occurred"
|
||||||
|
/>
|
||||||
<IconButton
|
<IconButton
|
||||||
color="inherit"
|
color="inherit"
|
||||||
size="small"
|
size="small"
|
||||||
|
@ -313,7 +351,10 @@ function Folder(props: singleFolderProps) {
|
||||||
alignItems: 'center'
|
alignItems: 'center'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Successfully updated
|
<FormattedMessage
|
||||||
|
id="successfullyUpdated"
|
||||||
|
defaultMessage="Successfully updated"
|
||||||
|
/>
|
||||||
<IconButton
|
<IconButton
|
||||||
color="inherit"
|
color="inherit"
|
||||||
size="small"
|
size="small"
|
||||||
|
@ -344,6 +385,9 @@ function Folder(props: singleFolderProps) {
|
||||||
</Grid>
|
</Grid>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Folder;
|
export default Folder;
|
||||||
|
|
|
@ -1,86 +1,33 @@
|
||||||
import React, { useContext, useEffect, useState } from 'react';
|
import React, { useContext, useEffect } from 'react';
|
||||||
import { Grid, useTheme } from '@mui/material';
|
import { Grid } from '@mui/material';
|
||||||
import { TreeView } from '@mui/lab';
|
import { TreeView } from '@mui/lab';
|
||||||
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
||||||
import ChevronRightIcon from '@mui/icons-material/ChevronRight';
|
import ChevronRightIcon from '@mui/icons-material/ChevronRight';
|
||||||
import CustomTreeItem from './CustomTreeItem';
|
import CustomTreeItem from './CustomTreeItem';
|
||||||
import Installation from '../Installations/Installation';
|
import Installation from '../Installations/Installation';
|
||||||
import { I_Folder, I_Installation } from 'src/interfaces/InstallationTypes';
|
|
||||||
import Folder from './Folder';
|
import Folder from './Folder';
|
||||||
import { InstallationsContext } from 'src/contexts/InstallationsContextProvider';
|
import { InstallationsContext } from 'src/contexts/InstallationsContextProvider';
|
||||||
import { LogContext } from '../../../contexts/LogContextProvider';
|
import LogContextProvider from 'src/contexts/LogContextProvider';
|
||||||
|
|
||||||
function InstallationTree() {
|
function InstallationTree() {
|
||||||
const theme = useTheme();
|
const { foldersAndInstallations, fetchAllFoldersAndInstallations } =
|
||||||
const { data, fetchAllFoldersAndInstallations } =
|
|
||||||
useContext(InstallationsContext);
|
useContext(InstallationsContext);
|
||||||
const [selectedInstallation, setSelectedInstallation] = useState<number>(-1);
|
|
||||||
const [selectedFolder, setSelectedFolder] = useState<number>(-1);
|
|
||||||
const [installationStatus, setInstallationStatus] = useState<
|
|
||||||
Record<number, number[]>
|
|
||||||
>({});
|
|
||||||
|
|
||||||
const selectedBulkActionsForFolders = selectedFolder === -1 ? false : true;
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchAllFoldersAndInstallations();
|
fetchAllFoldersAndInstallations();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const findInstallation = (id: number) => {
|
|
||||||
return data.find(
|
|
||||||
(installation) => installation.type != 'Folder' && installation.id === id
|
|
||||||
) as I_Installation;
|
|
||||||
};
|
|
||||||
|
|
||||||
const findFolder = (id: number) => {
|
|
||||||
return data.find(
|
|
||||||
(folder) => folder.type == 'Folder' && folder.id === id
|
|
||||||
) as I_Folder;
|
|
||||||
};
|
|
||||||
|
|
||||||
const logContext = useContext(LogContext);
|
|
||||||
const { getStatus } = logContext;
|
|
||||||
|
|
||||||
const handleSelectOneInstallation = (
|
|
||||||
installation: I_Installation | I_Folder
|
|
||||||
): void => {
|
|
||||||
if (installation.type != 'Folder') {
|
|
||||||
if (selectedInstallation != installation.id) {
|
|
||||||
setSelectedInstallation(installation.id);
|
|
||||||
setSelectedFolder(-1);
|
|
||||||
} else {
|
|
||||||
setSelectedInstallation(-1);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (selectedFolder != installation.id) {
|
|
||||||
setSelectedFolder(installation.id);
|
|
||||||
setSelectedInstallation(-1);
|
|
||||||
} else {
|
|
||||||
setSelectedFolder(-1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const TreeNode = ({ node, parent_id }) => {
|
const TreeNode = ({ node, parent_id }) => {
|
||||||
if (node.type == 'Folder') {
|
if (node.type == 'Folder') {
|
||||||
return (
|
return (
|
||||||
node.parentId == parent_id && (
|
node.parentId == parent_id && (
|
||||||
<CustomTreeItem
|
<CustomTreeItem node={node} parent_id={parent_id}>
|
||||||
node={node}
|
{foldersAndInstallations.map((subnode) => {
|
||||||
parent_id={parent_id}
|
|
||||||
handleSelectedInstallation={() => handleSelectOneInstallation(node)}
|
|
||||||
status={0}
|
|
||||||
>
|
|
||||||
{data.map((subnode) => {
|
|
||||||
return (
|
return (
|
||||||
subnode != node &&
|
subnode != node &&
|
||||||
subnode.parentId == node.id && (
|
subnode.parentId == node.id && (
|
||||||
<TreeNode
|
<TreeNode
|
||||||
key={
|
key={subnode.id.toString() + subnode.type}
|
||||||
subnode.id.toString() +
|
|
||||||
parent_id.toString() +
|
|
||||||
subnode.type
|
|
||||||
}
|
|
||||||
node={subnode}
|
node={subnode}
|
||||||
parent_id={node.id}
|
parent_id={node.id}
|
||||||
/>
|
/>
|
||||||
|
@ -91,32 +38,27 @@ function InstallationTree() {
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
const status = getStatus(node.id);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
node.parentId == parent_id && (
|
node.parentId == parent_id && (
|
||||||
<CustomTreeItem
|
<CustomTreeItem node={node} parent_id={parent_id} />
|
||||||
node={node}
|
|
||||||
parent_id={parent_id}
|
|
||||||
handleSelectedInstallation={() => handleSelectOneInstallation(node)}
|
|
||||||
status={status}
|
|
||||||
/>
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<LogContextProvider>
|
||||||
<Grid container spacing={1} sx={{ marginTop: 0.1 }}>
|
<Grid container spacing={1} sx={{ marginTop: 0.1 }}>
|
||||||
<Grid item xs={12} md={3}>
|
<Grid item xs={12} md={3}>
|
||||||
<TreeView
|
<TreeView
|
||||||
defaultCollapseIcon={<ExpandMoreIcon />}
|
defaultCollapseIcon={<ExpandMoreIcon />}
|
||||||
defaultExpandIcon={<ChevronRightIcon />}
|
defaultExpandIcon={<ChevronRightIcon />}
|
||||||
|
defaultExpanded={['1Folder']}
|
||||||
>
|
>
|
||||||
{data.map((node, index) => {
|
{foldersAndInstallations.map((node, index) => {
|
||||||
return (
|
return (
|
||||||
<TreeNode
|
<TreeNode
|
||||||
key={node.id.toString() + node.parentId.toString() + node.type}
|
key={node.id.toString() + node.type}
|
||||||
node={node}
|
node={node}
|
||||||
parent_id={'0'}
|
parent_id={'0'}
|
||||||
/>
|
/>
|
||||||
|
@ -125,26 +67,26 @@ function InstallationTree() {
|
||||||
</TreeView>
|
</TreeView>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
{data.map((installation) => {
|
{foldersAndInstallations.map((installation) => {
|
||||||
if (installation.type == 'Installation') {
|
if (installation.type == 'Installation') {
|
||||||
return (
|
return (
|
||||||
<Installation
|
<Installation
|
||||||
key={installation.id}
|
key={installation.id + installation.type}
|
||||||
current_installation={findInstallation(installation.id)}
|
current_installation={installation}
|
||||||
type="tree"
|
type="tree"
|
||||||
style={{
|
|
||||||
display:
|
|
||||||
installation.id === selectedInstallation ? 'block' : 'none'
|
|
||||||
}}
|
|
||||||
></Installation>
|
></Installation>
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<Folder
|
||||||
|
key={installation.id + installation.type}
|
||||||
|
current_folder={installation}
|
||||||
|
></Folder>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
})}
|
})}
|
||||||
|
|
||||||
{selectedBulkActionsForFolders && (
|
|
||||||
<Folder current_folder={findFolder(selectedFolder)}></Folder>
|
|
||||||
)}
|
|
||||||
</Grid>
|
</Grid>
|
||||||
|
</LogContextProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@ import { Close as CloseIcon } from '@mui/icons-material';
|
||||||
import { I_Folder } from 'src/interfaces/InstallationTypes';
|
import { I_Folder } from 'src/interfaces/InstallationTypes';
|
||||||
import { TokenContext } from 'src/contexts/tokenContext';
|
import { TokenContext } from 'src/contexts/tokenContext';
|
||||||
import { InstallationsContext } from 'src/contexts/InstallationsContextProvider';
|
import { InstallationsContext } from 'src/contexts/InstallationsContextProvider';
|
||||||
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
interface folderFormProps {
|
interface folderFormProps {
|
||||||
cancel: () => void;
|
cancel: () => void;
|
||||||
|
@ -94,7 +95,7 @@ function folderForm(props: folderFormProps) {
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<TextField
|
<TextField
|
||||||
label="Name"
|
label={<FormattedMessage id="name" defaultMessage="Name" />}
|
||||||
name="name"
|
name="name"
|
||||||
value={formValues.name}
|
value={formValues.name}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
|
@ -106,7 +107,12 @@ function folderForm(props: folderFormProps) {
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<TextField
|
<TextField
|
||||||
label="Information"
|
label={
|
||||||
|
<FormattedMessage
|
||||||
|
id="information"
|
||||||
|
defaultMessage="Information"
|
||||||
|
/>
|
||||||
|
}
|
||||||
name="information"
|
name="information"
|
||||||
value={formValues.information}
|
value={formValues.information}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
|
@ -129,7 +135,7 @@ function folderForm(props: folderFormProps) {
|
||||||
}}
|
}}
|
||||||
disabled={!areRequiredFieldsFilled()}
|
disabled={!areRequiredFieldsFilled()}
|
||||||
>
|
>
|
||||||
Submit
|
<FormattedMessage id="submit" defaultMessage="Submit" />
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
|
@ -139,7 +145,7 @@ function folderForm(props: folderFormProps) {
|
||||||
marginLeft: '10px'
|
marginLeft: '10px'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Cancel
|
<FormattedMessage id="cancel" defaultMessage="Cancel" />
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
{loading && (
|
{loading && (
|
||||||
|
@ -160,11 +166,14 @@ function folderForm(props: folderFormProps) {
|
||||||
alignItems: 'center'
|
alignItems: 'center'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
An error has occurred
|
<FormattedMessage
|
||||||
|
id="errorOccured"
|
||||||
|
defaultMessage="An error has occurred"
|
||||||
|
/>
|
||||||
<IconButton
|
<IconButton
|
||||||
color="inherit"
|
color="inherit"
|
||||||
size="small"
|
size="small"
|
||||||
onClick={() => setError(false)} // Set error state to false on click
|
onClick={() => setError(false)}
|
||||||
sx={{ marginLeft: '4px' }}
|
sx={{ marginLeft: '4px' }}
|
||||||
>
|
>
|
||||||
<CloseIcon fontSize="small" />
|
<CloseIcon fontSize="small" />
|
||||||
|
|
|
@ -12,6 +12,7 @@ import { UsersContext } from '../../../contexts/UsersContextProvider';
|
||||||
import Button from '@mui/material/Button';
|
import Button from '@mui/material/Button';
|
||||||
import UserForm from './userForm';
|
import UserForm from './userForm';
|
||||||
import { UserContext } from '../../../contexts/userContext';
|
import { UserContext } from '../../../contexts/userContext';
|
||||||
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
function UsersSearch() {
|
function UsersSearch() {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
@ -55,7 +56,7 @@ function UsersSearch() {
|
||||||
<Grid item xs={12} md={3}>
|
<Grid item xs={12} md={3}>
|
||||||
{currentUser.hasWriteAccess && (
|
{currentUser.hasWriteAccess && (
|
||||||
<Button variant="contained" onClick={handleSubmit}>
|
<Button variant="contained" onClick={handleSubmit}>
|
||||||
Create user
|
<FormattedMessage id="addUser" defaultMessage="Create user" />
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
|
@ -20,6 +20,7 @@ import { TokenContext } from 'src/contexts/tokenContext';
|
||||||
import FormControlLabel from '@mui/material/FormControlLabel';
|
import FormControlLabel from '@mui/material/FormControlLabel';
|
||||||
import Checkbox from '@mui/material/Checkbox';
|
import Checkbox from '@mui/material/Checkbox';
|
||||||
import { I_Folder, I_Installation } from 'src/interfaces/InstallationTypes';
|
import { I_Folder, I_Installation } from 'src/interfaces/InstallationTypes';
|
||||||
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
interface userFormProps {
|
interface userFormProps {
|
||||||
cancel: () => void;
|
cancel: () => void;
|
||||||
|
@ -208,7 +209,7 @@ function userForm(props: userFormProps) {
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<TextField
|
<TextField
|
||||||
label="Name"
|
label={<FormattedMessage id="name" defaultMessage="Name" />}
|
||||||
name="name"
|
name="name"
|
||||||
value={formValues.name}
|
value={formValues.name}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
|
@ -219,7 +220,7 @@ function userForm(props: userFormProps) {
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<TextField
|
<TextField
|
||||||
label="Email"
|
label={<FormattedMessage id="email" defaultMessage="Email" />}
|
||||||
name="email"
|
name="email"
|
||||||
value={formValues.email}
|
value={formValues.email}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
|
@ -230,7 +231,12 @@ function userForm(props: userFormProps) {
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<TextField
|
<TextField
|
||||||
label="Information"
|
label={
|
||||||
|
<FormattedMessage
|
||||||
|
id="information"
|
||||||
|
defaultMessage="Information"
|
||||||
|
/>
|
||||||
|
}
|
||||||
name="information"
|
name="information"
|
||||||
value={formValues.information}
|
value={formValues.information}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
|
@ -249,7 +255,10 @@ function userForm(props: userFormProps) {
|
||||||
backgroundColor: 'transparent'
|
backgroundColor: 'transparent'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Grant access to folders
|
<FormattedMessage
|
||||||
|
id="grantAccessToFolders"
|
||||||
|
defaultMessage="Grant access to folders"
|
||||||
|
/>
|
||||||
</InputLabel>
|
</InputLabel>
|
||||||
<Select
|
<Select
|
||||||
multiple
|
multiple
|
||||||
|
@ -284,7 +293,7 @@ function userForm(props: userFormProps) {
|
||||||
}}
|
}}
|
||||||
onClick={handleCloseFolder}
|
onClick={handleCloseFolder}
|
||||||
>
|
>
|
||||||
Submit
|
<FormattedMessage id="submit" defaultMessage="Submit" />
|
||||||
</Button>
|
</Button>
|
||||||
</Select>
|
</Select>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
@ -304,7 +313,10 @@ function userForm(props: userFormProps) {
|
||||||
fontSize: 14
|
fontSize: 14
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Grant access to installations
|
<FormattedMessage
|
||||||
|
id="grantAccessToInstallations"
|
||||||
|
defaultMessage="Grant access to installations"
|
||||||
|
/>
|
||||||
</InputLabel>
|
</InputLabel>
|
||||||
<Select
|
<Select
|
||||||
multiple
|
multiple
|
||||||
|
@ -339,7 +351,7 @@ function userForm(props: userFormProps) {
|
||||||
}}
|
}}
|
||||||
onClick={handleCloseInstallation}
|
onClick={handleCloseInstallation}
|
||||||
>
|
>
|
||||||
Submit
|
<FormattedMessage id="submit" defaultMessage="Submit" />
|
||||||
</Button>
|
</Button>
|
||||||
</Select>
|
</Select>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
@ -375,7 +387,7 @@ function userForm(props: userFormProps) {
|
||||||
}}
|
}}
|
||||||
disabled={!areRequiredFieldsFilled()}
|
disabled={!areRequiredFieldsFilled()}
|
||||||
>
|
>
|
||||||
Submit
|
<FormattedMessage id="submit" defaultMessage="Submit" />
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
|
@ -385,7 +397,7 @@ function userForm(props: userFormProps) {
|
||||||
marginLeft: '10px'
|
marginLeft: '10px'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Cancel
|
<FormattedMessage id="cancel" defaultMessage="Cancel" />
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
{loading && (
|
{loading && (
|
||||||
|
@ -410,7 +422,7 @@ function userForm(props: userFormProps) {
|
||||||
<IconButton
|
<IconButton
|
||||||
color="inherit"
|
color="inherit"
|
||||||
size="small"
|
size="small"
|
||||||
onClick={() => setError(false)} // Set error state to false on click
|
onClick={() => setError(false)}
|
||||||
sx={{ marginLeft: '4px' }}
|
sx={{ marginLeft: '4px' }}
|
||||||
>
|
>
|
||||||
<CloseIcon fontSize="small" />
|
<CloseIcon fontSize="small" />
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import {
|
import React, {
|
||||||
createContext,
|
createContext,
|
||||||
ReactNode,
|
ReactNode,
|
||||||
useCallback,
|
useCallback,
|
||||||
|
@ -11,6 +11,7 @@ import {
|
||||||
I_UserWithInheritedAccess,
|
I_UserWithInheritedAccess,
|
||||||
InnovEnergyUser
|
InnovEnergyUser
|
||||||
} from '../interfaces/UserTypes';
|
} from '../interfaces/UserTypes';
|
||||||
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
interface AccessContextProviderProps {
|
interface AccessContextProviderProps {
|
||||||
usersWithDirectAccess: InnovEnergyUser[];
|
usersWithDirectAccess: InnovEnergyUser[];
|
||||||
|
@ -82,7 +83,15 @@ const AccessContextProvider = ({ children }: { children: ReactNode }) => {
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
setError(true);
|
setError(true);
|
||||||
setErrorMessage('Unable to load data');
|
|
||||||
|
const message = (
|
||||||
|
<FormattedMessage
|
||||||
|
id="unableToLoadData"
|
||||||
|
defaultMessage="Unable to load data"
|
||||||
|
/>
|
||||||
|
).props.defaultMessage;
|
||||||
|
|
||||||
|
setErrorMessage(message);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[]
|
[]
|
||||||
|
@ -99,7 +108,13 @@ const AccessContextProvider = ({ children }: { children: ReactNode }) => {
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
setError(true);
|
setError(true);
|
||||||
setErrorMessage('Unable to load data');
|
const message = (
|
||||||
|
<FormattedMessage
|
||||||
|
id="unableToLoadData"
|
||||||
|
defaultMessage="Unable to load data"
|
||||||
|
/>
|
||||||
|
).props.defaultMessage;
|
||||||
|
setErrorMessage(message);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[]
|
[]
|
||||||
|
@ -127,7 +142,19 @@ const AccessContextProvider = ({ children }: { children: ReactNode }) => {
|
||||||
resourceType,
|
resourceType,
|
||||||
current_ResourceId
|
current_ResourceId
|
||||||
);
|
);
|
||||||
setUpdatedMessage('Revoked access from user: ' + name);
|
|
||||||
|
const message =
|
||||||
|
(
|
||||||
|
<FormattedMessage
|
||||||
|
id="revokedAccessFromUser"
|
||||||
|
defaultMessage="Revoked access from user: "
|
||||||
|
/>
|
||||||
|
).props.defaultMessage +
|
||||||
|
' ' +
|
||||||
|
name;
|
||||||
|
|
||||||
|
setUpdatedMessage(message);
|
||||||
|
|
||||||
setUpdated(true);
|
setUpdated(true);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
setUpdated(false);
|
setUpdated(false);
|
||||||
|
@ -136,7 +163,14 @@ const AccessContextProvider = ({ children }: { children: ReactNode }) => {
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
setError(true);
|
setError(true);
|
||||||
setErrorMessage('Unable to revoke access');
|
const message = (
|
||||||
|
<FormattedMessage
|
||||||
|
id="unableToRevokeAccess"
|
||||||
|
defaultMessage="Unable to revoke access"
|
||||||
|
/>
|
||||||
|
).props.defaultMessage;
|
||||||
|
|
||||||
|
setErrorMessage(message);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[]
|
[]
|
||||||
|
|
|
@ -11,7 +11,8 @@ import { I_Folder, I_Installation } from 'src/interfaces/InstallationTypes';
|
||||||
import { TokenContext } from './tokenContext';
|
import { TokenContext } from './tokenContext';
|
||||||
|
|
||||||
interface I_InstallationContextProviderProps {
|
interface I_InstallationContextProviderProps {
|
||||||
data: I_Installation[];
|
installations: I_Installation[];
|
||||||
|
foldersAndInstallations: I_Installation[];
|
||||||
fetchAllInstallations: () => Promise<void>;
|
fetchAllInstallations: () => Promise<void>;
|
||||||
fetchAllFoldersAndInstallations: () => Promise<void>;
|
fetchAllFoldersAndInstallations: () => Promise<void>;
|
||||||
createInstallation: (value: Partial<I_Installation>) => Promise<void>;
|
createInstallation: (value: Partial<I_Installation>) => Promise<void>;
|
||||||
|
@ -24,14 +25,14 @@ interface I_InstallationContextProviderProps {
|
||||||
setUpdated: (value: boolean) => void;
|
setUpdated: (value: boolean) => void;
|
||||||
deleteInstallation: (value: I_Installation, view: string) => Promise<void>;
|
deleteInstallation: (value: I_Installation, view: string) => Promise<void>;
|
||||||
createFolder: (value: Partial<I_Folder>) => Promise<void>;
|
createFolder: (value: Partial<I_Folder>) => Promise<void>;
|
||||||
|
|
||||||
updateFolder: (value: I_Folder) => Promise<void>;
|
updateFolder: (value: I_Folder) => Promise<void>;
|
||||||
deleteFolder: (value: I_Folder) => Promise<void>;
|
deleteFolder: (value: I_Folder) => Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const InstallationsContext =
|
export const InstallationsContext =
|
||||||
createContext<I_InstallationContextProviderProps>({
|
createContext<I_InstallationContextProviderProps>({
|
||||||
data: [],
|
installations: [],
|
||||||
|
foldersAndInstallations: [],
|
||||||
fetchAllInstallations: () => Promise.resolve(),
|
fetchAllInstallations: () => Promise.resolve(),
|
||||||
fetchAllFoldersAndInstallations: () => Promise.resolve(),
|
fetchAllFoldersAndInstallations: () => Promise.resolve(),
|
||||||
createInstallation: () => Promise.resolve(),
|
createInstallation: () => Promise.resolve(),
|
||||||
|
@ -44,7 +45,6 @@ export const InstallationsContext =
|
||||||
setUpdated: () => {},
|
setUpdated: () => {},
|
||||||
deleteInstallation: () => Promise.resolve(),
|
deleteInstallation: () => Promise.resolve(),
|
||||||
createFolder: () => Promise.resolve(),
|
createFolder: () => Promise.resolve(),
|
||||||
|
|
||||||
updateFolder: () => Promise.resolve(),
|
updateFolder: () => Promise.resolve(),
|
||||||
deleteFolder: () => Promise.resolve()
|
deleteFolder: () => Promise.resolve()
|
||||||
});
|
});
|
||||||
|
@ -54,7 +54,8 @@ const InstallationsContextProvider = ({
|
||||||
}: {
|
}: {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
}) => {
|
}) => {
|
||||||
const [data, setData] = useState([]);
|
const [installations, setInstallations] = useState([]);
|
||||||
|
const [foldersAndInstallations, setFoldersAndInstallations] = useState([]);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [error, setError] = useState(false);
|
const [error, setError] = useState(false);
|
||||||
const [updated, setUpdated] = useState(false);
|
const [updated, setUpdated] = useState(false);
|
||||||
|
@ -67,7 +68,7 @@ const InstallationsContextProvider = ({
|
||||||
axiosConfig
|
axiosConfig
|
||||||
.get('/GetAllInstallations', {})
|
.get('/GetAllInstallations', {})
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
setData(res.data);
|
setInstallations(res.data);
|
||||||
})
|
})
|
||||||
.catch((err: AxiosError) => {
|
.catch((err: AxiosError) => {
|
||||||
if (err.response && err.response.status == 401) {
|
if (err.response && err.response.status == 401) {
|
||||||
|
@ -80,14 +81,14 @@ const InstallationsContextProvider = ({
|
||||||
return axiosConfig
|
return axiosConfig
|
||||||
.get('/GetAllFoldersAndInstallations')
|
.get('/GetAllFoldersAndInstallations')
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
setData(res.data);
|
setFoldersAndInstallations(res.data);
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
if (err.response && err.response.status == 401) {
|
if (err.response && err.response.status == 401) {
|
||||||
removeToken();
|
removeToken();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, [setData]);
|
}, []);
|
||||||
|
|
||||||
const createInstallation = useCallback(
|
const createInstallation = useCallback(
|
||||||
async (formValues: Partial<I_Installation>) => {
|
async (formValues: Partial<I_Installation>) => {
|
||||||
|
@ -235,7 +236,8 @@ const InstallationsContextProvider = ({
|
||||||
return (
|
return (
|
||||||
<InstallationsContext.Provider
|
<InstallationsContext.Provider
|
||||||
value={{
|
value={{
|
||||||
data,
|
installations,
|
||||||
|
foldersAndInstallations,
|
||||||
fetchAllInstallations,
|
fetchAllInstallations,
|
||||||
fetchAllFoldersAndInstallations,
|
fetchAllFoldersAndInstallations,
|
||||||
createInstallation,
|
createInstallation,
|
||||||
|
|
|
@ -10,12 +10,13 @@ export const LogContext = createContext<LogContextProviderProps | undefined>(
|
||||||
undefined
|
undefined
|
||||||
);
|
);
|
||||||
|
|
||||||
// Create a UserContextProvider component
|
|
||||||
export const LogContextProvider = ({ children }: { children: ReactNode }) => {
|
export const LogContextProvider = ({ children }: { children: ReactNode }) => {
|
||||||
const [installationStatus, setInstallationStatus] = useState<
|
const [installationStatus, setInstallationStatus] = useState<
|
||||||
Record<number, number[]>
|
Record<number, number[]>
|
||||||
>({});
|
>({});
|
||||||
|
|
||||||
|
const BUFFER_LENGTH = 5;
|
||||||
|
|
||||||
const handleLogWarningOrError = (installation_id: number, value: number) => {
|
const handleLogWarningOrError = (installation_id: number, value: number) => {
|
||||||
setInstallationStatus((prevStatus) => {
|
setInstallationStatus((prevStatus) => {
|
||||||
const newStatus = { ...prevStatus };
|
const newStatus = { ...prevStatus };
|
||||||
|
@ -24,16 +25,23 @@ export const LogContextProvider = ({ children }: { children: ReactNode }) => {
|
||||||
newStatus[installation_id] = [];
|
newStatus[installation_id] = [];
|
||||||
}
|
}
|
||||||
newStatus[installation_id].unshift(value);
|
newStatus[installation_id].unshift(value);
|
||||||
newStatus[installation_id] = newStatus[installation_id].slice(0, 5);
|
newStatus[installation_id] = newStatus[installation_id].slice(
|
||||||
|
0,
|
||||||
|
BUFFER_LENGTH
|
||||||
|
);
|
||||||
return newStatus;
|
return newStatus;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const getStatus = (installationId: number) => {
|
const getStatus = (installationId: number) => {
|
||||||
let status;
|
let status;
|
||||||
|
|
||||||
if (!installationStatus.hasOwnProperty(installationId)) {
|
if (!installationStatus.hasOwnProperty(installationId)) {
|
||||||
status = -2;
|
status = -2;
|
||||||
} else {
|
} else {
|
||||||
|
if (installationId === 2) {
|
||||||
|
console.log(installationStatus[2]);
|
||||||
|
}
|
||||||
if (installationStatus[installationId][0] == -1) {
|
if (installationStatus[installationId][0] == -1) {
|
||||||
let i = 0;
|
let i = 0;
|
||||||
for (i; i < installationStatus[installationId].length; i++) {
|
for (i; i < installationStatus[installationId].length; i++) {
|
||||||
|
|
|
@ -8,6 +8,7 @@ import {RecordSeries} from './data';
|
||||||
import {isUndefined, Maybe} from './utils/maybe';
|
import {isUndefined, Maybe} from './utils/maybe';
|
||||||
import {isNumber} from './utils/runtimeTypeChecking';
|
import {isNumber} from './utils/runtimeTypeChecking';
|
||||||
import {I_CsvEntry} from 'src/content/dashboards/Log/graph.util';
|
import {I_CsvEntry} from 'src/content/dashboards/Log/graph.util';
|
||||||
|
import {I_S3Credentials} from '../interfaces/S3Types';
|
||||||
|
|
||||||
export const FetchResult = {
|
export const FetchResult = {
|
||||||
notAvailable: 'N/A',
|
notAvailable: 'N/A',
|
||||||
|
@ -33,7 +34,10 @@ function reverseBits(x: number): number {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class DataCache<T extends Record<string, I_CsvEntry>> {
|
export default class DataCache<T extends Record<string, I_CsvEntry>> {
|
||||||
readonly _fetch: (t: UnixTime) => Promise<FetchResult<T>>;
|
readonly _fetch: (
|
||||||
|
t: UnixTime,
|
||||||
|
s3Credentials: I_S3Credentials
|
||||||
|
) => Promise<FetchResult<T>>;
|
||||||
|
|
||||||
public readonly gotData: Observable<UnixTime>;
|
public readonly gotData: Observable<UnixTime>;
|
||||||
|
|
||||||
|
@ -41,16 +45,23 @@ export default class DataCache<T extends Record<string, I_CsvEntry>> {
|
||||||
|
|
||||||
private readonly resolution: TimeSpan;
|
private readonly resolution: TimeSpan;
|
||||||
|
|
||||||
|
private readonly s3Credentials: I_S3Credentials;
|
||||||
|
|
||||||
private readonly fetchQueue = createDispatchQueue(6);
|
private readonly fetchQueue = createDispatchQueue(6);
|
||||||
|
|
||||||
private readonly fetching: Set<number> = new Set<number>();
|
private readonly fetching: Set<number> = new Set<number>();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
fetch: (t: UnixTime) => Promise<FetchResult<T>>,
|
fetch: (
|
||||||
resolution: TimeSpan
|
t: UnixTime,
|
||||||
|
s3Credentials: I_S3Credentials
|
||||||
|
) => Promise<FetchResult<T>>,
|
||||||
|
resolution: TimeSpan,
|
||||||
|
s3Credentials: I_S3Credentials
|
||||||
) {
|
) {
|
||||||
this._fetch = fetch;
|
this._fetch = fetch;
|
||||||
this.resolution = resolution;
|
this.resolution = resolution;
|
||||||
|
this.s3Credentials = s3Credentials;
|
||||||
this.gotData = new Subject<UnixTime>();
|
this.gotData = new Subject<UnixTime>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,7 +82,7 @@ export default class DataCache<T extends Record<string, I_CsvEntry>> {
|
||||||
const t = time.ticks;
|
const t = time.ticks;
|
||||||
|
|
||||||
const node = this.cache.find(t);
|
const node = this.cache.find(t);
|
||||||
if (node.index !== t) this.fetchData(time);
|
if (node.index !== t) this.fetchData(time, this.s3Credentials);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,7 +93,7 @@ export default class DataCache<T extends Record<string, I_CsvEntry>> {
|
||||||
const node = this.cache.find(t);
|
const node = this.cache.find(t);
|
||||||
if (node.index === t) return node.value;
|
if (node.index === t) return node.value;
|
||||||
|
|
||||||
if (fetch) this.fetchData(time);
|
if (fetch) this.fetchData(time, this.s3Credentials);
|
||||||
|
|
||||||
return this.interpolate(node, t);
|
return this.interpolate(node, t);
|
||||||
}
|
}
|
||||||
|
@ -130,7 +141,7 @@ export default class DataCache<T extends Record<string, I_CsvEntry>> {
|
||||||
return interpolated as T;
|
return interpolated as T;
|
||||||
}
|
}
|
||||||
|
|
||||||
private fetchData(time: UnixTime) {
|
private fetchData(time: UnixTime, s3Credentials: I_S3Credentials) {
|
||||||
const t = time.ticks;
|
const t = time.ticks;
|
||||||
|
|
||||||
if (this.fetching.has(t))
|
if (this.fetching.has(t))
|
||||||
|
@ -160,7 +171,7 @@ export default class DataCache<T extends Record<string, I_CsvEntry>> {
|
||||||
(this.gotData as Subject<UnixTime>).next(time);
|
(this.gotData as Subject<UnixTime>).next(time);
|
||||||
};
|
};
|
||||||
|
|
||||||
return this._fetch(time)
|
return this._fetch(time, s3Credentials)
|
||||||
.then(
|
.then(
|
||||||
(d) => onSuccess(d),
|
(d) => onSuccess(d),
|
||||||
(f) => onFailure(f)
|
(f) => onFailure(f)
|
||||||
|
|
|
@ -7,6 +7,8 @@ export interface I_S3Credentials {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Notification {
|
export interface Notification {
|
||||||
key: string;
|
device: string;
|
||||||
value: string;
|
description: string;
|
||||||
|
date: string;
|
||||||
|
time: string;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
"german": "Deutsch",
|
"german": "Deutsch",
|
||||||
"groupTabs": "Gruppen",
|
"groupTabs": "Gruppen",
|
||||||
"groupTree": "Gruppenbaum",
|
"groupTree": "Gruppenbaum",
|
||||||
"information": "Information",
|
|
||||||
"inheritedAccess": "Vererbter Zugriff von",
|
"inheritedAccess": "Vererbter Zugriff von",
|
||||||
"installation": "Installation",
|
"installation": "Installation",
|
||||||
"installationTabs": "Installationen",
|
"installationTabs": "Installationen",
|
||||||
|
@ -48,5 +47,24 @@
|
||||||
"live": "Live Übertragung",
|
"live": "Live Übertragung",
|
||||||
"deleteInstallation": "Installation löschen",
|
"deleteInstallation": "Installation löschen",
|
||||||
"errorOccured": "Ein Fehler ist aufgetreten",
|
"errorOccured": "Ein Fehler ist aufgetreten",
|
||||||
"successfullyUpdated": "Erfolgreich aktualisiert"
|
"successfullyUpdated": "Erfolgreich aktualisiert",
|
||||||
|
"grantAccess": "Zugriff gewähren",
|
||||||
|
"UserswithDirectAccess": "Benutzer mit direktem Zugriff",
|
||||||
|
"UserswithInheritedAccess": "Benutzer mit geerbtem Zugriff",
|
||||||
|
"noerrors": "Es liegen keine Fehler vor",
|
||||||
|
"nowarnings": "Es gibt keine Warnungen",
|
||||||
|
"noUsersWithDirectAccessToThis": "Keine Benutzer mit direktem Zugriff darauf ",
|
||||||
|
"selectUsers": "Wählen Sie Benutzer aus",
|
||||||
|
"cancel": "Stornieren",
|
||||||
|
"addNewFolder": "Neuen Ordner hinzufügen",
|
||||||
|
"addNewInstallation": "Neue Installation hinzufügen",
|
||||||
|
"deleteFolder": "Lösche Ordner",
|
||||||
|
"grantAccessToFolders": "Gewähren Sie Zugriff auf Ordner",
|
||||||
|
"grantAccessToInstallations": "Gewähren Sie Zugriff auf Installationen",
|
||||||
|
"cannotloadloggingdata": "Protokollierungsdaten können nicht geladen werden",
|
||||||
|
"grantedAccessToUsers": "Benutzern Zugriff gewährt: ",
|
||||||
|
"unableToGrantAccess": "Der Zugriff auf kann nicht gewährt werden: ",
|
||||||
|
"unableToLoadData": "Daten können nicht geladen werden",
|
||||||
|
"unableToRevokeAccess": "Der Zugriff kann nicht widerrufen werden",
|
||||||
|
"revokedAccessFromUser": "Zugriff vom Benutzer widerrufen: "
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
{
|
{
|
||||||
"liveView": "Live view",
|
|
||||||
"allInstallations": "All installations",
|
"allInstallations": "All installations",
|
||||||
"applyChanges": "Apply changes",
|
"applyChanges": "Apply changes",
|
||||||
"deleteInstallation": "Delete Installation",
|
|
||||||
"country": "Country",
|
"country": "Country",
|
||||||
"customerName": "Customer name",
|
"customerName": "Customer name",
|
||||||
"english": "English",
|
"english": "English",
|
||||||
|
@ -53,5 +51,24 @@
|
||||||
"live": "Live View",
|
"live": "Live View",
|
||||||
"deleteInstallation": "Delete Installation",
|
"deleteInstallation": "Delete Installation",
|
||||||
"errorOccured": "An error has occurred",
|
"errorOccured": "An error has occurred",
|
||||||
"successfullyUpdated": "Successfully updated"
|
"successfullyUpdated": "Successfully updated",
|
||||||
|
"grantAccess": "Grant Access",
|
||||||
|
"UserswithDirectAccess": "Users with Direct Access",
|
||||||
|
"UserswithInheritedAccess": "Users with Inherited Access",
|
||||||
|
"noerrors": "There are no errors",
|
||||||
|
"nowarnings": "There are no warnings",
|
||||||
|
"noUsersWithDirectAccessToThis": "No users with direct access to this ",
|
||||||
|
"selectUsers": "Select Users",
|
||||||
|
"cancel": "Cancel",
|
||||||
|
"addNewFolder": "Add new Folder",
|
||||||
|
"addNewInstallation": "Add new Installation",
|
||||||
|
"deleteFolder": "Delete Folder",
|
||||||
|
"grantAccessToFolders": "Grant Access to Folders",
|
||||||
|
"grantAccessToInstallations": "Grant Access to Installations",
|
||||||
|
"cannotloadloggingdata": "Cannot load logging data",
|
||||||
|
"grantedAccessToUsers": "Granted access to users: ",
|
||||||
|
"unableToGrantAccess": "Unable to grant access to: ",
|
||||||
|
"unableToLoadData": "Unable to load data",
|
||||||
|
"unableToRevokeAccess": "Unable to revoke access",
|
||||||
|
"revokedAccessFromUser": "Revoked access from user: "
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
"german": "Allemand",
|
"german": "Allemand",
|
||||||
"groupTabs": "Onglets de groupe",
|
"groupTabs": "Onglets de groupe",
|
||||||
"groupTree": "Arbre de groupe",
|
"groupTree": "Arbre de groupe",
|
||||||
"information": "Informations",
|
|
||||||
"inheritedAccess": "Accès hérité de",
|
"inheritedAccess": "Accès hérité de",
|
||||||
"installation": "Installation",
|
"installation": "Installation",
|
||||||
"installationTabs": "Onglets d'installation",
|
"installationTabs": "Onglets d'installation",
|
||||||
|
@ -48,5 +47,24 @@
|
||||||
"live": "Diffusion en direct",
|
"live": "Diffusion en direct",
|
||||||
"deleteInstallation": "Supprimer l'installation",
|
"deleteInstallation": "Supprimer l'installation",
|
||||||
"errorOccured": "Une erreur est survenue",
|
"errorOccured": "Une erreur est survenue",
|
||||||
"successfullyUpdated": "Mise à jour réussie"
|
"successfullyUpdated": "Mise à jour réussie",
|
||||||
|
"grantAccess": "Accorder l'accès",
|
||||||
|
"UserswithDirectAccess": "Utilisateurs avec accès direct",
|
||||||
|
"UserswithInheritedAccess": "Utilisateurs avec accès hérité",
|
||||||
|
"noerrors": "Il n'y a pas d'erreurs",
|
||||||
|
"nowarnings": "Il n'y a aucun avertissement",
|
||||||
|
"noUsersWithDirectAccessToThis": "Aucun utilisateur ayant un accès direct à ceci ",
|
||||||
|
"selectUsers": "Sélectionnez les utilisateurs",
|
||||||
|
"cancel": "Annuler",
|
||||||
|
"addNewFolder": "Ajouter un nouveau dossier",
|
||||||
|
"addNewInstallation": "Ajouter une nouvelle installation",
|
||||||
|
"deleteFolder": "Supprimer le dossier",
|
||||||
|
"grantAccessToFolders": "Accorder l'accès aux dossiers",
|
||||||
|
"grantAccessToInstallations": "Accorder l'accès aux installations",
|
||||||
|
"cannotloadloggingdata": "Impossible de charger les données de journalisation",
|
||||||
|
"grantedAccessToUsers": "Accès accordé aux utilisateurs: ",
|
||||||
|
"unableToGrantAccess": "Impossible d'accorder l'accès à: ",
|
||||||
|
"unableToLoadData": "Impossible de charger les données",
|
||||||
|
"unableToRevokeAccess": "Impossible de révoquer l'accès",
|
||||||
|
"revokedAccessFromUser": "Accès révoqué de l'utilisateur: "
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,8 +4,7 @@ import {
|
||||||
ListItem,
|
ListItem,
|
||||||
ListItemText,
|
ListItemText,
|
||||||
Menu,
|
Menu,
|
||||||
MenuItem,
|
MenuItem
|
||||||
Switch
|
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import { useContext, useRef, useState } from 'react';
|
import { useContext, useRef, useState } from 'react';
|
||||||
import { styled } from '@mui/material/styles';
|
import { styled } from '@mui/material/styles';
|
||||||
|
@ -98,6 +97,11 @@ function HeaderMenu(props: HeaderButtonsProps) {
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleLanguageSelect = (language) => {
|
||||||
|
props.onSelectLanguage(language);
|
||||||
|
handleClose();
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ListWrapper
|
<ListWrapper
|
||||||
|
@ -129,15 +133,14 @@ function HeaderMenu(props: HeaderButtonsProps) {
|
||||||
</ListItem>
|
</ListItem>
|
||||||
</List>
|
</List>
|
||||||
</ListWrapper>
|
</ListWrapper>
|
||||||
<Switch checked={darkState} onChange={handleThemeChange} />
|
|
||||||
<Menu anchorEl={ref.current} onClose={handleClose} open={isOpen}>
|
<Menu anchorEl={ref.current} onClose={handleClose} open={isOpen}>
|
||||||
<MenuItem value="en" onClick={() => props.onSelectLanguage('en')}>
|
<MenuItem value="en" onClick={() => handleLanguageSelect('en')}>
|
||||||
English
|
English
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem value="de" onClick={() => props.onSelectLanguage('de')}>
|
<MenuItem value="de" onClick={() => handleLanguageSelect('de')}>
|
||||||
German
|
German
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem value="fr" onClick={() => props.onSelectLanguage('fr')}>
|
<MenuItem value="fr" onClick={() => handleLanguageSelect('fr')}>
|
||||||
French
|
French
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
</Menu>
|
</Menu>
|
||||||
|
|
|
@ -18,7 +18,7 @@ import SidebarMenu from './SidebarMenu';
|
||||||
const SidebarWrapper = styled(Box)(
|
const SidebarWrapper = styled(Box)(
|
||||||
({ theme }) => `
|
({ theme }) => `
|
||||||
width: ${theme.sidebar.width};
|
width: ${theme.sidebar.width};
|
||||||
min-width: ${theme.sidebar.width};
|
min-width: "${theme.sidebar.width}";
|
||||||
color: ${theme.colors.alpha.trueWhite[70]};
|
color: ${theme.colors.alpha.trueWhite[70]};
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 7;
|
z-index: 7;
|
||||||
|
@ -64,8 +64,7 @@ function Sidebar() {
|
||||||
alt="innovenergy logo"
|
alt="innovenergy logo"
|
||||||
style={{
|
style={{
|
||||||
maxWidth: '150px', // Maximum width for the image
|
maxWidth: '150px', // Maximum width for the image
|
||||||
maxHeight: '150px', // Maximum height for the image
|
maxHeight: '150px' // Maximum height for the image
|
||||||
marginLeft: '50px' // Adjust the value as needed
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { Box, styled } from '@mui/material';
|
import { Box, Card, styled } from '@mui/material';
|
||||||
|
import Avatar from '@mui/material/Avatar';
|
||||||
|
|
||||||
export const TabsContainerWrapper = styled(Box)(
|
export const TabsContainerWrapper = styled(Box)(
|
||||||
({ theme }) => `
|
({ theme }) => `
|
||||||
|
@ -81,3 +82,61 @@ export const TabsContainerWrapper = styled(Box)(
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const AvatarWrapper = styled(Avatar)(
|
||||||
|
({ theme }) => `
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 60px;
|
||||||
|
margin-top: -5px;
|
||||||
|
height: ${theme.spacing(3.5)};
|
||||||
|
width: ${theme.spacing(3.5)};
|
||||||
|
background: ${
|
||||||
|
theme.palette.mode === 'dark'
|
||||||
|
? theme.colors.alpha.trueWhite[30]
|
||||||
|
: '#ffffff'
|
||||||
|
};
|
||||||
|
|
||||||
|
img {
|
||||||
|
background: ${theme.colors.alpha.trueWhite[100]};
|
||||||
|
display: block;
|
||||||
|
border-radius: inherit;
|
||||||
|
height: ${theme.spacing(4.5)};
|
||||||
|
width: ${theme.spacing(4.5)};
|
||||||
|
}
|
||||||
|
`
|
||||||
|
);
|
||||||
|
|
||||||
|
export const AvatarAddWrapper = styled(Avatar)(
|
||||||
|
({ theme }) => `
|
||||||
|
background: ${theme.colors.alpha.black[10]};
|
||||||
|
color: ${theme.colors.primary.main};
|
||||||
|
width: ${theme.spacing(8)};
|
||||||
|
height: ${theme.spacing(8)};
|
||||||
|
`
|
||||||
|
);
|
||||||
|
|
||||||
|
export const CardAddAction = styled(Card)(
|
||||||
|
({ theme }) => `
|
||||||
|
border: ${theme.colors.primary.main} dashed 1px;
|
||||||
|
height: 100%;
|
||||||
|
color: ${theme.colors.primary.main};
|
||||||
|
transition: ${theme.transitions.create(['all'])};
|
||||||
|
|
||||||
|
.MuiCardActionArea-root {
|
||||||
|
height: 100%;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.MuiTouchRipple-root {
|
||||||
|
opacity: .2;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: ${theme.colors.alpha.black[70]};
|
||||||
|
}
|
||||||
|
`
|
||||||
|
);
|
||||||
|
|
|
@ -240,7 +240,7 @@ export const PureLightTheme = createTheme({
|
||||||
menuItemHeadingColor: colors.layout.sidebar.menuItemHeadingColor,
|
menuItemHeadingColor: colors.layout.sidebar.menuItemHeadingColor,
|
||||||
boxShadow:
|
boxShadow:
|
||||||
'2px 0 3px rgba(159, 162, 191, .18), 1px 0 1px rgba(159, 162, 191, 0.32)',
|
'2px 0 3px rgba(159, 162, 191, .18), 1px 0 1px rgba(159, 162, 191, 0.32)',
|
||||||
width: '290px'
|
width: '200px'
|
||||||
},
|
},
|
||||||
header: {
|
header: {
|
||||||
height: '80px',
|
height: '80px',
|
||||||
|
|
Loading…
Reference in New Issue