Fixed bug in database backup.

Updated view with icons in history actions
Increased performance when deleting an action
This commit is contained in:
Noe 2024-07-18 11:38:15 +02:00
parent ce50b7ef3e
commit 44f9fb7f7d
9 changed files with 472 additions and 435 deletions

View File

@ -808,10 +808,10 @@ public class Controller : ControllerBase
[HttpPost(nameof(DeleteAction))]
public async Task<ActionResult<IEnumerable<Object>>> DeleteAction([FromBody] UserAction action, Token authToken)
public async Task<ActionResult<IEnumerable<Object>>> DeleteAction(Int64 actionId, Token authToken)
{
var session = Db.GetSession(authToken);
var actionSuccess = await session.DeleteUserAction(action);
var actionSuccess = await session.DeleteUserAction(actionId);
return actionSuccess ? Ok() : Unauthorized();
}

View File

@ -151,22 +151,20 @@ public static class SessionMethods
if (user is null || user.UserType == 0)
return false;
action.UserName = user.Name;
Db.UpdateAction(action);
return true;
}
public static async Task<Boolean> DeleteUserAction(this Session? session, UserAction action)
public static async Task<Boolean> DeleteUserAction(this Session? session, Int64 actionId)
{
var user = session?.User;
if (user is null || user.UserType == 0)
return false;
var action = Db.GetActionById(actionId);
action.UserName = user.Name;
Db.Delete(action);
Console.WriteLine("---------------Deleted the Action in the database-----------------");

View File

@ -115,7 +115,7 @@ public static partial class Db
{
existingAction.Description = updatedAction.Description;
existingAction.Timestamp = updatedAction.Timestamp;
//Update(existingAction);
Update(existingAction);
Console.WriteLine("---------------Updated the Action in the database-----------------");
}
}

View File

@ -7,7 +7,7 @@ set -e
echo -e "\n============================ Deploy ============================\n"
ip_addresses_usb0=("10.2.2.118" "10.2.4.155" "10.2.3.244" "10.2.4.127")
ip_addresses_usb0=("10.2.2.118" "10.2.4.155" "10.2.3.244" "10.2.4.127" "10.2.4.96")
ip_addresses_usb1=("10.2.0.179" )

View File

