Merge branch 'release/0.9.0'

This commit is contained in:
jwu-fisker
2023-04-17 19:01:18 -07:00
20 changed files with 286 additions and 36 deletions

View File

@@ -128,7 +128,8 @@ const TRexLogsTable = ({ vin, token, classes }) => {
const [logs, setLogs] = useState([]);
const [pageIndex, setPageIndex] = useState(0);
const [pageSize, setPageSize] = useState(DEFAULT_PAGE_SIZE);
const [selectedDate, setSelectedDate] = useState(new Date());
const local_date = new Date()
const [selectedDate, setSelectedDate] = useState(new Date(local_date.getUTCFullYear(), local_date.getUTCMonth(), local_date.getUTCDate()));
const [total, setTotal] = useState(0);
const { setMessage } = useStatusContext();
@@ -140,7 +141,7 @@ const TRexLogsTable = ({ vin, token, classes }) => {
return pageSize * pageIndex + pageSize
}
const getReadPercentage = () => {
return (currentOffset * 100 / blobSize).toFixed(2);
return (currentOffset * 100 / blobSize);
}
const getFilteredLogs = (logs) => {
return logs.filter(log => currectLogLevels[log.level] === true)
@@ -182,6 +183,19 @@ const TRexLogsTable = ({ vin, token, classes }) => {
return fetched
}
const downloadFile = async () => {
let day = formatTwoDigit(selectedDate.getDate())
let month = formatTwoDigit(selectedDate.getMonth() + 1)// 0 Indexed
let year = selectedDate.getFullYear()
let result = await api.getLogFileLink({ vin, year, month, day }, token)
window.open(result['Link'], '_blank')
}
const formatTwoDigit = (num) => {
return num < 10 ? '0' + num : '' + num
}
useEffect(() => {
(async () => {
try {
@@ -258,7 +272,7 @@ const TRexLogsTable = ({ vin, token, classes }) => {
format="yyyy/MM/dd"
margin="normal"
id="date-picker-inline"
label="Choose date"
label="Choose date (UTC)"
value={selectedDate}
onChange={handleNewDate}
KeyboardButtonProps={{
@@ -266,11 +280,12 @@ const TRexLogsTable = ({ vin, token, classes }) => {
}}
/>
</MuiPickersUtilsProvider>
</TableCell>
</TableCell>
<TableCell align="center">
{
blobSize === 0 ? `No logs for ${fromatDateForRequest(selectedDate)}` :
`Read ${getReadPercentage()}% of logs`
`Read ${getReadPercentage().toFixed(2)}% of logs`
}
{
<LinearProgress
@@ -278,7 +293,10 @@ const TRexLogsTable = ({ vin, token, classes }) => {
align="center"
value={getReadPercentage()} />
}
</TableCell>
<br></br>
<button onClick={downloadFile} disabled={blobSize === 0}>Download Log File</button>
</TableCell>
</TableRow>
</Table>
@@ -293,7 +311,7 @@ const TRexLogsTable = ({ vin, token, classes }) => {
</TableRow>
</TableHead >
<TableBody>
{getFilteredLogs(logs).slice(-getDesiredSize(), (pageIndex === 0 ? undefined : -(pageSize * pageIndex))).map((log, i) => (
{getFilteredLogs(logs).slice(-getDesiredSize(), (pageIndex === 0 ? undefined : -(pageSize * pageIndex))).reverse().map((log, i) => (
<TableRow key={log.trex_timestamp + log.cloud_timestamp} >
<TableCell align="center">{log.level}</TableCell>
<TableCell align="center" style={{ wordBreak: "break-all" }}>{log.trex_timestamp}</TableCell>

View File

@@ -1,15 +1,15 @@
import DateFnsUtils from '@date-io/date-fns';
import { Button, CircularProgress, Grid, TableFooter, TablePagination, TableCell, Table, TableRow, TableBody} from "@material-ui/core";
import { Button, CircularProgress, Grid, Table, TableBody, TableCell, TableFooter, TablePagination, TableRow } from "@material-ui/core";
import { KeyboardDatePicker, MuiPickersUtilsProvider } from '@material-ui/pickers';
import clsx from "clsx";
import React, { useEffect, useState } from "react";
import { logger } from "../../../services/monitoring";
import { DTCTimelineProvider, useDTCTimelineContext } from '../../Contexts/DTCTimelineContext';
import { useStatusContext } from "../../Contexts/StatusContext";
import { useUserContext } from "../../Contexts/UserContext";
import { useLocalStorage } from "../../useLocalStorage";
import clsx from "clsx";
import TableHeaderSortable from "../../Table/HeaderSortable";
import SearchField from '../../Controls/SearchField';
import TableHeaderSortable from "../../Table/HeaderSortable";
import { useLocalStorage } from "../../useLocalStorage";
import useStyles from "../../useStyles";
const MainForm = ({ vin }) => {
@@ -41,8 +41,12 @@ const MainForm = ({ vin }) => {
label: "ECU",
},
{
id: "dtc",
label: "DTC",
id: "trouble_code",
label: "Trouble Code",
},
{
id: "status_byte",
label: "Status Code",
},
{
id: "epoch_usec",
@@ -190,7 +194,8 @@ const MainForm = ({ vin }) => {
</TableCell>
<TableCell>{dtc.vin}</TableCell>
<TableCell>{dtc.ecu_name}</TableCell>
<TableCell>{dtc.dtc}</TableCell>
<TableCell>{dtc.trouble_code}</TableCell>
<TableCell>{dtc.status_byte}</TableCell>
<TableCell>{formatDate(dtc.epoch_usec)}</TableCell>
</TableRow>
))}
@@ -216,7 +221,7 @@ const MainForm = ({ vin }) => {
</div>
</div>
);
};
const DTCTimeline = (props) => (

View File

@@ -218,6 +218,122 @@ exports[`Manifest Details Component Render 1`] = `
</fieldset>
</div>
</div>
<div
class="MuiFormControl-root MuiFormControl-marginNormal"
>
<label
class="MuiFormLabel-root MuiInputLabel-root makeStyles-whiteBackground-0 MuiInputLabel-formControl MuiInputLabel-animated MuiInputLabel-outlined"
data-shrink="false"
>
Rollback
</label>
<div
class="MuiInputBase-root MuiOutlinedInput-root MuiInputBase-formControl"
>
<select
aria-invalid="false"
class="MuiSelect-root MuiSelect-select MuiSelect-outlined MuiInputBase-input MuiOutlinedInput-input"
>
<option
value="true"
>
True
</option>
<option
value="false"
>
False
</option>
</select>
<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>
<div
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"
>
ECC Keys
</label>
<div
class="MuiInputBase-root MuiOutlinedInput-root MuiInputBase-formControl"
>
<select
aria-invalid="false"
class="MuiSelect-root MuiSelect-select MuiSelect-outlined MuiInputBase-input MuiOutlinedInput-input"
>
<option
value="current"
>
local
</option>
<option
value="dev"
>
dev
</option>
<option
value="stage"
>
stage
</option>
<option
value="prod"
>
prod
</option>
</select>
<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
aria-label="send command"
class="MuiButtonBase-root MuiButton-root MuiButton-contained makeStyles-submit-0 MuiButton-containedPrimary MuiButton-fullWidth"

View File

@@ -2,6 +2,7 @@ import { Button, FormControl, TextField } from "@material-ui/core";
import React, { useEffect, useState } from "react";
import { Redirect } from "react-router";
import { useParams } from "react-router-dom";
import { ENVS } from "../../../utils/key-envs";
import { ManifestsProvider, useManifestsContext } from "../../Contexts/ManifestsContext";
import { useStatusContext } from "../../Contexts/StatusContext";
import { useUserContext } from "../../Contexts/UserContext";
@@ -18,6 +19,11 @@ const activeStates = [
{value: false, label: "Archived" },
];
const booleanStates = [
{value: true, label: "True" },
{value: false, label: "False" },
];
const emptyManifest = {
name: "",
version: "",
@@ -43,7 +49,8 @@ const MainForm = () => {
const [name, setName] = useState("");
const [type, setType] = useState("");
const [active, setActive] = useState(true); // So !active = archived
const [rollback, setRollback] = useState(true);
const [env, setEnv] = useState("current");
const changeName = (e) => {
setName(e.target.value);
@@ -57,10 +64,18 @@ const MainForm = () => {
setActive(e.target.value === 'true')
}
const changeRollback = (e) => {
setRollback(e.target.value === 'true')
}
const changeEnv = (e) => {
setEnv(e.target.value)
}
const onSubmit = async (e) => {
e.preventDefault();
try {
const result = await updateManifest(manifest_id, { name, type, active }, token);
const result = await updateManifest(manifest_id, { name, type, active, rollback, env }, token);
if (!result || result.error) return;
setMessage(`Updated manifest ${manifest_id}`);
@@ -81,7 +96,9 @@ const MainForm = () => {
setManifest(result);
setName(result.name);
setType(result.type);
setActive(result.active)
setActive(result.active);
setRollback(result.rollback);
setEnv(result.env ?? "current");
}
} catch (e) {
setMessage(e.message);
@@ -146,6 +163,10 @@ const MainForm = () => {
/>
<DropDownList label="Type" data={manifestTypes} classes={classes} onChange={changeType} value={type} />
<DropDownList label="Active" data={activeStates} classes={classes} onChange={changeActive} value={active}/>
<DropDownList label="Rollback" data={booleanStates} classes={classes} onChange={changeRollback} value={rollback}/>
{
ENVS.length > 1 && <DropDownList label="ECC Keys" data={ENVS} classes={classes} onChange={changeEnv} value={env}/>
}
<Button
type="submit"
aria-label="send command"

View File

@@ -197,6 +197,18 @@ const vehiclesAPI = {
.then(fetchRespHandler)
.catch(errorHandler)
},
getLogFileLink: async ({vin, year, month, day}, token) => {
const u = `${API_ENDPOINT}/vehicle/${vin}/trex-logs-link?year=${year}&month=${month}&day=${day}`
return fetch(u, {
method: "GET",
headers: Object.assign(
{ "Content-Type": "application/json" },
getAuthHeaderOptions(token)
),
}).then(fetchRespHandler)
.catch(errorHandler)
},
};
export default vehiclesAPI;

35
src/utils/key-envs.js Normal file
View File

@@ -0,0 +1,35 @@
const KEY_ENVS = process.env.REACT_APP_ECCKEY_ENV;
const ENV = process.env.REACT_APP_ENV;
export const CURRENT_ENV = "current";
const GetEnvName = (value) => {
if (value === "cec-prd" || value === "cec-euprd") return "prd";
return value;
}
const MakeList = () => {
const result = [];
const list = KEY_ENVS.split(",");
list.forEach((env) => {
if (env.length > 0) result.push({
value: env,
label: env,
})
})
result.unshift({
value: CURRENT_ENV,
label: GetEnvName(ENV),
});
return result;
}
export const GetEnvValue = (value) => {
if (value === ENV) return CURRENT_ENV;
return value;
}
export const ENVS = MakeList();

View File

@@ -1,12 +1,12 @@
import { parsePayload } from "./jwt";
export const Roles = {
READ: "a729bbd4-2038-4649-9127-16782bb1e701",
CREATE: "efcc3025-e2d8-4212-8227-805c7be39d2c",
DELETE: "8f78dce7-f5f9-4033-a10c-c9c7408bfcfe",
CERTIFICATES: "746f34b0-9ba0-4b5d-8d84-0256a9c8e390",
APPROVESUPPLIERS: "a6c9805e-80b2-42b2-bfbb-9df52e5504d8",
MANUFACTURE: "3412e11a-a2d1-4355-be3e-ef9aa5065b69",
READ: process.env.REACT_APP_ROLE_READ_ONLY,
CREATE: process.env.REACT_APP_ROLE_CREATE,
DELETE: process.env.REACT_APP_ROLE_DELETE,
CERTIFICATES: process.env.REACT_APP_ROLE_GENERATE_CERTIFICATE,
APPROVESUPPLIERS: process.env.REACT_APP_ROLE_SUPPLIER_APPROVER,
MANUFACTURE: process.env.REACT_APP_ROLE_MANUFACTURE,
MAGNAGROUP: process.env.REACT_APP_MAGNA_GROUP_ID,
};