add frontend and backend for "Test Mode" toggle in Salimax

this toggle is designed to mark any hardware and software tests on the installation and "lock" the installation from others
this toggle is under "Add New Action" in "History of Action" tab
turn on the toggle when starting test and add test content
turn off the toggle when finishing test
This commit is contained in:
Yinyin Liu 2024-07-16 11:53:37 +02:00
parent 17fdcbd529
commit 6b201572cf
5 changed files with 99 additions and 7 deletions

View File

@ -11,6 +11,7 @@ import fr from './lang/fr.json';
import SuspenseLoader from './components/SuspenseLoader'; import SuspenseLoader from './components/SuspenseLoader';
import SidebarLayout from './layouts/SidebarLayout'; import SidebarLayout from './layouts/SidebarLayout';
import { TokenContext } from './contexts/tokenContext'; import { TokenContext } from './contexts/tokenContext';
import { TestModeProvider } from './contexts/TestModeContext';
import ResetPassword from './components/ResetPassword'; import ResetPassword from './components/ResetPassword';
import InstallationTabs from './content/dashboards/Installations/index'; import InstallationTabs from './content/dashboards/Installations/index';
import routes from 'src/Resources/routes.json'; import routes from 'src/Resources/routes.json';
@ -142,7 +143,9 @@ function App() {
element={ element={
<AccessContextProvider> <AccessContextProvider>
<InstallationsContextProvider> <InstallationsContextProvider>
<TestModeProvider>
<InstallationTabs /> <InstallationTabs />
</TestModeProvider>
</InstallationsContextProvider> </InstallationsContextProvider>
</AccessContextProvider> </AccessContextProvider>
} }

View File

@ -9,7 +9,9 @@ import {
IconButton, IconButton,
Modal, Modal,
TextField, TextField,
useTheme useTheme,
Switch,
FormControlLabel
} from '@mui/material'; } from '@mui/material';
import Typography from '@mui/material/Typography'; import Typography from '@mui/material/Typography';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
@ -18,6 +20,7 @@ import { AxiosError, AxiosResponse } from 'axios/index';
import routes from '../../../Resources/routes.json'; import routes from '../../../Resources/routes.json';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { TokenContext } from '../../../contexts/tokenContext'; import { TokenContext } from '../../../contexts/tokenContext';
import { useTestMode } from '../../../contexts/TestModeContext';
import { Action } from '../../../interfaces/S3Types'; import { Action } from '../../../interfaces/S3Types';
import Button from '@mui/material/Button'; import Button from '@mui/material/Button';
import { DateTimePicker, LocalizationProvider } from '@mui/x-date-pickers'; import { DateTimePicker, LocalizationProvider } from '@mui/x-date-pickers';
@ -45,7 +48,12 @@ function HistoryOfActions(props: HistoryProps) {
timestamp: actionDate.toDate(), timestamp: actionDate.toDate(),
description: '' description: ''
}); });
const { testModeMap, setTestMode } = useTestMode();
const isTestMode = testModeMap[props.id]||false;
const handleTestModeToggle = () => {
setTestMode(props.id,!isTestMode);
};
const handleDateChange = (newdate) => { const handleDateChange = (newdate) => {
setActionDate(newdate); setActionDate(newdate);
setNewAction({ setNewAction({
@ -132,15 +140,26 @@ function HistoryOfActions(props: HistoryProps) {
}} }}
> >
<div> <div>
{/*<DateTimePicker*/}
{/* label="Select Action Date"*/}
{/* name="timestamp"*/}
{/* value={actionDate}*/}
{/* onChange={(newDate) => handleDateChange(newDate)}*/}
{/* sx={{*/}
{/* width: 450,*/}
{/* marginTop: 2*/}
{/* }}*/}
{/*/>*/}
<DateTimePicker <DateTimePicker
label="Select Action Date" label="Select Action Date"
name="timestamp"
value={actionDate} value={actionDate}
onChange={(newDate) => handleDateChange(newDate)} onChange={handleDateChange}
sx={{ renderInput={(params) => (
width: 450, <TextField
marginTop: 2 {...params}
}} sx={{ width: 450, marginTop: 2 }}
/>
)}
/> />
<TextField <TextField
@ -166,8 +185,14 @@ function HistoryOfActions(props: HistoryProps) {
alignItems: 'center' alignItems: 'center'
}} }}
> >
<FormControlLabel
control={<Switch checked={isTestMode} onChange={handleTestModeToggle} />}
label="Test Mode"
/>
<Button <Button
sx={{ sx={{
marginLeft: 2,
textTransform: 'none', textTransform: 'none',
bgcolor: '#ffc04d', bgcolor: '#ffc04d',
color: '#111111', color: '#111111',

View File

@ -14,7 +14,9 @@ import {
} from '@mui/material'; } from '@mui/material';
import { I_Installation } from 'src/interfaces/InstallationTypes'; import { I_Installation } from 'src/interfaces/InstallationTypes';
import CancelIcon from '@mui/icons-material/Cancel'; import CancelIcon from '@mui/icons-material/Cancel';
import BuildIcon from '@mui/icons-material/Build';
import { WebSocketContext } from 'src/contexts/WebSocketContextProvider'; import { WebSocketContext } from 'src/contexts/WebSocketContextProvider';
import { useTestMode } from 'src/contexts/TestModeContext';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import { useLocation, useNavigate } from 'react-router-dom'; import { useLocation, useNavigate } from 'react-router-dom';
import routes from '../../../Resources/routes.json'; import routes from '../../../Resources/routes.json';
@ -30,6 +32,7 @@ const FlatInstallationView = (props: FlatInstallationViewProps) => {
const navigate = useNavigate(); const navigate = useNavigate();
const [selectedInstallation, setSelectedInstallation] = useState<number>(-1); const [selectedInstallation, setSelectedInstallation] = useState<number>(-1);
const currentLocation = useLocation(); const currentLocation = useLocation();
const { testModeMap } = useTestMode();
const sortedInstallations = [...props.installations].sort((a, b) => { const sortedInstallations = [...props.installations].sort((a, b) => {
// Compare the status field of each installation and sort them based on the status. // Compare the status field of each installation and sort them based on the status.
@ -242,6 +245,20 @@ const FlatInstallationView = (props: FlatInstallationViewProps) => {
: 'green' : 'green'
}} }}
/> />
{testModeMap[installation.id] && (
<BuildIcon
style={{
width: '23px',
height: '23px',
color: 'purple',
borderRadius: '50%',
position: 'relative',
zIndex: 1,
marginLeft: status === -1 || status === -2 ? '-23px' : '2px',
}}
/>
)}
</div> </div>
</TableCell> </TableCell>
</TableRow> </TableRow>

View File

@ -18,6 +18,7 @@ import Overview from '../Overview/overview';
import Configuration from '../Configuration/Configuration'; import Configuration from '../Configuration/Configuration';
import { fetchData } from 'src/content/dashboards/Installations/fetchData'; import { fetchData } from 'src/content/dashboards/Installations/fetchData';
import CancelIcon from '@mui/icons-material/Cancel'; import CancelIcon from '@mui/icons-material/Cancel';
import BuildIcon from '@mui/icons-material/Build';
import { Navigate, Route, Routes, useLocation } from 'react-router-dom'; import { Navigate, Route, Routes, useLocation } from 'react-router-dom';
import routes from '../../../Resources/routes.json'; import routes from '../../../Resources/routes.json';
import Information from '../Information/Information'; import Information from '../Information/Information';
@ -25,6 +26,7 @@ import BatteryView from '../BatteryView/BatteryView';
import { UserType } from '../../../interfaces/UserTypes'; import { UserType } from '../../../interfaces/UserTypes';
import HistoryOfActions from '../History/History'; import HistoryOfActions from '../History/History';
import PvView from '../PvView/PvView'; import PvView from '../PvView/PvView';
import { useTestMode } from '../../../contexts/TestModeContext';
interface singleInstallationProps { interface singleInstallationProps {
current_installation?: I_Installation; current_installation?: I_Installation;
@ -43,6 +45,7 @@ function Installation(props: singleInstallationProps) {
const [values, setValues] = useState<TopologyValues | null>(null); const [values, setValues] = useState<TopologyValues | null>(null);
const status = getStatus(props.current_installation.id); const status = getStatus(props.current_installation.id);
const [connected, setConnected] = useState(true); const [connected, setConnected] = useState(true);
const { testModeMap } = useTestMode();
if (props.current_installation == undefined) { if (props.current_installation == undefined) {
return null; return null;
@ -346,6 +349,20 @@ function Installation(props: singleInstallationProps) {
: 'green' : 'green'
}} }}
/> />
{testModeMap[props.current_installation.id] && (
<BuildIcon
style={{
width: '23px',
height: '23px',
color: 'purple',
borderRadius: '50%',
position: 'relative',
zIndex: 1,
marginLeft: status === -1 || status === -2 ? '-23px' : '2px',
}}
/>
)}
</div> </div>
</div> </div>

View File

@ -0,0 +1,30 @@
import React, { createContext, useContext, useState, ReactNode } from 'react';
interface TestModeContextProps {
testModeMap: { [key: number]: boolean };
setTestMode: (id: number, mode: boolean) => void;
}
const TestModeContext = createContext<TestModeContextProps | undefined>(undefined);
export const useTestMode = () => {
const context = useContext(TestModeContext);
if (!context) {
throw new Error('useTestMode must be used within a TestModeProvider');
}
return context;
};
export const TestModeProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
const [testModeMap, setTestModeMap] = useState<{ [key: number]: boolean }>({});
const setTestMode = (id: number, mode: boolean) => {
setTestModeMap(prev => ({ ...prev, [id]: mode }));
};
return (
<TestModeContext.Provider value={{ testModeMap, setTestMode }}>
{children}
</TestModeContext.Provider>
);
};