@ -37,8 +37,12 @@ function DetailedBatteryView(props: DetailedBatteryViewProps) {
const [openModalFirmwareUpdate, setOpenModalFirmwareUpdate] = useState(false);
const [openModalResultFirmwareUpdate, setOpenModalResultFirmwareUpdate] =
useState(false);
const [openModalDownloadBatteryLog, setOpenModalDownloadBatteryLog] = useState(false);
const [openModalStartDownloadBatteryLog, setOpenModalStartDownloadBatteryLog] = useState(false);
const [openModalDownloadBatteryLog, setOpenModalDownloadBatteryLog] =
useState(false);
const [
openModalStartDownloadBatteryLog,
setOpenModalStartDownloadBatteryLog
] = useState(false);
const [openModalError, setOpenModalError] = useState(false);
const [errorMessage, setErrorMessage] = useState('');
@ -153,7 +157,6 @@ function DetailedBatteryView(props: DetailedBatteryViewProps) {
setOpenModalDownloadBatteryLog(false);
};
const DownloadBatteryLogModalHandleProceed = async () => {
setOpenModalDownloadBatteryLog(false);
setOpenModalStartDownloadBatteryLog(true);
@ -161,7 +164,9 @@ function DetailedBatteryView(props: DetailedBatteryViewProps) {
try {
// Start the job to generate the battery log
const startRes = await axiosConfig.post(
`/StartDownloadBatteryLog?batteryNode=${props.batteryData.BatteryId.toString()}&installationId=${props.installationId}`
`/StartDownloadBatteryLog?batteryNode=${props.batteryData.BatteryId.toString()}&installationId=${
props.installationId
}`
);
if (startRes.status === 200) {
@ -170,27 +175,29 @@ function DetailedBatteryView(props: DetailedBatteryViewProps) {
// Polling to check the job status
const checkJobStatus = async () => {
try {
const statusRes = await axiosConfig.get(`/GetJobResult?jobId=${jobId}`);
const statusRes = await axiosConfig.get(
`/GetJobResult?jobId=${jobId}`
);
if (statusRes.status === 200) {
const jobStatus = statusRes.data.status;
switch (jobStatus) {
case "Completed":
case 'Completed':
return statusRes.data.fileName; // Return FileName upon completion
case "Failed":
throw new Error("Job processing failed.");
case "Processing":
await new Promise(resolve => setTimeout(resolve, 60000)); // Wait for 60 seconds before next check
case 'Failed':
throw new Error('Job processing failed.');
case 'Processing':
await new Promise((resolve) => setTimeout(resolve, 60000)); // Wait for 60 seconds before next check
return checkJobStatus();
default:
throw new Error("Unknown download battery log job status.");
throw new Error('Unknown download battery log job status.');
}
} else {
throw new Error("Unexpected error occurred.");
throw new Error('Unexpected error occurred.');
}
} catch (error) {
throw new Error("Failed to fetch job status."); // Catch errors from status check
throw new Error('Failed to fetch job status.'); // Catch errors from status check
}
};
@ -198,9 +205,12 @@ function DetailedBatteryView(props: DetailedBatteryViewProps) {
const fileName = await checkJobStatus();
// Once job is completed, download the file
const res = await axiosConfig.get(`/DownloadBatteryLog?jobId=${jobId}`, {
responseType: 'blob',
});
const res = await axiosConfig.get(
`/DownloadBatteryLog?jobId=${jobId}`,
{
responseType: 'blob'
}
);
const finalFileName = fileName || 'unknown_file_name'; // Default filename if not received
console.log('Downloaded file name:', finalFileName);
@ -216,10 +226,13 @@ function DetailedBatteryView(props: DetailedBatteryViewProps) {
// Delete the file after successful download
console.log('Deleted file name:', finalFileName);
await axiosConfig.delete(`/DeleteBatteryLog`, { params: { fileName: finalFileName } });
await axiosConfig.delete(`/DeleteBatteryLog`, {
params: { fileName: finalFileName }
});
} else {
console.error('Failed to start downloading battery log in the backend.');
console.error(
'Failed to start downloading battery log in the backend.'
);
}
} catch (error) {
console.error('Error:', error.message);
@ -322,7 +335,8 @@ function DetailedBatteryView(props: DetailedBatteryViewProps) {
</Typography>
<Typography variant="body1" gutterBottom>
This action requires the battery service to be stopped for around 10-15 minutes.
This action requires the battery service to be stopped for around
10-15 minutes.
</Typography>
<div
@ -389,7 +403,8 @@ function DetailedBatteryView(props: DetailedBatteryViewProps) {
gutterBottom
sx={{ fontWeight: 'bold' }}
>
The battery log is getting downloaded. It will be saved in the Downloads folder. Please wait...
The battery log is getting downloaded. It will be saved in the
Downloads folder. Please wait...
</Typography>
<div
@ -429,7 +444,7 @@ function DetailedBatteryView(props: DetailedBatteryViewProps) {
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
width: 420,
width: 500,
bgcolor: 'background.paper',
borderRadius: 4,
boxShadow: 24,
@ -448,7 +463,8 @@ function DetailedBatteryView(props: DetailedBatteryViewProps) {
</Typography>
<Typography variant="body1" gutterBottom>
This action requires the battery service to be stopped for around 10-15 minutes.
This action requires the battery service to be stopped for around
10-15 minutes.
</Typography>
<div
@ -518,9 +534,17 @@ function DetailedBatteryView(props: DetailedBatteryViewProps) {
{errorMessage}
</Typography>
<div style={{ display: 'flex', alignItems: 'center', marginTop: 10 }}>
<div
style={{ display: 'flex', alignItems: 'center', marginTop: 10 }}
>
<Button
sx={{ marginTop: 2, textTransform: 'none', bgcolor: '#ffc04d', color: '#111111', '&:hover': { bgcolor: '#f7b34d' } }}
sx={{
marginTop: 2,
textTransform: 'none',
bgcolor: '#ffc04d',
color: '#111111',
'&:hover': { bgcolor: '#f7b34d' }
}}
onClick={ErrorModalHandleOk}
>
Ok
@ -581,7 +605,7 @@ function DetailedBatteryView(props: DetailedBatteryViewProps) {
marginLeft: '20px',
backgroundColor: '#ffc04d',
color: '#000000',
'&:hover': { bgcolor: '#f7b34d' },
'&:hover': { bgcolor: '#f7b34d' }
}}
>
Download Battery Log

View File

@ -5,14 +5,13 @@ import {
Card,
Container,
Divider,
FormControlLabel,
Grid,
IconButton,
Modal,
TextField,
useTheme,
Switch,
FormControlLabel,
Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Paper
TextField,
useTheme
} from '@mui/material';
import Typography from '@mui/material/Typography';
import { FormattedMessage } from 'react-intl';
@ -27,7 +26,9 @@ import Button from '@mui/material/Button';
import { DateTimePicker, LocalizationProvider } from '@mui/x-date-pickers';
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
import dayjs from 'dayjs';
import { UserContext } from 'src/contexts/userContext';
import EditIcon from '@mui/icons-material/Edit';
import DeleteIcon from '@mui/icons-material/Delete';
import { UserContext } from '../../../contexts/userContext';
interface HistoryProps {
errorLoadingS3Data: boolean;
@ -37,14 +38,13 @@ interface HistoryProps {
function HistoryOfActions(props: HistoryProps) {
const theme = useTheme();
const searchParams = new URLSearchParams(location.search);
const context = useContext(UserContext);
const { currentUser } = context;
const [history, setHistory] = useState<Action[]>([]);
const navigate = useNavigate();
const tokencontext = useContext(TokenContext);
const [actionDate, setActionDate] = useState(dayjs());
const { removeToken } = tokencontext;
const [openModalAddAction, setOpenModalAddAction] = useState(false);
const requiredFields = ['description', 'timestamp'];
const [newAction, setNewAction] = useState<Partial<Action>>({
installationId: props.id,
@ -53,12 +53,15 @@ function HistoryOfActions(props: HistoryProps) {
});
const { testModeMap, setTestMode } = useTestMode();
const isTestMode = testModeMap[props.id] || false;
const context = useContext(UserContext);
const { currentUser, setUser } = context;
const [isRowHovered, setHoveredRow] = useState(-1);
const [selectedAction, setSelectedAction] = useState<number>(-1);
const [editMode, setEditMode] = useState(false);
const handleTestModeToggle = () => {
setTestMode(props.id,!isTestMode);
setTestMode(props.id, !isTestMode);
};
const handleDateChange = (newdate) => {
setActionDate(newdate);
setNewAction({
@ -76,33 +79,36 @@ function HistoryOfActions(props: HistoryProps) {
};
const resetNewAction = () => {
console.log("Reset action to default");
console.log('Reset action to default');
setActionDate(dayjs());
setNewAction({
...newAction,
['description']: '',
['description']: ''
});
setEditMode(false);
};
const handleAddActionButton = () => {
resetNewAction();
//setOpenModalAddAction(!openModalAddAction);
setOpenModalAddAction(true);
};
const SumbitNewAction = () => {
const handleEdit = (action) => {
setEditMode(true);
setNewAction(action);
setOpenModalAddAction(true);
};
const SumbitNewAction = async () => {
const endpoint = editMode ? `/UpdateAction` : `/InsertNewAction`;
console.log("Add an action", endpoint);
const res=axiosConfig.post(endpoint,newAction).catch((err)=>{
//console.log('Add an action', endpoint);
const res = await axiosConfig.post(endpoint, newAction).catch((err) => {
if (err.response) {
// setError(true);
// setLoading(false);
}
});
if (res){
//setOpenModalAddAction(!openModalAddAction);
if (res) {
getHistory();
setOpenModalAddAction(false);
setEditMode(false);
}
@ -113,19 +119,16 @@ function HistoryOfActions(props: HistoryProps) {
setEditMode(false);
};
const HandleDelete = (e) => {
console.log("Delete this action");
const res = axiosConfig.post(`/DeleteAction`, newAction).catch((err) => {
if (err.response) {
// setError(true);
// setLoading(false);
}
});
const HandleDelete = async (action) => {
const res = await axiosConfig
.post(`/DeleteAction?actionId=${action.id}`)
.catch((err) => {
if (err.response) {
}
});
if (res) {
console.log("Delete this action is successful");
setOpenModalAddAction(false);
setEditMode(false);
getHistory();
}
};
@ -138,34 +141,7 @@ function HistoryOfActions(props: HistoryProps) {
return true;
};
const handleSelectOneAction = (action) => {
if (selectedAction != action.id) {
setSelectedAction(action.id);
setSelectedAction(-1);
if (action.userName === currentUser.name){
console.log("Select this action");
setActionDate(dayjs(action.timestamp));
setNewAction({ description: action.description, timestamp: action.timestamp });
setEditMode(true);
setOpenModalAddAction(true);
} else {
console.log('The user is not authorized to edit this action.');
}
} else {
setSelectedAction(-1);
}
};
const handleRowMouseEnter = (id) => {
setHoveredRow(id);
};
const handleRowMouseLeave = () => {
setHoveredRow(-1);
};
useEffect(() => {
const getHistory = () => {
axiosConfig
.get(`/GetHistoryForInstallation?id=${props.id}`)
.then((res: AxiosResponse<Action[]>) => {
@ -177,7 +153,10 @@ function HistoryOfActions(props: HistoryProps) {
navigate(routes.login);
}
});
console.log("Update the tab:", openModalAddAction);
};
useEffect(() => {
getHistory();
}, [openModalAddAction]);
return (
@ -206,27 +185,27 @@ function HistoryOfActions(props: HistoryProps) {
}}
>
<div>
{/*<DateTimePicker*/}
{/* label="Select Action Date"*/}
{/* name="timestamp"*/}
{/* value={actionDate}*/}
{/* onChange={(newDate) => handleDateChange(newDate)}*/}
{/* sx={{*/}
{/* width: 450,*/}
{/* marginTop: 2*/}
{/* }}*/}
{/*/>*/}
<DateTimePicker
label="Select Action Date"
value={actionDate}
onChange={handleDateChange}
renderInput={(params) => (
<TextField
{...params}
sx={{ width: 450, marginTop: 2 }}
/>
)}
name="timestamp"
value={editMode ? dayjs(newAction.timestamp) : actionDate}
onChange={(newDate) => handleDateChange(newDate)}
sx={{
width: 450,
marginTop: 2
}}
/>
{/*<DateTimePicker*/}
{/* label="Select Action Date"*/}
{/* value={actionDate}*/}
{/* onChange={handleDateChange}*/}
{/* renderInput={(params) => (*/}
{/* <TextField*/}
{/* {...params}*/}
{/* sx={{ width: 450, marginTop: 2 }}*/}
{/* />*/}
{/* )}*/}
{/*/>*/}
<TextField
label="Description"
@ -252,7 +231,12 @@ function HistoryOfActions(props: HistoryProps) {
}}
>
<FormControlLabel
control={<Switch checked={isTestMode} onChange={handleTestModeToggle} />}
control={
<Switch
checked={isTestMode}
onChange={handleTestModeToggle}
/>
}
label="Test Mode"
/>
@ -282,328 +266,272 @@ function HistoryOfActions(props: HistoryProps) {
>
Cancel
</Button>
{editMode && (
<Button
sx={{
marginLeft: 2,
textTransform: 'none',
bgcolor: '#ffc04d',
color: '#111111',
'&:hover': { bgcolor: '#f7b34d' }
}}
onClick={HandleDelete}
>
Delete
</Button>
)}
</div>
</Box>
</LocalizationProvider>
</Modal>
)}
{!openModalAddAction && (
<Container maxWidth="xl">
<Container maxWidth="xl">
<Grid container>
<Grid container>
<Grid container>
<Grid item xs={6} md={6}>
<Button
variant="contained"
onClick={handleAddActionButton}
sx={{
marginTop: '20px',
backgroundColor: '#ffc04d',
color: '#000000',
'&:hover': { bgcolor: '#f7b34d' }
}}
>
<FormattedMessage
id="add_action"
defaultMessage="Add New Action"
/>
</Button>
</Grid>
</Grid>
<Grid item xs={12} md={12}>
{history.length > 0 && (
// <Card sx={{ marginTop: '10px' }}>
// <Divider />
// <div>
// <div
// style={{
// height: '40px',
// marginBottom: '10px',
// display: 'flex',
// alignItems: 'center'
// }}
// >
// <div
// style={{
// flex: 2,
// marginTop: '15px',
// display: 'flex',
// alignItems: 'center',
// justifyContent: 'center'
// }}
// >
// <Typography
// variant="body1"
// color="dimgrey"
// fontWeight="bold"
// fontSize="1rem"
// gutterBottom
// noWrap
// >
// <FormattedMessage id="user" defaultMessage="User" />
// </Typography>
// </div>
//
// <div
// style={{
// flex: 1,
// marginTop: '15px',
// display: 'flex',
// alignItems: 'center',
// justifyContent: 'center'
// }}
// >
// <Typography
// variant="body1"
// color="dimgrey"
// fontWeight="bold"
// fontSize="1rem"
// gutterBottom
// noWrap
// >
// <FormattedMessage id="date" defaultMessage="Date" />
// </Typography>
// </div>
// <div
// style={{
// flex: 1,
// marginTop: '15px',
// display: 'flex',
// alignItems: 'center',
// justifyContent: 'center'
// }}
// >
// <Typography
// variant="body1"
// color="dimgrey"
// fontWeight="bold"
// fontSize="1rem"
// gutterBottom
// noWrap
// >
// <FormattedMessage id="time" defaultMessage="Time" />
// </Typography>
// </div>
// <div
// style={{
// flex: 6,
// marginTop: '15px',
// display: 'flex',
// alignItems: 'center',
// justifyContent: 'center'
// }}
// >
// <Typography
// variant="body1"
// color="dimgrey"
// fontWeight="bold"
// fontSize="1rem"
// gutterBottom
// noWrap
// >
// <FormattedMessage
// id="description"
// defaultMessage="Description"
// />
// </Typography>
// </div>
// </div>
// <Divider />
// <div style={{ maxHeight: '400px', overflowY: 'auto' }}>
// {history.map((action, index) => {
// // Parse the timestamp string to a Date object
// const date = new Date(action.timestamp);
//
// // Extract the date part (e.g., "2023-05-31")
// const datePart = date.toLocaleDateString();
//
// // Extract the time part (e.g., "12:34:56")
// const timePart = date.toLocaleTimeString();
//
// return (
// <React.Fragment key={index}>
// <Divider />
// <div
// style={{
// minHeight: '40px',
// marginBottom: '10px',
// display: 'flex',
// alignItems: 'center'
// }}
// >
// <div
// style={{
// flex: 2,
// marginTop: '15px',
// display: 'flex',
// alignItems: 'center',
// justifyContent: 'center'
// }}
// >
// <Typography
// variant="body1"
// fontWeight="bold"
// color="text.primary"
// gutterBottom
// >
// {action.userName}
// </Typography>
// </div>
//
// <div
// style={{
// flex: 1,
// marginTop: '15px',
// display: 'flex',
// alignItems: 'center',
// justifyContent: 'center'
// }}
// >
// <Typography
// variant="body1"
// fontWeight="bold"
// color="text.primary"
// gutterBottom
// >
// {datePart}
// </Typography>
// </div>
// <div
// style={{
// flex: 1,
// marginTop: '15px',
// display: 'flex',
// alignItems: 'center',
// justifyContent: 'center'
// }}
// >
// <Typography
// variant="body1"
// fontWeight="bold"
// color="text.primary"
// gutterBottom
// >
// {timePart}
// </Typography>
// </div>
//
// <div
// style={{
// flex: 6,
// display: 'flex',
// marginTop: '15px',
// alignItems: 'center',
// justifyContent: 'center'
// }}
// >
// <Typography
// variant="body1"
// fontWeight="bold"
// color="text.primary"
// gutterBottom
// style={{
// whiteSpace: 'normal',
// wordBreak: 'break-word'
// }}
// >
// {action.description}
// </Typography>
// </div>
// </div>
// </React.Fragment>
// );
// })}
// </div>
// </div>
// </Card>
<Card sx={{ marginTop: '10px' }}>
<Divider />
<TableContainer component={Paper}>
<Table>
<TableHead>
<TableRow>
<TableCell>User</TableCell>
<TableCell>Date</TableCell>
<TableCell>Time</TableCell>
<TableCell>Description</TableCell>
</TableRow>
</TableHead>
<TableBody>
{history.map((action, index) => {
const isActionSelected =
action.id === selectedAction;
const date = new Date(action.timestamp);
const datePart = date.toLocaleDateString();
const timePart = date.toLocaleTimeString();
const rowStyles = isRowHovered === action.id
? { cursor: 'pointer', backgroundColor: 'lightgray' }
: {};
return (
<TableRow
hover
key={action.id}
selected={isActionSelected}
style={rowStyles}
onClick={() => handleSelectOneAction(action)}
onMouseEnter={() => handleRowMouseEnter(action.id)}
onMouseLeave={handleRowMouseLeave}
>
<TableCell>{action.userName}</TableCell>
<TableCell>{datePart}</TableCell>
<TableCell>{timePart}</TableCell>
<TableCell>{action.description}</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
</TableContainer>
</Card>
)}
{!props.errorLoadingS3Data && history.length == 0 && (
<Alert
severity="error"
sx={{
display: 'flex',
alignItems: 'center',
marginTop: '20px'
}}
>
<FormattedMessage
id="nohistory"
defaultMessage="There is no history of actions"
/>
<IconButton
color="inherit"
size="small"
sx={{ marginLeft: '4px' }}
></IconButton>
</Alert>
)}
<Grid item xs={6} md={6}>
<Button
variant="contained"
onClick={handleAddActionButton}
sx={{
marginTop: '20px',
backgroundColor: '#ffc04d',
color: '#000000',
'&:hover': { bgcolor: '#f7b34d' }
}}
>
<FormattedMessage
id="add_action"
defaultMessage="Add New Action"
/>
</Button>
</Grid>
</Grid>
<Grid item xs={12} md={12} style={{ marginBottom: '20px' }}>
{props.errorLoadingS3Data && (
<Grid item xs={12} md={12}>
{history.length > 0 && (
<Card sx={{ marginTop: '10px' }}>
<Divider />
<div>
<div
style={{
height: '40px',
marginBottom: '10px',
display: 'flex',
alignItems: 'center'
}}
>
<div
style={{
flex: 2,
marginTop: '15px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
}}
>
<Typography
variant="body1"
color="dimgrey"
fontWeight="bold"
fontSize="1rem"
gutterBottom
noWrap
>
<FormattedMessage id="user" defaultMessage="User" />
</Typography>
</div>
<div
style={{
flex: 1,
marginTop: '15px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
}}
>
<Typography
variant="body1"
color="dimgrey"
fontWeight="bold"
fontSize="1rem"
gutterBottom
noWrap
>
<FormattedMessage id="date" defaultMessage="Date" />
</Typography>
</div>
<div
style={{
flex: 1,
marginTop: '15px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
}}
>
<Typography
variant="body1"
color="dimgrey"
fontWeight="bold"
fontSize="1rem"
gutterBottom
noWrap
>
<FormattedMessage id="time" defaultMessage="Time" />
</Typography>
</div>
<div
style={{
flex: 6,
marginTop: '15px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
}}
>
<Typography
variant="body1"
color="dimgrey"
fontWeight="bold"
fontSize="1rem"
gutterBottom
noWrap
>
<FormattedMessage
id="description"
defaultMessage="Description"
/>
</Typography>
</div>
</div>
<Divider />
<div style={{ maxHeight: '600px', overflowY: 'auto' }}>
{history.map((action, index) => {
// Parse the timestamp string to a Date object
const date = new Date(action.timestamp);
// Extract the date part (e.g., "2023-05-31")
const datePart = date.toLocaleDateString();
// Extract the time part (e.g., "12:34:56")
const timePart = date.toLocaleTimeString();
const iconStyle =
action.userName === currentUser.name
? {}
: { color: 'rgba(0, 0, 0, 0.26)' };
return (
<React.Fragment key={index}>
<Divider />
<div
style={{
minHeight: '40px',
marginBottom: '10px',
display: 'flex',
alignItems: 'center'
}}
>
<div
style={{
flex: 2,
marginTop: '15px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
}}
>
<Typography
variant="body1"
fontWeight="bold"
color="text.primary"
gutterBottom
>
{action.userName}
</Typography>
</div>
<div
style={{
flex: 1,
marginTop: '15px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
}}
>
<Typography
variant="body1"
fontWeight="bold"
color="text.primary"
gutterBottom
>
{datePart}
</Typography>
</div>
<div
style={{
flex: 1,
marginTop: '15px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
}}
>
<Typography
variant="body1"
fontWeight="bold"
color="text.primary"
gutterBottom
>
{timePart}
</Typography>
</div>
<div
style={{
flex: 6,
display: 'flex',
marginTop: '15px',
alignItems: 'center',
justifyContent: 'center'
}}
>
<Typography
variant="body1"
fontWeight="bold"
color="text.primary"
gutterBottom
style={{
whiteSpace: 'normal',
wordBreak: 'break-word'
}}
>
{action.description}
</Typography>
</div>
<div
style={{
flex: 1,
marginTop: '15px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
}}
>
<IconButton
style={iconStyle}
onClick={() => handleEdit(action)}
disabled={action.userName != currentUser.name}
>
<EditIcon />
</IconButton>
<IconButton
style={iconStyle}
onClick={() => HandleDelete(action)}
disabled={action.userName != currentUser.name}
>
<DeleteIcon />
</IconButton>
</div>
</div>
</React.Fragment>
);
})}
</div>
</div>
</Card>
)}
{!props.errorLoadingS3Data && history.length == 0 && (
<Alert
severity="error"
sx={{
@ -613,8 +541,8 @@ function HistoryOfActions(props: HistoryProps) {
}}
>
<FormattedMessage
id="cannotloadloggingdata"
defaultMessage="Cannot load logging data"
id="nohistory"
defaultMessage="There is no history of actions"
/>
<IconButton
color="inherit"
@ -624,8 +552,31 @@ function HistoryOfActions(props: HistoryProps) {
</Alert>
)}
</Grid>
</Container>
)}
</Grid>
<Grid item xs={12} md={12} style={{ marginBottom: '20px' }}>
{props.errorLoadingS3Data && (
<Alert
severity="error"
sx={{
display: 'flex',
alignItems: 'center',
marginTop: '20px'
}}
>
<FormattedMessage
id="cannotloadloggingdata"
defaultMessage="Cannot load logging data"
/>
<IconButton
color="inherit"
size="small"
sx={{ marginLeft: '4px' }}
></IconButton>
</Alert>
)}
</Grid>
</Container>
</>
);
}

View File

@ -1,5 +1,11 @@
import React, { useContext, useEffect, useRef, useState } from 'react';
import { Card, CircularProgress, Grid, Typography } from '@mui/material';
import {
Card,
CircularProgress,
Container,
Grid,
Typography
} from '@mui/material';
import { I_Installation } from 'src/interfaces/InstallationTypes';
import { UserContext } from 'src/contexts/userContext';
import AccessContextProvider from 'src/contexts/AccessContextProvider';
@ -45,6 +51,7 @@ function Installation(props: singleInstallationProps) {
const [values, setValues] = useState<TopologyValues | null>(null);
const status = getStatus(props.current_installation.id);
const [connected, setConnected] = useState(true);
const [loading, setLoading] = useState(true);
const { testModeMap } = useTestMode();
if (props.current_installation == undefined) {
@ -94,9 +101,11 @@ function Installation(props: singleInstallationProps) {
if (i <= 0) {
setConnected(false);
setLoading(false);
return false;
}
setConnected(true);
setLoading(false);
const timestamp = Object.keys(res)[Object.keys(res).length - 1];
@ -133,9 +142,11 @@ function Installation(props: singleInstallationProps) {
if (i <= 0) {
setConnected(false);
setLoading(false);
return false;
}
setConnected(true);
setLoading(false);
console.log('NUMBER OF FILES=' + Object.keys(res).length);
while (continueFetching.current) {
@ -259,6 +270,7 @@ function Installation(props: singleInstallationProps) {
{props.current_installation.name}
</Typography>
</div>
{currentTab == 'live' && values && (
<div style={{ display: 'flex', alignItems: 'center' }}>
<Typography
@ -359,13 +371,35 @@ function Installation(props: singleInstallationProps) {
borderRadius: '50%',
position: 'relative',
zIndex: 1,
marginLeft: status === -1 || status === -2 ? '-23px' : '2px',
marginLeft: status === -1 || status === -2 ? '-23px' : '2px'
}}
/>
)}
</div>
</div>
{loading && currentTab != 'information' && currentTab != 'history' && (
<Container
maxWidth="xl"
sx={{
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
height: '70vh'
}}
>
<CircularProgress size={60} style={{ color: '#ffc04d' }} />
<Typography
variant="body2"
style={{ color: 'black', fontWeight: 'bold' }}
mt={2}
>
Connecting to the device...
</Typography>
</Container>
)}
<Card variant="outlined">
<Grid
container

View File

@ -1,5 +1,11 @@
import React, { useContext, useEffect, useRef, useState } from 'react';
import { Card, CircularProgress, Grid, Typography } from '@mui/material';
import {
Card,
CircularProgress,
Container,
Grid,
Typography
} from '@mui/material';
import { I_Installation } from 'src/interfaces/InstallationTypes';
import { UserContext } from 'src/contexts/userContext';
import { TimeSpan, UnixTime } from 'src/dataCache/time';
@ -39,6 +45,7 @@ function Installation(props: singleInstallationProps) {
setFailedToCommunicateWithInstallation
] = useState(0);
const [connected, setConnected] = useState(true);
const [loading, setLoading] = useState(true);
if (props.current_installation == undefined) {
return null;
@ -90,9 +97,11 @@ function Installation(props: singleInstallationProps) {
if (i <= 0) {
setConnected(false);
setLoading(false);
return false;
}
setConnected(true);
setLoading(false);
console.log('NUMBER OF FILES=' + Object.keys(res).length);
while (continueFetching.current) {
@ -267,6 +276,27 @@ function Installation(props: singleInstallationProps) {
/>
</div>
</div>
{loading && currentTab != 'information' && currentTab != 'overview' && (
<Container
maxWidth="xl"
sx={{
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
height: '70vh'
}}
>
<CircularProgress size={60} style={{ color: '#ffc04d' }} />
<Typography
variant="body2"
style={{ color: 'black', fontWeight: 'bold' }}
mt={2}
>
Connecting to the device...
</Typography>
</Container>
)}
<Card variant="outlined">
<Grid

View File

@ -92,10 +92,10 @@ function SalidomoInstallationTabs() {
/>
)
},
// {
// value: 'overview',
// label: <FormattedMessage id="overview" defaultMessage="Overview" />
// },
{
value: 'overview',
label: <FormattedMessage id="overview" defaultMessage="Overview" />
},
{
value: 'log',
label: <FormattedMessage id="log" defaultMessage="Log" />