510 lines
14 KiB
JavaScript
510 lines
14 KiB
JavaScript
import {
|
|
Checkbox,
|
|
Grid,
|
|
Table,
|
|
TableBody,
|
|
TableCell,
|
|
TableFooter,
|
|
TablePagination,
|
|
TableRow,
|
|
Tooltip,
|
|
} from "@material-ui/core";
|
|
import DeleteIcon from "@material-ui/icons/Delete";
|
|
import SendIcon from "@material-ui/icons/Send";
|
|
import VisibilityIcon from "@material-ui/icons/Visibility";
|
|
import {
|
|
ToggleButton,
|
|
ToggleButtonGroup
|
|
} from "@mui/material";
|
|
import clsx from "clsx";
|
|
import React, { useEffect, useState } from "react";
|
|
import { Link } from "react-router-dom";
|
|
|
|
import EditIcon from "@material-ui/icons/Edit";
|
|
import { logger } from "../../../services/monitoring";
|
|
import { LocalDateTimeString } from "../../../utils/dates";
|
|
import { TYPE_MANIFEST_AFTERSALES, TYPE_MANIFEST_CONFIG, TYPE_MANIFEST_SOFTWARE } from "../../../utils/manifest_types";
|
|
import { Permissions, hasRole } from "../../../utils/roles";
|
|
import {
|
|
ManifestsProvider,
|
|
useManifestsContext
|
|
} from "../../Contexts/ManifestsContext";
|
|
import { useStatusContext } from "../../Contexts/StatusContext";
|
|
import { useUserContext } from "../../Contexts/UserContext";
|
|
import DropDownButton from "../../Controls/DropDownButton";
|
|
import ECUList from "../../Controls/ECUList";
|
|
import { RoleWrap } from "../../Controls/RoleWrap";
|
|
import SearchField from "../../Controls/SearchField";
|
|
import DeleteConfirmation from "../../DeleteConfirmation";
|
|
import TableHeaderSortable from "../../Table/HeaderSortable";
|
|
import { useLocalStorage } from "../../useLocalStorage";
|
|
import useStyles from "../../useStyles";
|
|
import { useUpdateManifest } from "../../../hooks";
|
|
import GeneralConfirmation from "../../GeneralConfirmation";
|
|
|
|
const tableColumns = [
|
|
{
|
|
id: "id",
|
|
label: "ID",
|
|
},
|
|
{
|
|
id: "name",
|
|
label: "Name",
|
|
},
|
|
{
|
|
id: "version",
|
|
label: "Version",
|
|
},
|
|
{
|
|
id: "manifest_type",
|
|
label: "Type",
|
|
},
|
|
{
|
|
id: "sums",
|
|
label: "SUMS",
|
|
},
|
|
{
|
|
id: "type",
|
|
label: "Update",
|
|
},
|
|
{
|
|
id: "created_at",
|
|
label: "Created",
|
|
},
|
|
{
|
|
id: "updated_at",
|
|
label: "Updated",
|
|
},
|
|
{
|
|
id: "",
|
|
label: "Actions",
|
|
},
|
|
];
|
|
|
|
const formatType = (type) => {
|
|
switch (type) {
|
|
case "forced":
|
|
return "Forced";
|
|
default:
|
|
return "Standard";
|
|
}
|
|
};
|
|
|
|
const formatManifestType = (manifestType) => {
|
|
switch (manifestType) {
|
|
case 1:
|
|
return "Software";
|
|
case 2:
|
|
return "Config";
|
|
case 3:
|
|
return "Magna";
|
|
case 4:
|
|
return "Aftersales";
|
|
default:
|
|
return manifestType;
|
|
}
|
|
}
|
|
|
|
const PAGE_SIZE = "MANIFEST_LIST_PAGE_SIZE";
|
|
|
|
const MainForm = () => {
|
|
const classes = useStyles();
|
|
const [pageSize, setPageSize] = useLocalStorage(PAGE_SIZE, 10);
|
|
const [pageIndex, setPageIndex] = useState(0);
|
|
const [orderBy, setOrderBy] = useState("id");
|
|
const [order, setOrder] = useState("asc");
|
|
const [search, setSearch] = useLocalStorage("DEPLOYMENT_SEARCH", "");
|
|
const [active, setActive] = useLocalStorage("DEPLOYMENT_TAB_TOGGLE", "software");
|
|
|
|
const [showDeleteModal, setShowDeleteModal] = useState(false);
|
|
const [showArchiveModal, setShowArchiveModal] = useState(false);
|
|
const [archiveLabel, setArchiveLabel] = useState("Archive");
|
|
|
|
const { getManifests, manifests, totalManifests } =
|
|
useManifestsContext();
|
|
const { setMessage, setTitle, setSitePath } = useStatusContext();
|
|
const {
|
|
token: {
|
|
idToken: { jwtToken: token },
|
|
},
|
|
groups,
|
|
providers,
|
|
} = useUserContext();
|
|
const {
|
|
remove,
|
|
archive,
|
|
updateManifestIds,
|
|
setUpdateManifestIds,
|
|
setMakeActive,
|
|
} = useUpdateManifest(token);
|
|
|
|
const sortHandler = (event, property) => {
|
|
if (property === orderBy) {
|
|
if (order === "asc") {
|
|
setOrder("desc");
|
|
} else {
|
|
setOrder("asc");
|
|
}
|
|
} else {
|
|
setOrderBy(property);
|
|
setOrder("asc");
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
setTitle("Deployments");
|
|
setSitePath([]);
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
(async () => {
|
|
try {
|
|
handleActiveChange(null, active);
|
|
switch (active) {
|
|
case "all":
|
|
await getManifests(
|
|
{
|
|
limit: pageSize,
|
|
offset: pageSize * pageIndex,
|
|
order: `${orderBy} ${order}`,
|
|
search,
|
|
},
|
|
token
|
|
);
|
|
break;
|
|
case "aftersales":
|
|
await getManifests(
|
|
{
|
|
limit: pageSize,
|
|
offset: pageSize * pageIndex,
|
|
order: `${orderBy} ${order}`,
|
|
manifest_type: TYPE_MANIFEST_AFTERSALES,
|
|
search,
|
|
active: "true",
|
|
},
|
|
token
|
|
);
|
|
break;
|
|
case "config":
|
|
await getManifests(
|
|
{
|
|
limit: pageSize,
|
|
offset: pageSize * pageIndex,
|
|
order: `${orderBy} ${order}`,
|
|
manifest_type: TYPE_MANIFEST_CONFIG,
|
|
search,
|
|
active: "true",
|
|
},
|
|
token
|
|
);
|
|
break;
|
|
case "software":
|
|
await getManifests(
|
|
{
|
|
limit: pageSize,
|
|
offset: pageSize * pageIndex,
|
|
order: `${orderBy} ${order}`,
|
|
manifest_type: TYPE_MANIFEST_SOFTWARE,
|
|
search,
|
|
active: "true",
|
|
},
|
|
token
|
|
);
|
|
break;
|
|
case "archived":
|
|
await getManifests(
|
|
{
|
|
limit: pageSize,
|
|
offset: pageSize * pageIndex,
|
|
order: `${orderBy} ${order}`,
|
|
manifest_type: TYPE_MANIFEST_SOFTWARE,
|
|
search,
|
|
active: "false",
|
|
},
|
|
token
|
|
);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
} catch (e) {
|
|
setMessage(e.message);
|
|
logger.warn(e.stack);
|
|
}
|
|
})();
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [pageIndex, pageSize, token, orderBy, order, search, active, updateManifestIds]);
|
|
|
|
useEffect(() => {
|
|
setUpdateManifestIds([]);
|
|
}, [active, setUpdateManifestIds]);
|
|
|
|
const handleChangePageIndex = (_event, newIndex) => {
|
|
setPageIndex(newIndex);
|
|
};
|
|
|
|
const handleChangePageSize = (event) => {
|
|
setPageSize(parseInt(event.target.value, 10));
|
|
setPageIndex(0);
|
|
};
|
|
|
|
const handleSearch = (query) => {
|
|
setPageIndex(0);
|
|
setSearch(query);
|
|
};
|
|
|
|
const handleActiveChange = (event, newAlignment) => {
|
|
if (newAlignment !== null) {
|
|
setActive(newAlignment);
|
|
setMakeActive(newAlignment === 'archived');
|
|
setArchiveLabel(() => {
|
|
if (newAlignment === "archived") {
|
|
return "Activate";
|
|
}
|
|
|
|
return "Archive";
|
|
});
|
|
}
|
|
}
|
|
|
|
const handleSelectAll = () => {
|
|
setUpdateManifestIds((selected) => selected.length ? [] : manifests.map((manifest) => manifest.id));
|
|
};
|
|
|
|
const handleSelect = (event, manifest) => {
|
|
setUpdateManifestIds((selected) => {
|
|
if (event.target.checked) {
|
|
return [...selected, manifest.id];
|
|
}
|
|
return selected.filter(id => id !== manifest.id);
|
|
});
|
|
};
|
|
|
|
const setDeletePopup = (row) => {
|
|
handleSelect({ target: { checked: true } }, row);
|
|
setShowDeleteModal(true);
|
|
};
|
|
|
|
const onArchive = async () => {
|
|
try {
|
|
await archive()
|
|
.then(({ message }) => {
|
|
setUpdateManifestIds([]);
|
|
setMessage(message);
|
|
});
|
|
} catch (e) {
|
|
setMessage(e.message);
|
|
logger.warn(e.stack);
|
|
}
|
|
};
|
|
|
|
const onDelete = async () => {
|
|
try {
|
|
await remove()
|
|
.then(({ summary }) => {
|
|
setUpdateManifestIds([]);
|
|
setMessage(summary);
|
|
});
|
|
} catch (e) {
|
|
setMessage(e.message);
|
|
logger.warn(e.stack);
|
|
}
|
|
};
|
|
|
|
const Actions = (row) => {
|
|
let actions = [];
|
|
if (hasRole(groups, Permissions.FiskerMagnaRead, providers)) {
|
|
actions.push({
|
|
tip: `Status "${row.name} ${row.version}"`,
|
|
link: `/package-status/${row.id}`,
|
|
icon: (
|
|
<VisibilityIcon aria-label={`Status ${row.name} ${row.version}`} />
|
|
),
|
|
});
|
|
}
|
|
if (hasRole(groups, Permissions.FiskerCreate, providers)) {
|
|
actions.push({
|
|
tip: `Update "${row.name} ${row.version}"`,
|
|
link: `/package-update/${row.id}`,
|
|
icon: <EditIcon aria-label={`Update ${row.name} ${row.version}`} />,
|
|
});
|
|
}
|
|
if (hasRole(groups, Permissions.FiskerUpdateDeploy, providers)) {
|
|
actions.push({
|
|
tip: `Deploy "${row.name} ${row.version}"`,
|
|
link: `/package-deploy/${row.id}`,
|
|
icon: <SendIcon aria-label={`Deploy ${row.name} ${row.version}`} />,
|
|
});
|
|
}
|
|
if (hasRole(groups, Permissions.FiskerDelete, providers)) {
|
|
actions.push({
|
|
tip: `Delete "${row.name} ${row.version}"`,
|
|
id: row.id,
|
|
icon: <DeleteIcon aria-label={`Delete ${row.name} ${row.version}`} />,
|
|
});
|
|
}
|
|
if (actions.length === 0) return ["No actions"];
|
|
|
|
return actions.map((action) => {
|
|
if (action.link != null) {
|
|
return (
|
|
<Tooltip key={action.link} title={action.tip}>
|
|
<Link to={action.link} style={{ margin: 5 }}>
|
|
{action.icon}
|
|
</Link>
|
|
</Tooltip>
|
|
);
|
|
} else {
|
|
return (
|
|
<span key={`delete-${action.id}-of-key`}>
|
|
<Tooltip key={`delete-${action.id}`} title={action.tip}>
|
|
<Link to="#" onClick={() => setDeletePopup(row)}>
|
|
{action.icon}
|
|
</Link>
|
|
</Tooltip>
|
|
</span>
|
|
);
|
|
}
|
|
});
|
|
};
|
|
|
|
return (
|
|
<div className={clsx(classes.paper, classes.tableSize)}>
|
|
<Grid container className={classes.root} spacing={2}>
|
|
<Grid item md={4} className={classes.textJustifyAlign}></Grid>
|
|
<Grid item md={4} className={classes.textCenterAlign}>
|
|
<SearchField classes={classes} onSearch={handleSearch} savedSearchValue={search} />
|
|
<RoleWrap
|
|
groups={groups}
|
|
providers={providers}
|
|
rolesPerProvider={Permissions.FiskerCreate}
|
|
>
|
|
<ToggleButtonGroup
|
|
value={active}
|
|
exclusive
|
|
aria-label="Active"
|
|
onChange={handleActiveChange}
|
|
>
|
|
<ToggleButton value={"software"}>Software</ToggleButton>
|
|
<ToggleButton value={"archived"}>Archived</ToggleButton>
|
|
<ToggleButton value={"all"}>All</ToggleButton>
|
|
</ToggleButtonGroup>
|
|
</RoleWrap>
|
|
</Grid>
|
|
<Grid item md={4} className={classes.textRightAlign}>
|
|
<DropDownButton
|
|
actions={[
|
|
{
|
|
name: archiveLabel,
|
|
trigger: () => setShowArchiveModal(true),
|
|
disabled: !updateManifestIds.length || active === "all",
|
|
}
|
|
]}
|
|
/>
|
|
</Grid>
|
|
</Grid>
|
|
<Table>
|
|
<TableHeaderSortable
|
|
classes={classes}
|
|
orderBy={orderBy}
|
|
order={order}
|
|
columnData={tableColumns}
|
|
onSortRequest={sortHandler}
|
|
multiSelect
|
|
onSelectAll={handleSelectAll}
|
|
selectCount={updateManifestIds ? updateManifestIds.length : 0}
|
|
rowCount={manifests ? manifests.length : 0}
|
|
/>
|
|
<TableBody>
|
|
{manifests.map((row) => {
|
|
const isSelected = updateManifestIds
|
|
? !!updateManifestIds.find((id) => id === row.id)
|
|
: false;
|
|
return (
|
|
<TableRow key={row.id}>
|
|
<TableCell padding="checkbox">
|
|
<Checkbox
|
|
checked={isSelected}
|
|
onChange={(event) => handleSelect(event, row)}
|
|
/>
|
|
</TableCell>
|
|
<TableCell align="center">{row.id}</TableCell>
|
|
<TableCell align="center">
|
|
{row.name}
|
|
{row.ecu_list && (
|
|
<>
|
|
<br />
|
|
<ECUList
|
|
list={row.ecu_list}
|
|
search={search}
|
|
searchedOnly={true}
|
|
/>
|
|
</>
|
|
)}
|
|
</TableCell>
|
|
<TableCell align="center">{row.version}</TableCell>
|
|
<TableCell align="center">
|
|
{formatManifestType(row.manifest_type)}
|
|
</TableCell>
|
|
<TableCell align="center">
|
|
<Link to={`/sums/${row.sums}`}>
|
|
{row.sums}
|
|
</Link>
|
|
</TableCell>
|
|
<TableCell align="center">
|
|
{formatType(row.type)}
|
|
</TableCell>
|
|
<TableCell align="center">
|
|
{LocalDateTimeString(row.created)}
|
|
</TableCell>
|
|
<TableCell align="center">
|
|
{LocalDateTimeString(row.updated)}
|
|
</TableCell>
|
|
<TableCell align="center">{Actions(row)}</TableCell>
|
|
</TableRow>
|
|
);
|
|
})}
|
|
</TableBody>
|
|
<TableFooter>
|
|
<TableRow>
|
|
<TablePagination
|
|
rowsPerPageOptions={[5, 10, 25, 100]}
|
|
colSpan={tableColumns.length + 1}
|
|
count={totalManifests}
|
|
rowsPerPage={pageSize}
|
|
page={pageIndex}
|
|
SelectProps={{
|
|
inputProps: { "aria-label": "rows per page" },
|
|
native: true,
|
|
}}
|
|
onPageChange={handleChangePageIndex}
|
|
onRowsPerPageChange={handleChangePageSize}
|
|
/>
|
|
</TableRow>
|
|
</TableFooter>
|
|
</Table>
|
|
<DeleteConfirmation
|
|
message={`${updateManifestIds.length} records.`}
|
|
open={showDeleteModal}
|
|
close={() => setShowDeleteModal(false)}
|
|
deleteFunction={() => onDelete()}
|
|
/>
|
|
<GeneralConfirmation
|
|
title={`${archiveLabel} Resource?`}
|
|
message={`${archiveLabel} ${updateManifestIds.length} records.`}
|
|
open={showArchiveModal}
|
|
close={() => setShowArchiveModal(false)}
|
|
actionFunction={() => onArchive()}
|
|
/>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
const ManifestsList = () => (
|
|
<ManifestsProvider>
|
|
<MainForm />
|
|
</ManifestsProvider>
|
|
);
|
|
|
|
export default ManifestsList; |