Merge branch 'release/0.0.3'
This commit is contained in:
@@ -3359,10 +3359,52 @@ exports[`App Route /package-deploy authenticated 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="MuiGrid-root makeStyles-textRightAlign-0 MuiGrid-item MuiGrid-grid-md-4"
|
||||
class="MuiGrid-root MuiGrid-container MuiGrid-item MuiGrid-justify-content-xs-flex-end MuiGrid-grid-md-4"
|
||||
>
|
||||
<div
|
||||
class="MuiFormControl-root MuiFormControl-marginNormal"
|
||||
>
|
||||
<label
|
||||
class="MuiFormLabel-root MuiInputLabel-root makeStyles-whiteBackground-0 MuiInputLabel-formControl MuiInputLabel-animated MuiInputLabel-outlined"
|
||||
data-shrink="false"
|
||||
>
|
||||
Software Version
|
||||
</label>
|
||||
<div
|
||||
class="MuiInputBase-root MuiOutlinedInput-root MuiInputBase-formControl"
|
||||
>
|
||||
<select
|
||||
aria-invalid="false"
|
||||
class="MuiSelect-root MuiSelect-select MuiSelect-outlined MuiInputBase-input MuiOutlinedInput-input"
|
||||
/>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="MuiSvgIcon-root MuiSelect-icon MuiSelect-iconOutlined"
|
||||
focusable="false"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
d="M7 10l5 5 5-5z"
|
||||
/>
|
||||
</svg>
|
||||
<fieldset
|
||||
aria-hidden="true"
|
||||
class="PrivateNotchedOutline-root-0 MuiOutlinedInput-notchedOutline"
|
||||
style="padding-left: 8px;"
|
||||
>
|
||||
<legend
|
||||
class="PrivateNotchedOutline-legend-0"
|
||||
style="width: 0.01px;"
|
||||
>
|
||||
<span>
|
||||
|
||||
</span>
|
||||
</legend>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
class="MuiButtonBase-root MuiButton-root MuiButton-contained makeStyles-formControl-0 makeStyles-textField-0 MuiButton-containedPrimary Mui-disabled Mui-disabled"
|
||||
class="MuiButtonBase-root MuiButton-root MuiButton-text MuiButton-textPrimary Mui-disabled Mui-disabled"
|
||||
disabled=""
|
||||
tabindex="-1"
|
||||
type="submit"
|
||||
@@ -3370,7 +3412,16 @@ exports[`App Route /package-deploy authenticated 1`] = `
|
||||
<span
|
||||
class="MuiButton-label"
|
||||
>
|
||||
Deploy
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="MuiSvgIcon-root"
|
||||
focusable="false"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
@@ -5203,6 +5254,29 @@ exports[`App Route /packages authenticated 1`] = `
|
||||
</svg>
|
||||
</span>
|
||||
</th>
|
||||
<th
|
||||
class="MuiTableCell-root MuiTableCell-head MuiTableCell-alignCenter"
|
||||
scope="col"
|
||||
>
|
||||
<span
|
||||
aria-disabled="false"
|
||||
class="MuiButtonBase-root MuiTableSortLabel-root"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
SUMS
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="MuiSvgIcon-root MuiTableSortLabel-icon MuiTableSortLabel-iconDirectionAsc"
|
||||
focusable="false"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
d="M20 12l-1.41-1.41L13 16.17V4h-2v12.17l-5.58-5.59L4 12l8 8 8-8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</th>
|
||||
<th
|
||||
class="MuiTableCell-root MuiTableCell-head MuiTableCell-alignCenter"
|
||||
scope="col"
|
||||
@@ -5301,6 +5375,9 @@ exports[`App Route /packages authenticated 1`] = `
|
||||
>
|
||||
1.0
|
||||
</td>
|
||||
<td
|
||||
class="MuiTableCell-root MuiTableCell-body MuiTableCell-alignCenter"
|
||||
/>
|
||||
<td
|
||||
class="MuiTableCell-root MuiTableCell-body MuiTableCell-alignCenter"
|
||||
>
|
||||
@@ -5384,7 +5461,7 @@ exports[`App Route /packages authenticated 1`] = `
|
||||
>
|
||||
<td
|
||||
class="MuiTableCell-root MuiTableCell-footer MuiTablePagination-root"
|
||||
colspan="6"
|
||||
colspan="8"
|
||||
>
|
||||
<div
|
||||
class="MuiToolbar-root MuiToolbar-regular MuiTablePagination-toolbar MuiToolbar-gutters"
|
||||
@@ -7259,7 +7336,7 @@ exports[`App Route /tools/security-dll authenticated 1`] = `
|
||||
<strong>
|
||||
Important Note:
|
||||
</strong>
|
||||
Certificates expire in one month from the time they are generated. They have to be re-downloaded again to connect to the Fisker cloud.
|
||||
Certificates expire in 3 months from the time they are generated. They have to be re-downloaded again to connect to the Fisker cloud.
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
|
||||
@@ -80,6 +80,12 @@ exports[`CarUpdatesTab Render 1`] = `
|
||||
</svg>
|
||||
</span>
|
||||
</th>
|
||||
<th
|
||||
class="MuiTableCell-root MuiTableCell-head MuiTableCell-alignCenter"
|
||||
scope="col"
|
||||
>
|
||||
SUMS
|
||||
</th>
|
||||
<th
|
||||
class="MuiTableCell-root MuiTableCell-head MuiTableCell-alignCenter"
|
||||
scope="col"
|
||||
@@ -187,8 +193,11 @@ exports[`CarUpdatesTab Render 1`] = `
|
||||
<tr
|
||||
class="MuiTableRow-root MuiTableRow-footer"
|
||||
>
|
||||
<td>
|
||||
No Car Updates found
|
||||
<td
|
||||
class="MuiTableCell-root MuiTableCell-footer MuiTableCell-alignCenter"
|
||||
colspan="7"
|
||||
>
|
||||
No Car Updates
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import React, { useContext, useState } from "react";
|
||||
|
||||
import api from "../../services/updatesAPI";
|
||||
import { validateSoftwareVersion } from "../../utils/softwareVersions";
|
||||
import { validateStatusMessage } from "../../utils/statusMessage";
|
||||
|
||||
export const SELECT_VERSION = "Select Version";
|
||||
const FINAL_UPDATE_STATES = ["package_install_complete"];
|
||||
const CarUpdatesContext = React.createContext();
|
||||
const SELECT_VERSION_OBJ = { version: SELECT_VERSION }
|
||||
|
||||
const validateDeployClosure = (data, propertyName, errPfx) => {
|
||||
if (data === null) {
|
||||
@@ -32,10 +35,26 @@ const validateDeployFleetUpdates = (data) => {
|
||||
export const CarUpdatesProvider = ({ children }) => {
|
||||
const [busy, setBusy] = useState(false);
|
||||
const [carUpdates, setCarUpdates] = useState([]);
|
||||
const [versions, setVersions] = useState([SELECT_VERSION_OBJ]);
|
||||
const [totalCarUpdates, setTotalCarUpdates] = useState(0);
|
||||
const [delayCount, setDelayCount] = useState(0);
|
||||
let progressTimer = 0;
|
||||
|
||||
const cancelUpdate = async (id, token) => {
|
||||
let result;
|
||||
|
||||
try {
|
||||
setBusy(true);
|
||||
result = await api.cancelCarUpdate(id, token);
|
||||
if (result.error)
|
||||
throw new Error(`Cancel car update error. ${result.message}`);
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
const deployCarUpdates = async (data, token) => {
|
||||
let result;
|
||||
|
||||
@@ -223,14 +242,36 @@ export const CarUpdatesProvider = ({ children }) => {
|
||||
return result;
|
||||
};
|
||||
|
||||
const cancelUpdate = async (id, token) => {
|
||||
const getSUMSVersions = async (token) => {
|
||||
let result;
|
||||
|
||||
try {
|
||||
setBusy(true);
|
||||
result = await api.cancelCarUpdate(id, token);
|
||||
|
||||
result = await api.getSUMSVersions(token);
|
||||
if (result.error)
|
||||
throw new Error(`Cancel car update error. ${result.message}`);
|
||||
throw new Error(`Get software versions error. ${result.message}`);
|
||||
|
||||
result.data.unshift(SELECT_VERSION_OBJ)
|
||||
setVersions(result.data);
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
const updateSUMSVersion = async (id, version, token) => {
|
||||
let result;
|
||||
|
||||
try {
|
||||
setBusy(true);
|
||||
|
||||
if (!validateSoftwareVersion(version)) throw new Error(`invalid version ${version}`);
|
||||
|
||||
result = await api.updateSUMSVersion(id, version, token);
|
||||
if (result.error)
|
||||
throw new Error(`Update manifest version error. ${result.message}`);
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
@@ -244,14 +285,17 @@ export const CarUpdatesProvider = ({ children }) => {
|
||||
busy,
|
||||
carUpdates,
|
||||
totalCarUpdates,
|
||||
versions,
|
||||
cancelUpdate,
|
||||
deployCarUpdates,
|
||||
deployFleetUpdates,
|
||||
getCarUpdates,
|
||||
getLog,
|
||||
getSUMSVersions,
|
||||
getVINUpdates,
|
||||
startMonitor,
|
||||
stopMonitor,
|
||||
updateSUMSVersion,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
||||
@@ -60,6 +60,10 @@ let carUpdateLog = {
|
||||
total: 3,
|
||||
};
|
||||
|
||||
let sumsVersions = {
|
||||
"data": ["2023.02.01.0.0.A", "2023.02.01.0.0.B"]
|
||||
}
|
||||
|
||||
export const CarUpdatesProvider = ({ children }) => {
|
||||
return <div data-testid="mocked-carupdatesprovider">{children}</div>;
|
||||
};
|
||||
@@ -77,4 +81,6 @@ export const useCarUpdatesContext = () => ({
|
||||
startMonitor: jest.fn(),
|
||||
stopMonitor: jest.fn(),
|
||||
approveUpdate: jest.fn(),
|
||||
getSUMSVersions: jest.fn(() => sumsVersions),
|
||||
updateSUMSVersion: jest.fn(),
|
||||
});
|
||||
@@ -35,6 +35,10 @@ const tableColumns = [
|
||||
id: "update_package_id",
|
||||
label: "Name",
|
||||
},
|
||||
{
|
||||
id: "",
|
||||
label: "SUMS",
|
||||
},
|
||||
{
|
||||
id: "username",
|
||||
label: "Username",
|
||||
@@ -164,14 +168,17 @@ const MainForm = ({ vin, token }) => {
|
||||
onSortRequest={handleSort}
|
||||
/>
|
||||
<TableBody>
|
||||
{carUpdates.map((row) => (
|
||||
<TableRow key={row.id}>
|
||||
{carUpdates.map((row, index) => (
|
||||
<TableRow key={index}>
|
||||
<TableCell align="center">{row.id}</TableCell>
|
||||
<TableCell align="center">
|
||||
<Link to={`/vehicle-status/${row.vin}/${row.id}`}>
|
||||
{updateName(row)}
|
||||
</Link>
|
||||
</TableCell>
|
||||
<TableCell align="center">
|
||||
{row.updatemanifest?.sums}
|
||||
</TableCell>
|
||||
<TableCell align="center">
|
||||
{row.username}
|
||||
</TableCell>
|
||||
@@ -206,11 +213,11 @@ const MainForm = ({ vin, token }) => {
|
||||
<TableFooter>
|
||||
<TableRow>
|
||||
{totalCarUpdates === 0 ? (
|
||||
<td>No Car Updates found</td>
|
||||
<TableCell colSpan={7} align="center">No Car Updates</TableCell>
|
||||
) : (
|
||||
<TablePagination
|
||||
rowsPerPageOptions={[5, 10, 25, 100]}
|
||||
colSpan={6}
|
||||
colSpan={7}
|
||||
count={totalCarUpdates}
|
||||
rowsPerPage={pageSize}
|
||||
page={pageIndex}
|
||||
|
||||
25
src/components/Controls/DropDownList/index.jsx
Normal file
25
src/components/Controls/DropDownList/index.jsx
Normal file
@@ -0,0 +1,25 @@
|
||||
import { FormControl, InputLabel, Select } from "@material-ui/core";
|
||||
|
||||
export const DropDownList = ({data, label, value, labelField, valueField, onChange, classes, ...others}) => {
|
||||
return (
|
||||
<FormControl
|
||||
variant="outlined"
|
||||
margin="normal"
|
||||
>
|
||||
<InputLabel className={classes.whiteBackground}>
|
||||
{label}
|
||||
</InputLabel>
|
||||
<Select
|
||||
native
|
||||
value={value}
|
||||
variant="outlined"
|
||||
onChange={onChange}
|
||||
{...others}
|
||||
>
|
||||
{data && data.map((item, index) => (
|
||||
<option key={index} value={item[valueField || "value"]}>{item[labelField || "label"]}</option>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
);
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from "react";
|
||||
|
||||
import { LocalDateTimeString } from "../../utils/dates";
|
||||
import { ValidateLocationByParam } from "../../utils/locations"
|
||||
import { ValidateLocationByParam } from "../../utils/locations";
|
||||
import useStyles from "../useStyles";
|
||||
|
||||
const keyValueTemplate = (key, value) => (
|
||||
@@ -58,11 +58,10 @@ const DigitalTwin = (props) => {
|
||||
<div className={classes.popupSection}>
|
||||
<h3>Misc Windows</h3>
|
||||
{Object.entries(misc_windows).map((value) => {
|
||||
if (value[1] === 0) {
|
||||
return keyValueTemplate(value[0], "closed");
|
||||
if (value[1] === 0 || value[1] > 100) {
|
||||
return keyValueTemplate(value[0], `closed ${value[1]}%`);
|
||||
} else {
|
||||
const percentOpen = Math.min(value[1], 100);
|
||||
return keyValueTemplate(value[0], `${percentOpen}% open`);
|
||||
return keyValueTemplate(value[0], `${value[1]}% open`);
|
||||
}
|
||||
})}
|
||||
</div>
|
||||
|
||||
@@ -10,7 +10,7 @@ exports[`Magna Security DLL page Security DLL Result page 1`] = `
|
||||
<strong>
|
||||
Important Note:
|
||||
</strong>
|
||||
Certificates expire in one month from the time they are generated. They have to be re-downloaded again to connect to the Fisker cloud.
|
||||
Certificates expire in 3 months from the time they are generated. They have to be re-downloaded again to connect to the Fisker cloud.
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
|
||||
@@ -9,7 +9,7 @@ const Result = ({ public_key, private_key }) => (
|
||||
<>
|
||||
<form>
|
||||
<p>Please click the links below to download the certificate.pem, key.pem and the security.dll (either the 32-bit or 64-bit version to match your application). Install all files in the same folder, and then run your application of choice to connect to the Fisker cloud.</p>
|
||||
<p><strong>Important Note:</strong> Certificates expire in one month from the time they are generated. They have to be re-downloaded again to connect to the Fisker cloud.</p>
|
||||
<p><strong>Important Note:</strong> Certificates expire in 3 months from the time they are generated. They have to be re-downloaded again to connect to the Fisker cloud.</p>
|
||||
<ul>
|
||||
<li>
|
||||
<DownloadFileLink
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
import { Button, FormControlLabel, Grid, Switch, Typography } from "@material-ui/core";
|
||||
import clsx from "clsx";
|
||||
import SendIcon from "@material-ui/icons/Send";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Redirect, useParams } from "react-router";
|
||||
|
||||
import { logger } from "../../../services/monitoring";
|
||||
import { LocalDateTimeString } from "../../../utils/dates";
|
||||
import { Permissions } from "../../../utils/roles";
|
||||
import { CarUpdatesProvider, useCarUpdatesContext } from "../../Contexts/CarUpdatesContext";
|
||||
import { CarUpdatesProvider, SELECT_VERSION, useCarUpdatesContext } from "../../Contexts/CarUpdatesContext";
|
||||
import { FleetProvider } from "../../Contexts/FleetContext";
|
||||
import { ManifestsProvider, useManifestsContext } from "../../Contexts/ManifestsContext";
|
||||
import { useStatusContext } from "../../Contexts/StatusContext";
|
||||
import { useUserContext } from "../../Contexts/UserContext";
|
||||
import { VehicleProvider } from "../../Contexts/VehicleContext";
|
||||
import CarSelectionTable from "../../Controls/CarSelectionTable";
|
||||
import { DropDownList } from "../../Controls/DropDownList";
|
||||
import FleetSelectionTable from "../../Controls/FleetSelectionTable";
|
||||
import { RoleWrap } from "../../Controls/RoleWrap";
|
||||
import SearchField from "../../Controls/SearchField";
|
||||
@@ -25,7 +26,7 @@ const MainForm = () => {
|
||||
const [updateType, setUpdateType] = useState(CAR_UPDATE);
|
||||
const {manifest_id} = useParams();
|
||||
const {getManifests, manifests, busy} = useManifestsContext();
|
||||
const {deployCarUpdates, deployFleetUpdates} = useCarUpdatesContext();
|
||||
const {deployCarUpdates, deployFleetUpdates, getSUMSVersions, versions, updateSUMSVersion} = useCarUpdatesContext();
|
||||
const {
|
||||
groups,
|
||||
providers,
|
||||
@@ -36,9 +37,11 @@ const MainForm = () => {
|
||||
const {setMessage, setTitle, setSitePath} = useStatusContext();
|
||||
const [manifestName, setManifestName] = useState("");
|
||||
const [version, setVersion] = useState("");
|
||||
const [sumsVersion, setSUMSersion] = useState("");
|
||||
const [createDate, setCreateDate] = useState("");
|
||||
const [selected, setSelected] = useState([]);
|
||||
const [search, setSearch] = useState("");
|
||||
const [softwareVersion, setSoftwareVersion] = useState(SELECT_VERSION);
|
||||
const [redirect, setRedirect] = useState("");
|
||||
const classes = useStyles();
|
||||
|
||||
@@ -77,6 +80,10 @@ const MainForm = () => {
|
||||
const data = {
|
||||
manifest_id: parseInt(manifest_id),
|
||||
}
|
||||
if (sumsVersion.length === 0) {
|
||||
await updateSUMSVersion(manifest_id, softwareVersion, token);
|
||||
}
|
||||
|
||||
if (updateType === CAR_UPDATE) {
|
||||
data.vins = selected;
|
||||
await deployCarUpdates(data, token);
|
||||
@@ -96,13 +103,18 @@ const MainForm = () => {
|
||||
|
||||
const getData = async () => {
|
||||
try {
|
||||
getManifests({id: parseInt(manifest_id)}, token);
|
||||
await getManifests({id: parseInt(manifest_id)}, token);
|
||||
await getSUMSVersions(token);
|
||||
} catch (e) {
|
||||
setMessage(e.message);
|
||||
logger.warn(e.stack);
|
||||
}
|
||||
};
|
||||
|
||||
const changeVersion = (e) => {
|
||||
setSoftwareVersion(e.target.value);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
getData();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
@@ -129,6 +141,7 @@ const MainForm = () => {
|
||||
|
||||
setManifestName(data.name);
|
||||
setVersion(data.version);
|
||||
setSUMSersion(data.sums || "");
|
||||
setCreateDate(LocalDateTimeString(data.created));
|
||||
}, [manifests]);
|
||||
|
||||
@@ -162,16 +175,24 @@ const MainForm = () => {
|
||||
<Grid item md={4} className={classes.textCenterAlign}>
|
||||
<SearchField classes={classes} onSearch={handleSearch}/>
|
||||
</Grid>
|
||||
<Grid item md={4} className={classes.textRightAlign}>
|
||||
<Grid item md={4} container justifyContent="flex-end">
|
||||
{sumsVersion.length === 0 &&
|
||||
<DropDownList
|
||||
label="Software Version"
|
||||
labelField="version"
|
||||
valueField="version"
|
||||
value={softwareVersion}
|
||||
data={versions}
|
||||
classes={classes}
|
||||
onChange={changeVersion} />
|
||||
}
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={busy || selected.length === 0}
|
||||
variant="contained"
|
||||
disabled={busy || selected.length === 0 || (sumsVersion.length === 0 && softwareVersion === SELECT_VERSION)}
|
||||
color="primary"
|
||||
className={clsx(classes.formControl, classes.textField)}
|
||||
onClick={onSubmit}
|
||||
>
|
||||
{busy ? "Deploying..." : "Deploy"}
|
||||
<SendIcon />
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
@@ -6,15 +6,15 @@ import {
|
||||
TableFooter,
|
||||
TablePagination,
|
||||
TableRow,
|
||||
Tooltip,
|
||||
Tooltip
|
||||
} from "@material-ui/core";
|
||||
import {
|
||||
ToggleButton,
|
||||
ToggleButtonGroup
|
||||
} from "@mui/material"
|
||||
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";
|
||||
@@ -26,7 +26,7 @@ import { TYPE_MANIFEST_SOFTWARE } from "../../../utils/manifest_types";
|
||||
import { hasRole, Permissions } from "../../../utils/roles";
|
||||
import {
|
||||
ManifestsProvider,
|
||||
useManifestsContext,
|
||||
useManifestsContext
|
||||
} from "../../Contexts/ManifestsContext";
|
||||
import { useStatusContext } from "../../Contexts/StatusContext";
|
||||
import { useUserContext } from "../../Contexts/UserContext";
|
||||
@@ -50,6 +50,10 @@ const tableColumns = [
|
||||
id: "version",
|
||||
label: "Version",
|
||||
},
|
||||
{
|
||||
id: "sums",
|
||||
label: "SUMS",
|
||||
},
|
||||
{
|
||||
id: "type",
|
||||
label: "Type",
|
||||
@@ -280,6 +284,7 @@ const MainForm = () => {
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell align="center">{row.version}</TableCell>
|
||||
<TableCell align="center">{row.sums}</TableCell>
|
||||
<TableCell align="center">
|
||||
{formatManifestType(row.type)}
|
||||
</TableCell>
|
||||
@@ -297,7 +302,7 @@ const MainForm = () => {
|
||||
<TableRow>
|
||||
<TablePagination
|
||||
rowsPerPageOptions={[5, 10, 25, 100]}
|
||||
colSpan={6}
|
||||
colSpan={8}
|
||||
count={totalManifests}
|
||||
rowsPerPage={pageSize}
|
||||
page={pageIndex}
|
||||
|
||||
@@ -113,12 +113,11 @@ exports[`Manifest Details Component Render 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="MuiFormControl-root makeStyles-form-0 MuiFormControl-marginNormal MuiFormControl-fullWidth"
|
||||
class="MuiFormControl-root MuiFormControl-marginNormal"
|
||||
>
|
||||
<label
|
||||
class="MuiFormLabel-root MuiInputLabel-root makeStyles-whiteBackground-0 MuiInputLabel-formControl MuiInputLabel-animated MuiInputLabel-outlined"
|
||||
data-shrink="false"
|
||||
for="manifest-type"
|
||||
>
|
||||
Type
|
||||
</label>
|
||||
@@ -128,8 +127,6 @@ exports[`Manifest Details Component Render 1`] = `
|
||||
<select
|
||||
aria-invalid="false"
|
||||
class="MuiSelect-root MuiSelect-select MuiSelect-outlined MuiInputBase-input MuiOutlinedInput-input"
|
||||
id="send-manifest-type"
|
||||
name="manifest-type"
|
||||
>
|
||||
<option
|
||||
value="standard"
|
||||
@@ -169,14 +166,13 @@ exports[`Manifest Details Component Render 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="MuiFormControl-root makeStyles-form-0 MuiFormControl-marginNormal MuiFormControl-fullWidth"
|
||||
class="MuiFormControl-root MuiFormControl-marginNormal"
|
||||
>
|
||||
<label
|
||||
class="MuiFormLabel-root MuiInputLabel-root makeStyles-whiteBackground-0 MuiInputLabel-formControl MuiInputLabel-animated MuiInputLabel-shrink MuiInputLabel-outlined MuiFormLabel-filled"
|
||||
data-shrink="true"
|
||||
for="manifest-active"
|
||||
>
|
||||
Type
|
||||
Active
|
||||
</label>
|
||||
<div
|
||||
class="MuiInputBase-root MuiOutlinedInput-root MuiInputBase-formControl"
|
||||
@@ -184,18 +180,16 @@ exports[`Manifest Details Component Render 1`] = `
|
||||
<select
|
||||
aria-invalid="false"
|
||||
class="MuiSelect-root MuiSelect-select MuiSelect-outlined MuiInputBase-input MuiOutlinedInput-input"
|
||||
id="send-manifest-active"
|
||||
name="manifest-active"
|
||||
>
|
||||
<option
|
||||
value="true"
|
||||
>
|
||||
active
|
||||
Active
|
||||
</option>
|
||||
<option
|
||||
value="false"
|
||||
>
|
||||
archived
|
||||
Archived
|
||||
</option>
|
||||
</select>
|
||||
<svg
|
||||
|
||||
@@ -1,17 +1,23 @@
|
||||
import { Button, FormControl, TextField } from "@material-ui/core";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { Redirect } from "react-router";
|
||||
import useStyles from "../../useStyles";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { ManifestsProvider, useManifestsContext } from "../../Contexts/ManifestsContext";
|
||||
import { useUserContext } from "../../Contexts/UserContext";
|
||||
import { useStatusContext } from "../../Contexts/StatusContext";
|
||||
import { Button, FormControl, InputLabel, Select, TextField } from "@material-ui/core";
|
||||
import { useUserContext } from "../../Contexts/UserContext";
|
||||
import { DropDownList } from "../../Controls/DropDownList";
|
||||
import useStyles from "../../useStyles";
|
||||
|
||||
const manifestTypes = [
|
||||
{ value: "standard", label: "Standard" },
|
||||
{ value: "forced", label: "Forced" },
|
||||
];
|
||||
|
||||
const activeStates = [
|
||||
{value: true, label: "Active" },
|
||||
{value: false, label: "Archived" },
|
||||
];
|
||||
|
||||
const emptyManifest = {
|
||||
name: "",
|
||||
version: "",
|
||||
@@ -138,53 +144,8 @@ const MainForm = () => {
|
||||
fullWidth
|
||||
onChange={changeName}
|
||||
/>
|
||||
<FormControl
|
||||
className={classes.form}
|
||||
variant="outlined"
|
||||
fullWidth
|
||||
margin="normal"
|
||||
>
|
||||
<InputLabel htmlFor="manifest-type" className={classes.whiteBackground}>
|
||||
Type
|
||||
</InputLabel>
|
||||
<Select
|
||||
native
|
||||
value={type}
|
||||
variant="outlined"
|
||||
inputProps={{
|
||||
name: "manifest-type",
|
||||
id: "send-manifest-type",
|
||||
}}
|
||||
onChange={changeType}
|
||||
>
|
||||
{manifestTypes.map((item, index) => (
|
||||
<option key={index} value={item.value}>{item.label}</option>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
<FormControl
|
||||
className={classes.form}
|
||||
variant="outlined"
|
||||
fullWidth
|
||||
margin="normal"
|
||||
>
|
||||
<InputLabel htmlFor="manifest-active" className={classes.whiteBackground}>
|
||||
Type
|
||||
</InputLabel>
|
||||
<Select
|
||||
native
|
||||
value={active}
|
||||
variant="outlined"
|
||||
inputProps={{
|
||||
name: "manifest-active",
|
||||
id: "send-manifest-active",
|
||||
}}
|
||||
onChange={changeActive}
|
||||
>
|
||||
<option key={0} value={true}>active</option>
|
||||
<option key={1} value={false}>archived</option>
|
||||
</Select>
|
||||
</FormControl>
|
||||
<DropDownList label="Type" data={manifestTypes} classes={classes} onChange={changeType} value={type} />
|
||||
<DropDownList label="Active" data={activeStates} classes={classes} onChange={changeActive} value={active}/>
|
||||
<Button
|
||||
type="submit"
|
||||
aria-label="send command"
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import {
|
||||
Checkbox,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableRow,
|
||||
TableSortLabel,
|
||||
TableSortLabel
|
||||
} from "@material-ui/core";
|
||||
import PropTypes from "prop-types";
|
||||
import React from "react";
|
||||
|
||||
const HeaderSortable = (props) => {
|
||||
const {
|
||||
@@ -75,9 +75,9 @@ const HeaderSortable = (props) => {
|
||||
/>
|
||||
</TableCell>
|
||||
)}
|
||||
{columnData.map((column) => (
|
||||
{columnData.map((column, index) => (
|
||||
<TableCell
|
||||
key={column.id}
|
||||
key={index}
|
||||
align={column.numeric ? "right" : "center"}
|
||||
padding={column.disablePadding ? "none" : "normal"}
|
||||
sortDirection={orderBy === column.id ? order : false}
|
||||
|
||||
@@ -26,6 +26,16 @@ const updatesAPI = {
|
||||
cancelCarUpdate: async (id, token) => {
|
||||
return { message: "OK" };
|
||||
},
|
||||
|
||||
getSUMSVersions: async (token) => {
|
||||
return {
|
||||
"data": ["2023.02.01.0.0.A", "2023.02.01.0.0.B"]
|
||||
};
|
||||
},
|
||||
|
||||
updateSUMSVersion: async (_id, version) => {
|
||||
return { version };
|
||||
},
|
||||
};
|
||||
|
||||
export default updatesAPI;
|
||||
|
||||
@@ -86,6 +86,31 @@ const updatesAPI = {
|
||||
.then(fetchRespHandler)
|
||||
.catch(errorHandler);
|
||||
},
|
||||
|
||||
getSUMSVersions: async (token) => {
|
||||
return fetch(`${API_ENDPOINT}/manifest/sums`, {
|
||||
method: "GET",
|
||||
headers: Object.assign(
|
||||
{ "Content-Type": "application/json" },
|
||||
getAuthHeaderOptions(token)
|
||||
),
|
||||
})
|
||||
.then(fetchRespHandler)
|
||||
.catch(errorHandler);
|
||||
},
|
||||
|
||||
updateSUMSVersion: async (id, version, token) => {
|
||||
return fetch(`${API_ENDPOINT}/manifests/${id}/sums`, {
|
||||
method: "PUT",
|
||||
headers: Object.assign(
|
||||
{ "Content-Type": "application/json" },
|
||||
getAuthHeaderOptions(token),
|
||||
),
|
||||
body: JSON.stringify({ version }),
|
||||
})
|
||||
.then(fetchRespHandler)
|
||||
.catch(errorHandler);
|
||||
}
|
||||
};
|
||||
|
||||
export default updatesAPI;
|
||||
5
src/utils/softwareVersions.js
Normal file
5
src/utils/softwareVersions.js
Normal file
@@ -0,0 +1,5 @@
|
||||
const rxSoftwareVersion = /^\d{4}\.(0[1-9]|1[0-2])\.\d{2}\.\d{2}(\.[\d\w]{1})?$/i;
|
||||
|
||||
export const validateSoftwareVersion = (version) => {
|
||||
return rxSoftwareVersion.test(version);
|
||||
}
|
||||
19
src/utils/softwareVersions.test.js
Normal file
19
src/utils/softwareVersions.test.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import { validateSoftwareVersion } from "./softwareVersions";
|
||||
|
||||
describe("Software versions", () => {
|
||||
it("validation", () =>{
|
||||
expect(validateSoftwareVersion("2023.12.01.01.A")).toEqual(true);
|
||||
expect(validateSoftwareVersion("2023.10.01.01.A")).toEqual(true);
|
||||
expect(validateSoftwareVersion("2023.09.01.01.A")).toEqual(true);
|
||||
expect(validateSoftwareVersion("2023.13.01.01.A")).toEqual(false);
|
||||
expect(validateSoftwareVersion("2023.12.01.01")).toEqual(true);
|
||||
expect(validateSoftwareVersion("2023.10.01.01")).toEqual(true);
|
||||
expect(validateSoftwareVersion("2023.09.01.01")).toEqual(true);
|
||||
expect(validateSoftwareVersion("2023.13.01.01")).toEqual(false);
|
||||
expect(validateSoftwareVersion("2023.12.01")).toEqual(false);
|
||||
expect(validateSoftwareVersion("2023.10.AA.01")).toEqual(false);
|
||||
expect(validateSoftwareVersion("2023.09.01.AA")).toEqual(false);
|
||||
expect(validateSoftwareVersion("202A.09.01.01")).toEqual(false);
|
||||
expect(validateSoftwareVersion("2023.1A.01.01")).toEqual(false);
|
||||
})
|
||||
});
|
||||
Reference in New Issue
Block a user