CEC-179 Car download progress (#32)
* Display download progress * Change default * Fix * Fix * Update readme * Update readme and defaults Fix Dockerfile
This commit is contained in:
@@ -99,7 +99,7 @@ describe("App", () => {
|
||||
|
||||
it("Route /package-upload authenticated", async () => {
|
||||
setToken(TEST_AUTH_OBJECT);
|
||||
await check("/package-upload", "h1", "Upload Update Package");
|
||||
await check("/package-upload", "h1", "Create Update Package");
|
||||
});
|
||||
|
||||
it("Route /vehicle-add authenticated", async () => {
|
||||
@@ -109,17 +109,17 @@ describe("App", () => {
|
||||
|
||||
it("Route /updates authenticated", async () => {
|
||||
setToken(TEST_AUTH_OBJECT);
|
||||
await check("/updates", "h1", "Updates");
|
||||
await check("/updates", "h1", "Update Packages");
|
||||
});
|
||||
|
||||
it("Route /update authenticated", async () => {
|
||||
setToken(TEST_AUTH_OBJECT);
|
||||
await check("/update/1", "h1", "Update Package 1");
|
||||
await check("/update/1", "h1", "Edit Update Package 1");
|
||||
});
|
||||
|
||||
it("Route /carupdate-deploy authenticated", async () => {
|
||||
setToken(TEST_AUTH_OBJECT);
|
||||
await check("/carupdate-deploy/1", "h1", "[1] ");
|
||||
await check("/carupdate-deploy/1", "h1", "Deploy [1]");
|
||||
});
|
||||
|
||||
it("Route /carupdate-status authenticated", async () => {
|
||||
|
||||
@@ -134,7 +134,7 @@ exports[`App Route / authenticated 1`] = `
|
||||
<span
|
||||
class="MuiTypography-root MuiListItemText-primary MuiTypography-body1 MuiTypography-displayBlock"
|
||||
>
|
||||
View Updates
|
||||
View Packages
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
@@ -156,7 +156,7 @@ exports[`App Route / authenticated 1`] = `
|
||||
<span
|
||||
class="MuiTypography-root MuiListItemText-primary MuiTypography-body1 MuiTypography-displayBlock"
|
||||
>
|
||||
Create Updates
|
||||
Create Packages
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
@@ -427,7 +427,7 @@ exports[`App Route /carupdate-deploy authenticated 1`] = `
|
||||
<span
|
||||
class="MuiTypography-root MuiListItemText-primary MuiTypography-body1 MuiTypography-displayBlock"
|
||||
>
|
||||
View Updates
|
||||
View Packages
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
@@ -449,7 +449,7 @@ exports[`App Route /carupdate-deploy authenticated 1`] = `
|
||||
<span
|
||||
class="MuiTypography-root MuiListItemText-primary MuiTypography-body1 MuiTypography-displayBlock"
|
||||
>
|
||||
Create Updates
|
||||
Create Packages
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
@@ -525,7 +525,8 @@ exports[`App Route /carupdate-deploy authenticated 1`] = `
|
||||
<h1
|
||||
class="MuiTypography-root MuiTypography-h5"
|
||||
>
|
||||
[1]
|
||||
Deploy
|
||||
[1]
|
||||
</h1>
|
||||
<form
|
||||
action="{onSubmit}"
|
||||
@@ -891,7 +892,7 @@ exports[`App Route /carupdate-status authenticated 1`] = `
|
||||
<span
|
||||
class="MuiTypography-root MuiListItemText-primary MuiTypography-body1 MuiTypography-displayBlock"
|
||||
>
|
||||
View Updates
|
||||
View Packages
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
@@ -913,7 +914,7 @@ exports[`App Route /carupdate-status authenticated 1`] = `
|
||||
<span
|
||||
class="MuiTypography-root MuiListItemText-primary MuiTypography-body1 MuiTypography-displayBlock"
|
||||
>
|
||||
Create Updates
|
||||
Create Packages
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
@@ -1356,7 +1357,7 @@ exports[`App Route /home authenticated 1`] = `
|
||||
<span
|
||||
class="MuiTypography-root MuiListItemText-primary MuiTypography-body1 MuiTypography-displayBlock"
|
||||
>
|
||||
View Updates
|
||||
View Packages
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
@@ -1378,7 +1379,7 @@ exports[`App Route /home authenticated 1`] = `
|
||||
<span
|
||||
class="MuiTypography-root MuiListItemText-primary MuiTypography-body1 MuiTypography-displayBlock"
|
||||
>
|
||||
Create Updates
|
||||
Create Packages
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
@@ -1649,7 +1650,7 @@ exports[`App Route /package-upload authenticated 1`] = `
|
||||
<span
|
||||
class="MuiTypography-root MuiListItemText-primary MuiTypography-body1 MuiTypography-displayBlock"
|
||||
>
|
||||
View Updates
|
||||
View Packages
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
@@ -1671,7 +1672,7 @@ exports[`App Route /package-upload authenticated 1`] = `
|
||||
<span
|
||||
class="MuiTypography-root MuiListItemText-primary MuiTypography-body1 MuiTypography-displayBlock"
|
||||
>
|
||||
Create Updates
|
||||
Create Packages
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
@@ -1744,7 +1745,7 @@ exports[`App Route /package-upload authenticated 1`] = `
|
||||
<h1
|
||||
class="MuiTypography-root MuiTypography-h5"
|
||||
>
|
||||
Upload Update Package
|
||||
Create Update Package
|
||||
</h1>
|
||||
<form
|
||||
action="{onSubmit}"
|
||||
@@ -1847,12 +1848,19 @@ exports[`App Route /package-upload authenticated 1`] = `
|
||||
class="MuiFormControl-root MuiTextField-root MuiFormControl-marginNormal MuiFormControl-fullWidth"
|
||||
>
|
||||
<label
|
||||
class="MuiFormLabel-root MuiInputLabel-root MuiInputLabel-formControl MuiInputLabel-animated MuiInputLabel-outlined"
|
||||
class="MuiFormLabel-root MuiInputLabel-root MuiInputLabel-formControl MuiInputLabel-animated MuiInputLabel-outlined Mui-required Mui-required"
|
||||
data-shrink="false"
|
||||
for="description"
|
||||
id="description-label"
|
||||
>
|
||||
Description
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="MuiFormLabel-asterisk MuiInputLabel-asterisk"
|
||||
>
|
||||
|
||||
*
|
||||
</span>
|
||||
</label>
|
||||
<div
|
||||
class="MuiInputBase-root MuiOutlinedInput-root MuiInputBase-fullWidth MuiInputBase-formControl MuiInputBase-multiline MuiOutlinedInput-multiline"
|
||||
@@ -1864,6 +1872,7 @@ exports[`App Route /package-upload authenticated 1`] = `
|
||||
maxlength="5120"
|
||||
name="description"
|
||||
placeholder="Package description"
|
||||
required=""
|
||||
rows="4"
|
||||
/>
|
||||
<fieldset
|
||||
@@ -1875,6 +1884,7 @@ exports[`App Route /package-upload authenticated 1`] = `
|
||||
>
|
||||
<span>
|
||||
Description
|
||||
*
|
||||
</span>
|
||||
</legend>
|
||||
</fieldset>
|
||||
@@ -1884,12 +1894,19 @@ exports[`App Route /package-upload authenticated 1`] = `
|
||||
class="MuiFormControl-root MuiTextField-root MuiFormControl-marginNormal MuiFormControl-fullWidth"
|
||||
>
|
||||
<label
|
||||
class="MuiFormLabel-root MuiInputLabel-root MuiInputLabel-formControl MuiInputLabel-animated MuiInputLabel-outlined"
|
||||
class="MuiFormLabel-root MuiInputLabel-root MuiInputLabel-formControl MuiInputLabel-animated MuiInputLabel-outlined Mui-required Mui-required"
|
||||
data-shrink="false"
|
||||
for="releasenotes"
|
||||
id="releasenotes-label"
|
||||
>
|
||||
Release Notes URL
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="MuiFormLabel-asterisk MuiInputLabel-asterisk"
|
||||
>
|
||||
|
||||
*
|
||||
</span>
|
||||
</label>
|
||||
<div
|
||||
class="MuiInputBase-root MuiOutlinedInput-root MuiInputBase-fullWidth MuiInputBase-formControl"
|
||||
@@ -1901,6 +1918,7 @@ exports[`App Route /package-upload authenticated 1`] = `
|
||||
maxlength="1024"
|
||||
name="releasenotes"
|
||||
placeholder="Release Notes URL"
|
||||
required=""
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
@@ -1913,6 +1931,7 @@ exports[`App Route /package-upload authenticated 1`] = `
|
||||
>
|
||||
<span>
|
||||
Release Notes URL
|
||||
*
|
||||
</span>
|
||||
</legend>
|
||||
</fieldset>
|
||||
@@ -2164,7 +2183,7 @@ exports[`App Route /page-not-found authenticated 1`] = `
|
||||
<span
|
||||
class="MuiTypography-root MuiListItemText-primary MuiTypography-body1 MuiTypography-displayBlock"
|
||||
>
|
||||
View Updates
|
||||
View Packages
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
@@ -2186,7 +2205,7 @@ exports[`App Route /page-not-found authenticated 1`] = `
|
||||
<span
|
||||
class="MuiTypography-root MuiListItemText-primary MuiTypography-body1 MuiTypography-displayBlock"
|
||||
>
|
||||
Create Updates
|
||||
Create Packages
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
@@ -2446,7 +2465,7 @@ exports[`App Route /update authenticated 1`] = `
|
||||
<span
|
||||
class="MuiTypography-root MuiListItemText-primary MuiTypography-body1 MuiTypography-displayBlock"
|
||||
>
|
||||
View Updates
|
||||
View Packages
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
@@ -2468,7 +2487,7 @@ exports[`App Route /update authenticated 1`] = `
|
||||
<span
|
||||
class="MuiTypography-root MuiListItemText-primary MuiTypography-body1 MuiTypography-displayBlock"
|
||||
>
|
||||
Create Updates
|
||||
Create Packages
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
@@ -2541,7 +2560,7 @@ exports[`App Route /update authenticated 1`] = `
|
||||
<h1
|
||||
class="MuiTypography-root MuiTypography-h5"
|
||||
>
|
||||
Update Package
|
||||
Edit Update Package
|
||||
1
|
||||
</h1>
|
||||
<form
|
||||
@@ -2999,7 +3018,7 @@ exports[`App Route /updates authenticated 1`] = `
|
||||
<span
|
||||
class="MuiTypography-root MuiListItemText-primary MuiTypography-body1 MuiTypography-displayBlock"
|
||||
>
|
||||
View Updates
|
||||
View Packages
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
@@ -3021,7 +3040,7 @@ exports[`App Route /updates authenticated 1`] = `
|
||||
<span
|
||||
class="MuiTypography-root MuiListItemText-primary MuiTypography-body1 MuiTypography-displayBlock"
|
||||
>
|
||||
Create Updates
|
||||
Create Packages
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
@@ -3095,7 +3114,7 @@ exports[`App Route /updates authenticated 1`] = `
|
||||
<h1
|
||||
class="MuiTypography-root MuiTypography-h5"
|
||||
>
|
||||
Updates
|
||||
Update Packages
|
||||
</h1>
|
||||
<div
|
||||
class="MuiTableContainer-root"
|
||||
@@ -3465,7 +3484,7 @@ exports[`App Route /vehicle-add authenticated 1`] = `
|
||||
<span
|
||||
class="MuiTypography-root MuiListItemText-primary MuiTypography-body1 MuiTypography-displayBlock"
|
||||
>
|
||||
View Updates
|
||||
View Packages
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
@@ -3487,7 +3506,7 @@ exports[`App Route /vehicle-add authenticated 1`] = `
|
||||
<span
|
||||
class="MuiTypography-root MuiListItemText-primary MuiTypography-body1 MuiTypography-displayBlock"
|
||||
>
|
||||
Create Updates
|
||||
Create Packages
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
@@ -3921,7 +3940,7 @@ exports[`App Route /vehicle-status authenticated 1`] = `
|
||||
<span
|
||||
class="MuiTypography-root MuiListItemText-primary MuiTypography-body1 MuiTypography-displayBlock"
|
||||
>
|
||||
View Updates
|
||||
View Packages
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
@@ -3943,7 +3962,7 @@ exports[`App Route /vehicle-status authenticated 1`] = `
|
||||
<span
|
||||
class="MuiTypography-root MuiListItemText-primary MuiTypography-body1 MuiTypography-displayBlock"
|
||||
>
|
||||
Create Updates
|
||||
Create Packages
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
@@ -4388,7 +4407,7 @@ exports[`App Route /vehicles authenticated 1`] = `
|
||||
<span
|
||||
class="MuiTypography-root MuiListItemText-primary MuiTypography-body1 MuiTypography-displayBlock"
|
||||
>
|
||||
View Updates
|
||||
View Packages
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
@@ -4410,7 +4429,7 @@ exports[`App Route /vehicles authenticated 1`] = `
|
||||
<span
|
||||
class="MuiTypography-root MuiListItemText-primary MuiTypography-body1 MuiTypography-displayBlock"
|
||||
>
|
||||
Create Updates
|
||||
Create Packages
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useParams } from "react-router";
|
||||
import { useParams, Redirect } from "react-router";
|
||||
import {
|
||||
Button,
|
||||
Chip,
|
||||
@@ -44,6 +44,7 @@ const MainForm = () => {
|
||||
const [releaseNotesLink, setReleaseNotesLink] = useState("");
|
||||
const [createDate, setCreateDate] = useState("");
|
||||
const [selectedVehicles, setSelectedVehicles] = useState([]);
|
||||
const [redirect, setRedirect] = useState("");
|
||||
const classes = useStyles();
|
||||
const theme = useTheme();
|
||||
const handleVehiclesChange = (event) => {
|
||||
@@ -60,7 +61,7 @@ const MainForm = () => {
|
||||
setMessage(
|
||||
`Deployed ${packageName} ${version} to ${selectedVehicles.length} cars`
|
||||
);
|
||||
setSelectedVehicles([]);
|
||||
setRedirect(`/carupdate-status/${packageid}`);
|
||||
} catch (e) {
|
||||
setMessage(e.message);
|
||||
}
|
||||
@@ -95,10 +96,14 @@ const MainForm = () => {
|
||||
setCreateDate(tsLocalDateTimeString(data.timestamp));
|
||||
}, [packages]);
|
||||
|
||||
if (redirect.length > 0) {
|
||||
return <Redirect to={redirect} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={classes.paper}>
|
||||
<Typography component="h1" variant="h5">
|
||||
{`[${packageid}] ${packageName} ${version}`}
|
||||
Deploy {`${packageName} ${version} [${packageid}]`}
|
||||
</Typography>
|
||||
<form className={classes.form} noValidate action="{onSubmit}">
|
||||
<TextField
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useParams } from "react-router";
|
||||
import {
|
||||
LinearProgress,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
@@ -25,16 +26,17 @@ import VehicleStatus from "../../Cars/StatusModal";
|
||||
const MainForm = () => {
|
||||
const { packageid } = useParams();
|
||||
const classes = useStyles();
|
||||
const [pageSize, setPageSize] = useState(10);
|
||||
const [pageSize, setPageSize] = useState(25);
|
||||
const [pageIndex, setPageIndex] = useState(0);
|
||||
const [viewVIN, setViewVIN] = useState(null);
|
||||
|
||||
const {
|
||||
getCarUpdates,
|
||||
carUpdates,
|
||||
totalCarUpdates,
|
||||
getPackages,
|
||||
packages,
|
||||
startMonitor,
|
||||
stopMonitor,
|
||||
} = useUpdatesContext();
|
||||
const { setMessage } = useStatusContext();
|
||||
const {
|
||||
@@ -54,6 +56,7 @@ const MainForm = () => {
|
||||
|
||||
useEffect(() => {
|
||||
try {
|
||||
stopMonitor();
|
||||
getCarUpdates(
|
||||
{
|
||||
packageid,
|
||||
@@ -68,6 +71,19 @@ const MainForm = () => {
|
||||
// eslint-disable-next-line
|
||||
}, [pageIndex, pageSize, token]);
|
||||
|
||||
useEffect(() => {
|
||||
try {
|
||||
if (carUpdates.length === 0) return;
|
||||
startMonitor(token);
|
||||
} catch (e) {
|
||||
setMessage(e.message);
|
||||
}
|
||||
return () => {
|
||||
stopMonitor();
|
||||
};
|
||||
// eslint-disable-next-line
|
||||
}, [carUpdates]);
|
||||
|
||||
const handleChangePageIndex = (event, newIndex) => {
|
||||
setPageIndex(newIndex);
|
||||
};
|
||||
@@ -113,7 +129,15 @@ const MainForm = () => {
|
||||
{row.vin}
|
||||
</span>
|
||||
</TableCell>
|
||||
<TableCell align="center">{row.status}</TableCell>
|
||||
<TableCell align="center">
|
||||
{row.status}
|
||||
{row.progress > 0 && (
|
||||
<LinearProgress
|
||||
variant="determinate"
|
||||
value={row.progress}
|
||||
/>
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell align="center">
|
||||
{LocalDateTimeString(row.created)}
|
||||
</TableCell>
|
||||
|
||||
@@ -23,7 +23,7 @@ import { LocalDateTimeString } from "../../../utils/dates";
|
||||
|
||||
const MainForm = () => {
|
||||
const classes = useStyles();
|
||||
const [pageSize, setPageSize] = useState(5);
|
||||
const [pageSize, setPageSize] = useState(25);
|
||||
const [pageIndex, setPageIndex] = useState(0);
|
||||
const { getVehicles, vehicles, totalVehicles } = useVehicleContext();
|
||||
const { setMessage } = useStatusContext();
|
||||
|
||||
@@ -8,7 +8,6 @@ export const FileUploadProvider = ({ children }) => {
|
||||
const [progress, setProgress] = useState(0);
|
||||
const [status, setStatus] = useState(null);
|
||||
const [cancelUpload, setCancelUpload] = useState(null);
|
||||
const [linkURL, setLinkURL] = useState(null);
|
||||
const [files, setFiles] = useState(null);
|
||||
|
||||
const done = () => {
|
||||
@@ -45,6 +44,14 @@ export const FileUploadProvider = ({ children }) => {
|
||||
if (!accessToken || accessToken.length === 0) {
|
||||
throw new Error("Access token required");
|
||||
}
|
||||
|
||||
if (!formData.description || formData.description.length === 0) {
|
||||
throw new Error("Package update description required");
|
||||
}
|
||||
|
||||
if (!formData.releasenotes || formData.releasenotes.length === 0) {
|
||||
throw new Error("Package update release notes link required");
|
||||
}
|
||||
};
|
||||
|
||||
const upload = async (formData, accessToken, uploadFiles) => {
|
||||
@@ -55,7 +62,6 @@ export const FileUploadProvider = ({ children }) => {
|
||||
const filename = file.name;
|
||||
|
||||
setUploading(true);
|
||||
setLinkURL(null);
|
||||
setProgress(0);
|
||||
setStatus(`Uploading ${filename}`);
|
||||
setCancelUpload(getCancelToken());
|
||||
@@ -70,11 +76,11 @@ export const FileUploadProvider = ({ children }) => {
|
||||
if (data.message) {
|
||||
throw new Error(`${data.error}. ${data.message}`);
|
||||
}
|
||||
const url = data && data.link ? data.link : "No URL available";
|
||||
setLinkURL(url);
|
||||
setStatus(`Uploaded ${filename}`);
|
||||
setCancelUpload(null);
|
||||
setProgress(100);
|
||||
|
||||
return data;
|
||||
} catch (e) {
|
||||
setUploading(true);
|
||||
setStatus(`Error occured: ${e.message}`);
|
||||
@@ -88,7 +94,6 @@ export const FileUploadProvider = ({ children }) => {
|
||||
uploading,
|
||||
progress,
|
||||
status,
|
||||
linkURL,
|
||||
files,
|
||||
upload,
|
||||
cancel,
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
fireEvent,
|
||||
waitFor,
|
||||
} from "@testing-library/react";
|
||||
import { useState } from "react";
|
||||
|
||||
import { setUploadFileDelay } from "../../services/uploadFile";
|
||||
import {
|
||||
@@ -30,21 +31,26 @@ describe("FileUploadContext", () => {
|
||||
progress,
|
||||
uploading,
|
||||
status,
|
||||
linkURL,
|
||||
upload,
|
||||
cancel,
|
||||
} = useFileUploadContext();
|
||||
const { message, setMessage } = useStatusContext();
|
||||
const [link, setLink] = useState(null);
|
||||
const TEST_FILE = [{ name: "test.jpg", size: 0 }];
|
||||
const TEST_ACCESSTOKEN = "ACCESSTOKEN";
|
||||
const TEST_FORMDATA = {
|
||||
packagename: "TEST",
|
||||
version: "VERSION",
|
||||
vehicles: ["VIN"],
|
||||
description: "TEST DESC",
|
||||
releasenotes: "http://releasenotes.com",
|
||||
};
|
||||
const exec = async (form, token, file) => {
|
||||
try {
|
||||
await upload(form, token, file);
|
||||
const data = await upload(form, token, file);
|
||||
if (data.link) {
|
||||
setLink(data.link);
|
||||
}
|
||||
} catch (e) {
|
||||
setMessage(e.message);
|
||||
}
|
||||
@@ -56,7 +62,7 @@ describe("FileUploadContext", () => {
|
||||
<div data-testid="progress">{progress.toString()}</div>
|
||||
<div data-testid="status">{status}</div>
|
||||
<div data-testid="message">{message}</div>
|
||||
<div data-testid="linkURL">{linkURL}</div>
|
||||
<div data-testid="linkURL">{link}</div>
|
||||
<button
|
||||
data-testid="uploadNoFile"
|
||||
onClick={() => {
|
||||
|
||||
@@ -10,6 +10,8 @@ export const UpdatesProvider = ({ children }) => {
|
||||
const [carUpdates, setCarUpdates] = useState([]);
|
||||
const [totalPackages, setTotalPackages] = useState(0);
|
||||
const [totalCarUpdates, setTotalCarUpdates] = useState(0);
|
||||
const [delayCount, setDelayCount] = useState(0);
|
||||
let progressTimer = 0;
|
||||
|
||||
const getPackages = async (search, token) => {
|
||||
let result;
|
||||
@@ -96,6 +98,86 @@ export const UpdatesProvider = ({ children }) => {
|
||||
return result;
|
||||
};
|
||||
|
||||
const applyProgressStatus = (item, status) => {
|
||||
if (status.msg === "DONE") {
|
||||
delete item.progress;
|
||||
item.status = "downloaded";
|
||||
} else if (status.msg === "downloading" && status.total > 0) {
|
||||
let progress = Math.floor((100 * status.bytes) / status.total);
|
||||
if (progress > 99) progress = 0;
|
||||
item.progress = progress;
|
||||
item.status = `downloading ${progress}%`;
|
||||
} else if (status.error > 0) {
|
||||
item.status = "download error";
|
||||
} else {
|
||||
item.status = "downloading";
|
||||
}
|
||||
};
|
||||
|
||||
const applyProgressStatuses = (statuses) => {
|
||||
let items = JSON.parse(JSON.stringify(carUpdates));
|
||||
|
||||
statuses.forEach((status) => {
|
||||
let item = items.find((item) => status.id === item.id);
|
||||
if (!item || status.id === 0) return;
|
||||
applyProgressStatus(item, status);
|
||||
});
|
||||
|
||||
setCarUpdates(items);
|
||||
};
|
||||
|
||||
const updateStatusProgress = async (token) => {
|
||||
stopMonitor();
|
||||
|
||||
if (!token || carUpdates.length === 0) return;
|
||||
|
||||
try {
|
||||
setBusy(true);
|
||||
const carupdateids = carUpdates.reduce((accum, update) => {
|
||||
if (update.status !== "downloaded") accum.push(update.id);
|
||||
return accum;
|
||||
}, []);
|
||||
if (carupdateids.length === 0) return;
|
||||
|
||||
const result = await api.getCarUpdateProgress(
|
||||
carupdateids.join(","),
|
||||
token
|
||||
);
|
||||
if (result.error)
|
||||
throw new Error(`Get update progress error. ${result.message}`);
|
||||
|
||||
applyProgressStatuses(result.statuses);
|
||||
} catch (e) {
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
};
|
||||
|
||||
const getDelay = () => {
|
||||
if (delayCount < 3) {
|
||||
setDelayCount(delayCount + 1);
|
||||
return 1000;
|
||||
}
|
||||
for (let i = 0, len = carUpdates.length; i < len; i++) {
|
||||
if (carUpdates[i].status.indexOf("downloading") > -1) return 1000;
|
||||
}
|
||||
return 10000;
|
||||
};
|
||||
|
||||
const startMonitor = async (token) => {
|
||||
const delay = getDelay();
|
||||
stopMonitor();
|
||||
progressTimer = setTimeout(() => {
|
||||
updateStatusProgress(token);
|
||||
}, delay);
|
||||
};
|
||||
|
||||
const stopMonitor = async () => {
|
||||
if (progressTimer === 0) return;
|
||||
clearTimeout(progressTimer);
|
||||
progressTimer = 0;
|
||||
};
|
||||
|
||||
return (
|
||||
<UpdatesContext.Provider
|
||||
value={{
|
||||
@@ -109,6 +191,8 @@ export const UpdatesProvider = ({ children }) => {
|
||||
createCarUpdates,
|
||||
getCarUpdates,
|
||||
getVINUpdates,
|
||||
startMonitor,
|
||||
stopMonitor,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
||||
@@ -23,4 +23,6 @@ export const useUpdatesContext = () => ({
|
||||
createCarUpdates: jest.fn((data) => data),
|
||||
getCarUpdates: jest.fn(() => carUpdates),
|
||||
getVINUpdates: jest.fn(() => carUpdates),
|
||||
startMonitor: jest.fn(),
|
||||
stopMonitor: jest.fn(),
|
||||
});
|
||||
|
||||
@@ -11,12 +11,12 @@ const menuData = [
|
||||
roles: [],
|
||||
},
|
||||
{
|
||||
label: "View Updates",
|
||||
label: "View Packages",
|
||||
to: "/updates",
|
||||
roles: [Roles.CREATE, Roles.READ],
|
||||
},
|
||||
{
|
||||
label: "Create Updates",
|
||||
label: "Create Packages",
|
||||
to: "/package-upload",
|
||||
roles: [Roles.CREATE],
|
||||
},
|
||||
|
||||
@@ -44,7 +44,7 @@ exports[`SideMenu Authenticated 1`] = `
|
||||
<span
|
||||
class="MuiTypography-root MuiListItemText-primary MuiTypography-body1 MuiTypography-displayBlock"
|
||||
>
|
||||
View Updates
|
||||
View Packages
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
@@ -66,7 +66,7 @@ exports[`SideMenu Authenticated 1`] = `
|
||||
<span
|
||||
class="MuiTypography-root MuiListItemText-primary MuiTypography-body1 MuiTypography-displayBlock"
|
||||
>
|
||||
Create Updates
|
||||
Create Packages
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
|
||||
@@ -11,7 +11,7 @@ exports[`File Upload Form Should render 1`] = `
|
||||
<h1
|
||||
class="MuiTypography-root MuiTypography-h5"
|
||||
>
|
||||
Upload Update Package
|
||||
Create Update Package
|
||||
</h1>
|
||||
<form
|
||||
action="{onSubmit}"
|
||||
@@ -114,12 +114,19 @@ exports[`File Upload Form Should render 1`] = `
|
||||
class="MuiFormControl-root MuiTextField-root MuiFormControl-marginNormal MuiFormControl-fullWidth"
|
||||
>
|
||||
<label
|
||||
class="MuiFormLabel-root MuiInputLabel-root MuiInputLabel-formControl MuiInputLabel-animated MuiInputLabel-outlined"
|
||||
class="MuiFormLabel-root MuiInputLabel-root MuiInputLabel-formControl MuiInputLabel-animated MuiInputLabel-outlined Mui-required Mui-required"
|
||||
data-shrink="false"
|
||||
for="description"
|
||||
id="description-label"
|
||||
>
|
||||
Description
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="MuiFormLabel-asterisk MuiInputLabel-asterisk"
|
||||
>
|
||||
|
||||
*
|
||||
</span>
|
||||
</label>
|
||||
<div
|
||||
class="MuiInputBase-root MuiOutlinedInput-root MuiInputBase-fullWidth MuiInputBase-formControl MuiInputBase-multiline MuiOutlinedInput-multiline"
|
||||
@@ -131,6 +138,7 @@ exports[`File Upload Form Should render 1`] = `
|
||||
maxlength="5120"
|
||||
name="description"
|
||||
placeholder="Package description"
|
||||
required=""
|
||||
rows="4"
|
||||
/>
|
||||
<fieldset
|
||||
@@ -142,6 +150,7 @@ exports[`File Upload Form Should render 1`] = `
|
||||
>
|
||||
<span>
|
||||
Description
|
||||
*
|
||||
</span>
|
||||
</legend>
|
||||
</fieldset>
|
||||
@@ -151,12 +160,19 @@ exports[`File Upload Form Should render 1`] = `
|
||||
class="MuiFormControl-root MuiTextField-root MuiFormControl-marginNormal MuiFormControl-fullWidth"
|
||||
>
|
||||
<label
|
||||
class="MuiFormLabel-root MuiInputLabel-root MuiInputLabel-formControl MuiInputLabel-animated MuiInputLabel-outlined"
|
||||
class="MuiFormLabel-root MuiInputLabel-root MuiInputLabel-formControl MuiInputLabel-animated MuiInputLabel-outlined Mui-required Mui-required"
|
||||
data-shrink="false"
|
||||
for="releasenotes"
|
||||
id="releasenotes-label"
|
||||
>
|
||||
Release Notes URL
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="MuiFormLabel-asterisk MuiInputLabel-asterisk"
|
||||
>
|
||||
|
||||
*
|
||||
</span>
|
||||
</label>
|
||||
<div
|
||||
class="MuiInputBase-root MuiOutlinedInput-root MuiInputBase-fullWidth MuiInputBase-formControl"
|
||||
@@ -168,6 +184,7 @@ exports[`File Upload Form Should render 1`] = `
|
||||
maxlength="1024"
|
||||
name="releasenotes"
|
||||
placeholder="Release Notes URL"
|
||||
required=""
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
@@ -180,6 +197,7 @@ exports[`File Upload Form Should render 1`] = `
|
||||
>
|
||||
<span>
|
||||
Release Notes URL
|
||||
*
|
||||
</span>
|
||||
</legend>
|
||||
</fieldset>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useRef } from "react";
|
||||
import React, { useRef, useState } from "react";
|
||||
import { Button, TextField, Typography } from "@material-ui/core";
|
||||
import { DropzoneArea } from "material-ui-dropzone";
|
||||
import { useUserContext } from "../../Contexts/UserContext";
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
} from "../../Contexts/FileUploadContext";
|
||||
import ModalProgressBar from "../../ModalProgressBar";
|
||||
import useStyles from "../../useStyles";
|
||||
import { Redirect } from "react-router";
|
||||
|
||||
const FileUploadZone = ({ classes, token }) => {
|
||||
const { setFiles } = useFileUploadContext();
|
||||
@@ -39,9 +40,10 @@ const FileUploadZone = ({ classes, token }) => {
|
||||
};
|
||||
|
||||
const MainForm = () => {
|
||||
const { uploading, upload, files } = useFileUploadContext();
|
||||
const { uploading, upload, files, cancel } = useFileUploadContext();
|
||||
const { token } = useUserContext();
|
||||
const { setMessage } = useStatusContext();
|
||||
const [redirect, setRedirect] = useState(null);
|
||||
const classes = useStyles();
|
||||
const packagenameEl = useRef(null);
|
||||
const versionEl = useRef(null);
|
||||
@@ -59,17 +61,26 @@ const MainForm = () => {
|
||||
description: descEl.current.value,
|
||||
releasenotes: releasenotesEl.current.value,
|
||||
};
|
||||
const result = await upload(formData, authToken, files);
|
||||
|
||||
await upload(formData, authToken, files);
|
||||
if (!result || result.error) return;
|
||||
|
||||
cancel();
|
||||
setMessage(`Package uploaded`);
|
||||
setRedirect(`/carupdate-deploy/${result.id}`);
|
||||
} catch (e) {
|
||||
setMessage(e.message);
|
||||
}
|
||||
};
|
||||
|
||||
if (redirect && redirect.length > 0) {
|
||||
return <Redirect to={redirect} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={classes.paper}>
|
||||
<Typography component="h1" variant="h5">
|
||||
Upload Update Package
|
||||
Create Update Package
|
||||
</Typography>
|
||||
<form className={classes.form} noValidate action="{onSubmit}">
|
||||
<TextField
|
||||
@@ -107,6 +118,7 @@ const MainForm = () => {
|
||||
inputProps={{
|
||||
maxLength: "5120",
|
||||
}}
|
||||
required
|
||||
fullWidth
|
||||
multiline
|
||||
rows={4}
|
||||
@@ -122,6 +134,7 @@ const MainForm = () => {
|
||||
inputProps={{
|
||||
maxLength: "1024",
|
||||
}}
|
||||
required
|
||||
fullWidth
|
||||
placeholder="Release Notes URL"
|
||||
inputRef={releasenotesEl}
|
||||
|
||||
@@ -87,7 +87,7 @@ const MainForm = () => {
|
||||
return (
|
||||
<div className={classes.paper}>
|
||||
<Typography component="h1" variant="h5">
|
||||
Update Package {id}
|
||||
Edit Update Package {id}
|
||||
</Typography>
|
||||
<form className={classes.form} noValidate action="{onSubmit}">
|
||||
<TextField
|
||||
|
||||
@@ -26,7 +26,7 @@ import { Roles, hasRole } from "../../../utils/roles";
|
||||
|
||||
const UpdatePackagesList = () => {
|
||||
const classes = useStyles();
|
||||
const [pageSize, setPageSize] = useState(5);
|
||||
const [pageSize, setPageSize] = useState(25);
|
||||
const [pageIndex, setPageIndex] = useState(0);
|
||||
const { getPackages, packages, totalPackages } = useUpdatesContext();
|
||||
const {
|
||||
@@ -98,7 +98,7 @@ const UpdatePackagesList = () => {
|
||||
return (
|
||||
<div className={classes.paper} style={{ height: 700, width: "100%" }}>
|
||||
<Typography component="h1" variant="h5">
|
||||
Updates
|
||||
Update Packages
|
||||
</Typography>
|
||||
<TableContainer>
|
||||
<Table>
|
||||
|
||||
@@ -41,11 +41,15 @@ const updatesAPI = {
|
||||
},
|
||||
|
||||
getCarUpdates: async (filter, token) => {
|
||||
return { data:[] };
|
||||
return { data: [] };
|
||||
},
|
||||
|
||||
getVINUpdates: async (vin, token) => {
|
||||
return { data:[] };
|
||||
return { data: [] };
|
||||
},
|
||||
|
||||
getCarUpdateProgress: async (carupdateids, token) => {
|
||||
return { statuses: [] };
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -45,6 +45,15 @@ const updatesAPI = {
|
||||
})
|
||||
.then(fetchRespHandler);
|
||||
},
|
||||
|
||||
getCarUpdateProgress: async (carupdateids, token) => {
|
||||
var u = `${API_ENDPOINT}/carupdatesstatuses?carupdateids=${carupdateids}`;
|
||||
return fetch(u, {
|
||||
method: "GET",
|
||||
headers: Object.assign({ "Content-Type": "application/json" }, getAuthHeaderOptions(token)),
|
||||
})
|
||||
.then(fetchRespHandler);
|
||||
},
|
||||
};
|
||||
|
||||
export default updatesAPI;
|
||||
|
||||
Reference in New Issue
Block a user