@@ -1,4 +1,4 @@
|
||||
# Fisker OTA Admin Portal
|
||||
# Fisker Admin Portal
|
||||
|
||||
Front-end web application for administarting OTA services
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@ server {
|
||||
root /usr/share/nginx/html;
|
||||
include /etc/nginx/mime.types;
|
||||
|
||||
expires 1d;
|
||||
|
||||
location / {
|
||||
try_files $uri /index.html;
|
||||
}
|
||||
|
||||
152
package-lock.json
generated
152
package-lock.json
generated
@@ -1183,6 +1183,54 @@
|
||||
"resolved": "https://registry.npmjs.org/@csstools/normalize.css/-/normalize.css-10.1.0.tgz",
|
||||
"integrity": "sha512-ij4wRiunFfaJxjB0BdrYHIH8FxBJpOwNPhhAcunlmPdXudL1WQV1qoP9un6JsEBAgQH+7UXyyjh0g7jTxXK6tg=="
|
||||
},
|
||||
"@datadog/browser-core": {
|
||||
"version": "2.6.2",
|
||||
"resolved": "https://registry.npmjs.org/@datadog/browser-core/-/browser-core-2.6.2.tgz",
|
||||
"integrity": "sha512-ap7moHk9UuYQWSvmwLFJpyQ9QAUA4n9QHUJhn6H7bSz5QtHFTjgIxnE4ViwwolmaZteHoNtKQSTSM/T8mEHPuA==",
|
||||
"requires": {
|
||||
"tslib": "^1.10.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"tslib": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@datadog/browser-rum": {
|
||||
"version": "2.6.2",
|
||||
"resolved": "https://registry.npmjs.org/@datadog/browser-rum/-/browser-rum-2.6.2.tgz",
|
||||
"integrity": "sha512-r4dzf6xVudJYHPO87wsRp9sCX1YzdruEkHt/ANLywNKFKJusb7v6Qr/NMapQSdCcDSKMlT6hFu57GMzT0wBskQ==",
|
||||
"requires": {
|
||||
"@datadog/browser-core": "2.6.2",
|
||||
"@datadog/browser-rum-core": "2.6.2",
|
||||
"tslib": "^1.10.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"tslib": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@datadog/browser-rum-core": {
|
||||
"version": "2.6.2",
|
||||
"resolved": "https://registry.npmjs.org/@datadog/browser-rum-core/-/browser-rum-core-2.6.2.tgz",
|
||||
"integrity": "sha512-Ws47kXFUDUGln76qsu+hkTcLr5RLmmYqfRymY5H6We5Ov86fQ2p4oryRokc6WgV50oLAxicUf4Ojt7MhcPFdeg==",
|
||||
"requires": {
|
||||
"@datadog/browser-core": "2.6.2",
|
||||
"tslib": "^1.10.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"tslib": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@emotion/hash": {
|
||||
"version": "0.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz",
|
||||
@@ -2129,25 +2177,26 @@
|
||||
"integrity": "sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug=="
|
||||
},
|
||||
"@types/react": {
|
||||
"version": "17.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.1.tgz",
|
||||
"integrity": "sha512-w8t9f53B2ei4jeOqf/gxtc2Sswnc3LBK5s0DyJcg5xd10tMHXts2N31cKjWfH9IC/JvEPa/YF1U4YeP1t4R6HQ==",
|
||||
"version": "17.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.3.tgz",
|
||||
"integrity": "sha512-wYOUxIgs2HZZ0ACNiIayItyluADNbONl7kt8lkLjVK8IitMH5QMyAh75Fwhmo37r1m7L2JaFj03sIfxBVDvRAg==",
|
||||
"requires": {
|
||||
"@types/prop-types": "*",
|
||||
"@types/scheduler": "*",
|
||||
"csstype": "^3.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"csstype": {
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.6.tgz",
|
||||
"integrity": "sha512-+ZAmfyWMT7TiIlzdqJgjMb7S4f1beorDbWbsocyK4RaiqA5RTX3K14bnBWmmA9QEM0gRdsjyyrEmcyga8Zsxmw=="
|
||||
"version": "3.0.7",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.7.tgz",
|
||||
"integrity": "sha512-KxnUB0ZMlnUWCsx2Z8MUsr6qV6ja1w9ArPErJaJaF8a5SOWoHLIszeCTKGRGRgtLgYrs1E8CHkNSP1VZTTPc9g=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@types/react-transition-group": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.0.tgz",
|
||||
"integrity": "sha512-/QfLHGpu+2fQOqQaXh8MG9q03bFENooTb/it4jr5kKaZlDQfWvjqWZg48AwzPVMBHlRuTRAY7hRHCEOXz5kV6w==",
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.1.tgz",
|
||||
"integrity": "sha512-vIo69qKKcYoJ8wKCJjwSgCTM+z3chw3g18dkrDfVX665tMH7tmbDxEAnPdey4gTlwZz5QuHGzd+hul0OVZDqqQ==",
|
||||
"requires": {
|
||||
"@types/react": "*"
|
||||
}
|
||||
@@ -2160,6 +2209,11 @@
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/scheduler": {
|
||||
"version": "0.16.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.1.tgz",
|
||||
"integrity": "sha512-EaCxbanVeyxDRTQBkdLb3Bvl/HK7PBK6UJjsSixB0iHKoWxE5uu2Q/DgtpOhPIojN0Zl1whvOd7PoHs2P0s5eA=="
|
||||
},
|
||||
"@types/source-list-map": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz",
|
||||
@@ -4607,9 +4661,9 @@
|
||||
}
|
||||
},
|
||||
"csstype": {
|
||||
"version": "2.6.14",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.14.tgz",
|
||||
"integrity": "sha512-2mSc+VEpGPblzAxyeR+vZhJKgYg0Og0nnRi7pmRXFYYxSfnOnW8A5wwQb4n4cE2nIOzqKOAzLCaEX6aBmNEv8A=="
|
||||
"version": "2.6.16",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.16.tgz",
|
||||
"integrity": "sha512-61FBWoDHp/gRtsoDkq/B1nWrCUG/ok1E3tUrcNbZjsE9Cxd9yzUirjS3+nAATB8U4cTtaQmAHbNndoFz5L6C9Q=="
|
||||
},
|
||||
"cyclist": {
|
||||
"version": "1.0.1",
|
||||
@@ -4950,9 +5004,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"csstype": {
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.6.tgz",
|
||||
"integrity": "sha512-+ZAmfyWMT7TiIlzdqJgjMb7S4f1beorDbWbsocyK4RaiqA5RTX3K14bnBWmmA9QEM0gRdsjyyrEmcyga8Zsxmw=="
|
||||
"version": "3.0.7",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.7.tgz",
|
||||
"integrity": "sha512-KxnUB0ZMlnUWCsx2Z8MUsr6qV6ja1w9ArPErJaJaF8a5SOWoHLIszeCTKGRGRgtLgYrs1E8CHkNSP1VZTTPc9g=="
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -8894,9 +8948,9 @@
|
||||
}
|
||||
},
|
||||
"jss": {
|
||||
"version": "10.5.1",
|
||||
"resolved": "https://registry.npmjs.org/jss/-/jss-10.5.1.tgz",
|
||||
"integrity": "sha512-hbbO3+FOTqVdd7ZUoTiwpHzKXIo5vGpMNbuXH1a0wubRSWLWSBvwvaq4CiHH/U42CmjOnp6lVNNs/l+Z7ZdDmg==",
|
||||
"version": "10.6.0",
|
||||
"resolved": "https://registry.npmjs.org/jss/-/jss-10.6.0.tgz",
|
||||
"integrity": "sha512-n7SHdCozmxnzYGXBHe0NsO0eUf9TvsHVq2MXvi4JmTn3x5raynodDVE/9VQmBdWFyyj9HpHZ2B4xNZ7MMy7lkw==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.3.1",
|
||||
"csstype": "^3.0.2",
|
||||
@@ -8906,77 +8960,77 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"csstype": {
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.6.tgz",
|
||||
"integrity": "sha512-+ZAmfyWMT7TiIlzdqJgjMb7S4f1beorDbWbsocyK4RaiqA5RTX3K14bnBWmmA9QEM0gRdsjyyrEmcyga8Zsxmw=="
|
||||
"version": "3.0.7",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.7.tgz",
|
||||
"integrity": "sha512-KxnUB0ZMlnUWCsx2Z8MUsr6qV6ja1w9ArPErJaJaF8a5SOWoHLIszeCTKGRGRgtLgYrs1E8CHkNSP1VZTTPc9g=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"jss-plugin-camel-case": {
|
||||
"version": "10.5.1",
|
||||
"resolved": "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.5.1.tgz",
|
||||
"integrity": "sha512-9+oymA7wPtswm+zxVti1qiowC5q7bRdCJNORtns2JUj/QHp2QPXYwSNRD8+D2Cy3/CEMtdJzlNnt5aXmpS6NAg==",
|
||||
"version": "10.6.0",
|
||||
"resolved": "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.6.0.tgz",
|
||||
"integrity": "sha512-JdLpA3aI/npwj3nDMKk308pvnhoSzkW3PXlbgHAzfx0yHWnPPVUjPhXFtLJzgKZge8lsfkUxvYSQ3X2OYIFU6A==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.3.1",
|
||||
"hyphenate-style-name": "^1.0.3",
|
||||
"jss": "10.5.1"
|
||||
"jss": "10.6.0"
|
||||
}
|
||||
},
|
||||
"jss-plugin-default-unit": {
|
||||
"version": "10.5.1",
|
||||
"resolved": "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.5.1.tgz",
|
||||
"integrity": "sha512-D48hJBc9Tj3PusvlillHW8Fz0y/QqA7MNmTYDQaSB/7mTrCZjt7AVRROExoOHEtd2qIYKOYJW3Jc2agnvsXRlQ==",
|
||||
"version": "10.6.0",
|
||||
"resolved": "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.6.0.tgz",
|
||||
"integrity": "sha512-7y4cAScMHAxvslBK2JRK37ES9UT0YfTIXWgzUWD5euvR+JR3q+o8sQKzBw7GmkQRfZijrRJKNTiSt1PBsLI9/w==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.3.1",
|
||||
"jss": "10.5.1"
|
||||
"jss": "10.6.0"
|
||||
}
|
||||
},
|
||||
"jss-plugin-global": {
|
||||
"version": "10.5.1",
|
||||
"resolved": "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.5.1.tgz",
|
||||
"integrity": "sha512-jX4XpNgoaB8yPWw/gA1aPXJEoX0LNpvsROPvxlnYe+SE0JOhuvF7mA6dCkgpXBxfTWKJsno7cDSCgzHTocRjCQ==",
|
||||
"version": "10.6.0",
|
||||
"resolved": "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.6.0.tgz",
|
||||
"integrity": "sha512-I3w7ji/UXPi3VuWrTCbHG9rVCgB4yoBQLehGDTmsnDfXQb3r1l3WIdcO8JFp9m0YMmyy2CU7UOV6oPI7/Tmu+w==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.3.1",
|
||||
"jss": "10.5.1"
|
||||
"jss": "10.6.0"
|
||||
}
|
||||
},
|
||||
"jss-plugin-nested": {
|
||||
"version": "10.5.1",
|
||||
"resolved": "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.5.1.tgz",
|
||||
"integrity": "sha512-xXkWKOCljuwHNjSYcXrCxBnjd8eJp90KVFW1rlhvKKRXnEKVD6vdKXYezk2a89uKAHckSvBvBoDGsfZrldWqqQ==",
|
||||
"version": "10.6.0",
|
||||
"resolved": "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.6.0.tgz",
|
||||
"integrity": "sha512-fOFQWgd98H89E6aJSNkEh2fAXquC9aZcAVjSw4q4RoQ9gU++emg18encR4AT4OOIFl4lQwt5nEyBBRn9V1Rk8g==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.3.1",
|
||||
"jss": "10.5.1",
|
||||
"jss": "10.6.0",
|
||||
"tiny-warning": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"jss-plugin-props-sort": {
|
||||
"version": "10.5.1",
|
||||
"resolved": "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.5.1.tgz",
|
||||
"integrity": "sha512-t+2vcevNmMg4U/jAuxlfjKt46D/jHzCPEjsjLRj/J56CvP7Iy03scsUP58Iw8mVnaV36xAUZH2CmAmAdo8994g==",
|
||||
"version": "10.6.0",
|
||||
"resolved": "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.6.0.tgz",
|
||||
"integrity": "sha512-oMCe7hgho2FllNc60d9VAfdtMrZPo9n1Iu6RNa+3p9n0Bkvnv/XX5San8fTPujrTBScPqv9mOE0nWVvIaohNuw==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.3.1",
|
||||
"jss": "10.5.1"
|
||||
"jss": "10.6.0"
|
||||
}
|
||||
},
|
||||
"jss-plugin-rule-value-function": {
|
||||
"version": "10.5.1",
|
||||
"resolved": "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.5.1.tgz",
|
||||
"integrity": "sha512-3gjrSxsy4ka/lGQsTDY8oYYtkt2esBvQiceGBB4PykXxHoGRz14tbCK31Zc6DHEnIeqsjMUGbq+wEly5UViStQ==",
|
||||
"version": "10.6.0",
|
||||
"resolved": "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.6.0.tgz",
|
||||
"integrity": "sha512-TKFqhRTDHN1QrPTMYRlIQUOC2FFQb271+AbnetURKlGvRl/eWLswcgHQajwuxI464uZk91sPiTtdGi7r7XaWfA==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.3.1",
|
||||
"jss": "10.5.1",
|
||||
"jss": "10.6.0",
|
||||
"tiny-warning": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"jss-plugin-vendor-prefixer": {
|
||||
"version": "10.5.1",
|
||||
"resolved": "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.5.1.tgz",
|
||||
"integrity": "sha512-cLkH6RaPZWHa1TqSfd2vszNNgxT1W0omlSjAd6hCFHp3KIocSrW21gaHjlMU26JpTHwkc+tJTCQOmE/O1A4FKQ==",
|
||||
"version": "10.6.0",
|
||||
"resolved": "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.6.0.tgz",
|
||||
"integrity": "sha512-doJ7MouBXT1lypLLctCwb4nJ6lDYqrTfVS3LtXgox42Xz0gXusXIIDboeh6UwnSmox90QpVnub7au8ybrb0krQ==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.3.1",
|
||||
"css-vendor": "^2.0.8",
|
||||
"jss": "10.5.1"
|
||||
"jss": "10.6.0"
|
||||
}
|
||||
},
|
||||
"jsx-ast-utils": {
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
"version": "0.1.1",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@material-ui/core": "^4.11.2",
|
||||
"@datadog/browser-rum": "^2.6.2",
|
||||
"@material-ui/core": "^4.11.3",
|
||||
"@material-ui/icons": "^4.11.2",
|
||||
"@testing-library/jest-dom": "^5.11.8",
|
||||
"@testing-library/react": "^11.2.2",
|
||||
|
||||
@@ -7,13 +7,13 @@
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta
|
||||
name="description"
|
||||
content="Web site created using create-react-app"
|
||||
content="Fisker Admin Portal"
|
||||
/>
|
||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" />
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons" />
|
||||
<title>File Upload App</title>
|
||||
<title>Fisker Admin Portal</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"short_name": "OTA Admin Portal",
|
||||
"name": "Fisker OTA Admin Portal",
|
||||
"short_name": "Admin Portal",
|
||||
"name": "Fisker Admin Portal",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
|
||||
2
run.sh
2
run.sh
@@ -2,5 +2,5 @@
|
||||
|
||||
node -v
|
||||
npm install
|
||||
cp .env.template .env
|
||||
#cp .env.template .env
|
||||
npm start
|
||||
@@ -1,6 +1,8 @@
|
||||
jest.mock("../Contexts/UserContext");
|
||||
jest.mock("../Contexts/FileUploadContext");
|
||||
jest.mock("../Contexts/VehicleContext");
|
||||
jest.mock("../Contexts/UpdatesContext");
|
||||
jest.mock("../Contexts/UserContext");
|
||||
jest.mock("../../services/monitoring");
|
||||
|
||||
import { render, screen, cleanup, waitForElementToBeRemoved } from "@testing-library/react";
|
||||
import { setToken } from "../Contexts/UserContext";
|
||||
@@ -18,7 +20,27 @@ const renderRoute = async (route) => {
|
||||
return container;
|
||||
};
|
||||
|
||||
const check = async (path, selector, compare) => {
|
||||
const container = await renderRoute(path);
|
||||
expect(container.querySelector(selector).innerHTML).toEqual(compare);
|
||||
expect(container).toMatchSnapshot();
|
||||
};
|
||||
|
||||
describe("App", () => {
|
||||
beforeAll(() => {
|
||||
// Stablize Table Pagination control ids
|
||||
expect.addSnapshotSerializer({
|
||||
test: function(val) {
|
||||
return val && typeof val === "string" && val.indexOf("mui-") >= 0;
|
||||
},
|
||||
print: function(val) {
|
||||
let str = val;
|
||||
str = str.replace(/mui-[0-9]*/g, "mui-00000");
|
||||
|
||||
return `"${str}"`;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
setToken(null);
|
||||
@@ -26,15 +48,15 @@ describe("App", () => {
|
||||
});
|
||||
|
||||
it("Route / unauthenticated", async () => {
|
||||
const container = await renderRoute("/");
|
||||
expect(container.querySelector("span.MuiButton-label").innerHTML).toEqual("Sign In");
|
||||
expect(container).toMatchSnapshot();
|
||||
await check("/", "span.MuiButton-label", "Sign In");
|
||||
});
|
||||
|
||||
it("Route /home unauthenticated", async () => {
|
||||
const container = await renderRoute("/home");
|
||||
expect(container.querySelector("span.MuiButton-label").innerHTML).toEqual("Sign In");
|
||||
expect(container).toMatchSnapshot();
|
||||
await check("/home", "span.MuiButton-label", "Sign In");
|
||||
});
|
||||
|
||||
it("Route /package-upload unauthenticated", async () => {
|
||||
await check("/package-upload", "span.MuiButton-label", "Sign In");
|
||||
});
|
||||
|
||||
it("Route /package-upload unauthenticated", async () => {
|
||||
@@ -44,49 +66,89 @@ describe("App", () => {
|
||||
});
|
||||
|
||||
it("Route /vehicle-add unauthenticated", async () => {
|
||||
const container = await renderRoute("/vehicle-add");
|
||||
expect(container.querySelector("span.MuiButton-label").innerHTML).toEqual("Sign In");
|
||||
expect(container).toMatchSnapshot();
|
||||
await check("/vehicle-add", "span.MuiButton-label", "Sign In");
|
||||
});
|
||||
|
||||
it("Route /updates unauthenticated", async () => {
|
||||
await check("/updates", "span.MuiButton-label", "Sign In");
|
||||
});
|
||||
|
||||
it("Route /update unauthenticated", async () => {
|
||||
await check("/update/1", "span.MuiButton-label", "Sign In");
|
||||
});
|
||||
|
||||
it("Route /carupdate-deploy unauthenticated", async () => {
|
||||
await check("/carupdate-deploy/1", "span.MuiButton-label", "Sign In");
|
||||
});
|
||||
|
||||
it("Route /carupdate-status unauthenticated", async () => {
|
||||
await check("/carupdate-status/1", "span.MuiButton-label", "Sign In");
|
||||
});
|
||||
|
||||
it("Route /vehicles unauthenticated", async () => {
|
||||
await check("/vehicles", "span.MuiButton-label", "Sign In");
|
||||
});
|
||||
|
||||
it("Route /vehicle-status unauthenticated", async () => {
|
||||
await check("/vehicle-status/FISKER123", "span.MuiButton-label", "Sign In");
|
||||
});
|
||||
|
||||
it("Route / authenticated", async () => {
|
||||
setToken(TEST_AUTH_OBJECT);
|
||||
const container = await renderRoute("/");
|
||||
expect(container.querySelector("h1").innerHTML).toEqual("Welcome John!");
|
||||
expect(container).toMatchSnapshot();
|
||||
await check("/", "h1", "Welcome John!");
|
||||
});
|
||||
|
||||
it("Route /home authenticated", async () => {
|
||||
setToken(TEST_AUTH_OBJECT);
|
||||
const container = await renderRoute("/home");
|
||||
expect(container.querySelector("h1").innerHTML).toEqual("Welcome John!");
|
||||
expect(container).toMatchSnapshot();
|
||||
await check("/home", "h1", "Welcome John!");
|
||||
});
|
||||
|
||||
it("Route /package-upload authenticated", async () => {
|
||||
setToken(TEST_AUTH_OBJECT);
|
||||
const container = await renderRoute("/package-upload");
|
||||
expect(container.querySelector("h1").innerHTML).toEqual("Upload Update Package");
|
||||
expect(container).toMatchSnapshot();
|
||||
await check("/package-upload", "h1", "Upload Update Package");
|
||||
});
|
||||
|
||||
it("Route /vehicle-add authenticated", async () => {
|
||||
setToken(TEST_AUTH_OBJECT);
|
||||
const container = await renderRoute("/vehicle-add");
|
||||
expect(container.querySelector("h1").innerHTML).toEqual("Add Vehicle");
|
||||
expect(container).toMatchSnapshot();
|
||||
await check("/vehicle-add", "h1", "Add Vehicle");
|
||||
});
|
||||
|
||||
it("Route /updates authenticated", async () => {
|
||||
setToken(TEST_AUTH_OBJECT);
|
||||
await check("/updates", "h1", "Updates");
|
||||
});
|
||||
|
||||
it("Route /update authenticated", async () => {
|
||||
setToken(TEST_AUTH_OBJECT);
|
||||
await check("/update/1", "h1", "Update Package 1");
|
||||
});
|
||||
|
||||
it("Route /carupdate-deploy authenticated", async () => {
|
||||
setToken(TEST_AUTH_OBJECT);
|
||||
await check("/carupdate-deploy/1", "h1", "[1] ");
|
||||
});
|
||||
|
||||
it("Route /carupdate-status authenticated", async () => {
|
||||
setToken(TEST_AUTH_OBJECT);
|
||||
await check("/carupdate-status/1", "h1", "");
|
||||
});
|
||||
|
||||
it("Route /vehicles authenticated", async () => {
|
||||
setToken(TEST_AUTH_OBJECT);
|
||||
await check("/vehicles", "h1", "Vehicles");
|
||||
});
|
||||
|
||||
it("Route /vehicle-status authenticated", async () => {
|
||||
setToken(TEST_AUTH_OBJECT);
|
||||
await check("/vehicle-status/FISKER123", "h1", "FISKER123 Updates");
|
||||
});
|
||||
|
||||
it("Route /page-not-found unauthenticated", async () => {
|
||||
const container = await renderRoute("/page-not-found");
|
||||
expect(container.querySelector("h1").innerHTML).toEqual("Page Not Found");
|
||||
expect(container).toMatchSnapshot();
|
||||
await check("/page-not-found", "h1", "Page Not Found");
|
||||
});
|
||||
|
||||
it("Route /page-not-found authenticated", async () => {
|
||||
setToken(TEST_AUTH_OBJECT);
|
||||
const container = await renderRoute("/page-not-found");
|
||||
expect(container.querySelector("h1").innerHTML).toEqual("Page Not Found");
|
||||
expect(container).toMatchSnapshot();
|
||||
await check("/page-not-found", "h1", "Page Not Found");
|
||||
});
|
||||
})
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,6 +5,7 @@ import { StatusProvider } from "../Contexts/StatusContext";
|
||||
import { CssBaseline } from "@material-ui/core";
|
||||
import MenuDrawer from "../Layouts/MenuDrawer";
|
||||
import SiteRoutes from "../Routes/SiteRoutes";
|
||||
import {} from "../../services/monitoring";
|
||||
|
||||
function App() {
|
||||
return (
|
||||
|
||||
@@ -49,18 +49,12 @@ const MainForm = () => {
|
||||
const handleVehiclesChange = (event) => {
|
||||
setSelectedVehicles(event.target.value);
|
||||
};
|
||||
const getCarIDs = () => {
|
||||
if (!selectedVehicles) return [];
|
||||
return selectedVehicles.map((vin) => {
|
||||
return vehicles.find((vehicle) => vehicle.vin === vin).id;
|
||||
});
|
||||
};
|
||||
const onSubmit = async (event) => {
|
||||
try {
|
||||
event.preventDefault();
|
||||
const data = {
|
||||
package_id: parseInt(packageid),
|
||||
car_ids: getCarIDs(),
|
||||
vins: selectedVehicles,
|
||||
};
|
||||
await createCarUpdates(data, token);
|
||||
setMessage(
|
||||
|
||||
@@ -20,12 +20,15 @@ import { useUserContext } from "../../Contexts/UserContext";
|
||||
import { useStatusContext } from "../../Contexts/StatusContext";
|
||||
import useStyles from "../../useStyles";
|
||||
import { LocalDateTimeString } from "../../../utils/dates";
|
||||
import VehicleStatus from "../../Cars/StatusModal";
|
||||
|
||||
const MainForm = () => {
|
||||
const { packageid } = useParams();
|
||||
const classes = useStyles();
|
||||
const [pageSize, setPageSize] = useState(10);
|
||||
const [pageIndex, setPageIndex] = useState(0);
|
||||
const [viewVIN, setViewVIN] = useState(null);
|
||||
|
||||
const {
|
||||
getCarUpdates,
|
||||
carUpdates,
|
||||
@@ -74,6 +77,15 @@ const MainForm = () => {
|
||||
setPageIndex(0);
|
||||
};
|
||||
|
||||
const handleViewVIN = (event) => {
|
||||
event.preventDefault();
|
||||
setViewVIN(event.target.innerHTML);
|
||||
};
|
||||
|
||||
const handleCloseViewVIN = (event) => {
|
||||
setViewVIN(null);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={classes.paper} style={{ height: 700, width: "100%" }}>
|
||||
<Typography component="h1" variant="h5">
|
||||
@@ -96,7 +108,11 @@ const MainForm = () => {
|
||||
{carUpdates.map((row) => (
|
||||
<TableRow key={row.id}>
|
||||
<TableCell align="center">{row.id}</TableCell>
|
||||
<TableCell align="center">{`${row.car.vin} ${row.car.model} ${row.car.year}`}</TableCell>
|
||||
<TableCell align="center">
|
||||
<span className={classes.link} onClick={handleViewVIN}>
|
||||
{row.vin}
|
||||
</span>
|
||||
</TableCell>
|
||||
<TableCell align="center">{row.status}</TableCell>
|
||||
<TableCell align="center">
|
||||
{LocalDateTimeString(row.created)}
|
||||
@@ -126,6 +142,7 @@ const MainForm = () => {
|
||||
</TableFooter>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
<VehicleStatus vin={viewVIN} handleClose={handleCloseViewVIN} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
@@ -65,7 +66,6 @@ const MainForm = () => {
|
||||
<Table>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell align="center">ID</TableCell>
|
||||
<TableCell align="center">VIN</TableCell>
|
||||
<TableCell align="center">Model</TableCell>
|
||||
<TableCell align="center">Year</TableCell>
|
||||
@@ -75,9 +75,10 @@ const MainForm = () => {
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{vehicles.map((row) => (
|
||||
<TableRow key={row.id}>
|
||||
<TableCell align="center">{row.id}</TableCell>
|
||||
<TableCell align="center">{row.vin}</TableCell>
|
||||
<TableRow key={row.vin}>
|
||||
<TableCell align="center">
|
||||
<Link to={`/vehicle-status/${row.vin}`}>{row.vin}</Link>
|
||||
</TableCell>
|
||||
<TableCell align="center">{row.model}</TableCell>
|
||||
<TableCell align="center">{row.year}</TableCell>
|
||||
<TableCell align="center">
|
||||
|
||||
122
src/components/Cars/Status/index.jsx
Normal file
122
src/components/Cars/Status/index.jsx
Normal file
@@ -0,0 +1,122 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useParams } from "react-router";
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableContainer,
|
||||
TableFooter,
|
||||
TableHead,
|
||||
TablePagination,
|
||||
TableRow,
|
||||
Typography,
|
||||
} from "@material-ui/core";
|
||||
|
||||
import {
|
||||
UpdatesProvider,
|
||||
useUpdatesContext,
|
||||
} from "../../Contexts/UpdatesContext";
|
||||
import { useUserContext } from "../../Contexts/UserContext";
|
||||
import { useStatusContext } from "../../Contexts/StatusContext";
|
||||
import useStyles from "../../useStyles";
|
||||
import { LocalDateTimeString } from "../../../utils/dates";
|
||||
|
||||
const MainForm = () => {
|
||||
const { vin } = useParams();
|
||||
const classes = useStyles();
|
||||
const [pageSize, setPageSize] = useState(10);
|
||||
const [pageIndex, setPageIndex] = useState(0);
|
||||
const { getCarUpdates, carUpdates, totalCarUpdates } = useUpdatesContext();
|
||||
const { setMessage } = useStatusContext();
|
||||
const {
|
||||
token: {
|
||||
idToken: { jwtToken: token },
|
||||
},
|
||||
} = useUserContext();
|
||||
|
||||
useEffect(() => {
|
||||
try {
|
||||
getCarUpdates(
|
||||
{
|
||||
vin,
|
||||
limit: pageSize,
|
||||
offset: pageSize * pageIndex,
|
||||
},
|
||||
token
|
||||
);
|
||||
} catch (e) {
|
||||
setMessage(e.message);
|
||||
}
|
||||
// eslint-disable-next-line
|
||||
}, [pageIndex, pageSize, token]);
|
||||
|
||||
const handleChangePageIndex = (event, newIndex) => {
|
||||
setPageIndex(newIndex);
|
||||
};
|
||||
|
||||
const handleChangePageSize = (event) => {
|
||||
setPageSize(parseInt(event.target.value, 10));
|
||||
setPageIndex(0);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={classes.paper} style={{ height: 700, width: "100%" }}>
|
||||
<Typography component="h1" variant="h5">
|
||||
{vin} Updates
|
||||
</Typography>
|
||||
<TableContainer>
|
||||
<Table>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell align="center">ID</TableCell>
|
||||
<TableCell align="center">Update</TableCell>
|
||||
<TableCell align="center">Status</TableCell>
|
||||
<TableCell align="center">Created</TableCell>
|
||||
<TableCell align="center">Updated</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{carUpdates.map((row) => (
|
||||
<TableRow key={row.id}>
|
||||
<TableCell align="center">{row.id}</TableCell>
|
||||
<TableCell align="center">{`${row.updatepackage.package_name} ${row.updatepackage.version}`}</TableCell>
|
||||
<TableCell align="center">{row.status}</TableCell>
|
||||
<TableCell align="center">
|
||||
{LocalDateTimeString(row.created)}
|
||||
</TableCell>
|
||||
<TableCell align="center">
|
||||
{LocalDateTimeString(row.updated)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
<TableFooter>
|
||||
<TableRow>
|
||||
<TablePagination
|
||||
rowsPerPageOptions={[5, 10, 25]}
|
||||
colSpan={5}
|
||||
count={totalCarUpdates}
|
||||
rowsPerPage={pageSize}
|
||||
page={pageIndex}
|
||||
SelectProps={{
|
||||
inputProps: { "aria-label": "rows per page" },
|
||||
native: true,
|
||||
}}
|
||||
onChangePage={handleChangePageIndex}
|
||||
onChangeRowsPerPage={handleChangePageSize}
|
||||
/>
|
||||
</TableRow>
|
||||
</TableFooter>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const CarUpdates = () => (
|
||||
<UpdatesProvider>
|
||||
<MainForm />
|
||||
</UpdatesProvider>
|
||||
);
|
||||
|
||||
export default CarUpdates;
|
||||
91
src/components/Cars/StatusModal/index.jsx
Normal file
91
src/components/Cars/StatusModal/index.jsx
Normal file
@@ -0,0 +1,91 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import {
|
||||
Backdrop,
|
||||
Modal,
|
||||
Fade,
|
||||
Table,
|
||||
TableHead,
|
||||
TableRow,
|
||||
TableCell,
|
||||
TableBody,
|
||||
} from "@material-ui/core";
|
||||
|
||||
import useStyles from "../../useStyles";
|
||||
import { useUpdatesContext } from "../../Contexts/UpdatesContext";
|
||||
import { useUserContext } from "../../Contexts/UserContext";
|
||||
import { useStatusContext } from "../../Contexts/StatusContext";
|
||||
import { LocalDateTimeString } from "../../../utils/dates";
|
||||
|
||||
export default function CarStatusModal(props) {
|
||||
const classes = useStyles();
|
||||
const [updates, setUpdates] = useState([]);
|
||||
const { setMessage } = useStatusContext();
|
||||
const { getVINUpdates } = useUpdatesContext();
|
||||
const {
|
||||
token: {
|
||||
idToken: { jwtToken: token },
|
||||
},
|
||||
} = useUserContext();
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
try {
|
||||
if (!props.vin) return;
|
||||
const result = await getVINUpdates(props.vin, token);
|
||||
if (result.error) {
|
||||
throw new Error(`Get VIN updates error. ${result.message}`);
|
||||
} else {
|
||||
setUpdates(result.data);
|
||||
}
|
||||
} catch (e) {
|
||||
setMessage(e.message);
|
||||
}
|
||||
})();
|
||||
// eslint-disable-next-line
|
||||
}, [props.vin]);
|
||||
return (
|
||||
<div>
|
||||
<Modal
|
||||
aria-labelledby="transition-modal-title"
|
||||
aria-describedby="transition-modal-description"
|
||||
className={classes.modal}
|
||||
open={props.vin !== null && props.vin !== undefined}
|
||||
onClose={props.handleClose}
|
||||
BackdropComponent={Backdrop}
|
||||
BackdropProps={{
|
||||
timeout: 500,
|
||||
}}
|
||||
>
|
||||
<Fade in={props.vin}>
|
||||
<div className={classes.modaldialog}>
|
||||
<h2 id="transition-modal-title">{props.vin} Updates</h2>
|
||||
<Table>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell align="center">Date</TableCell>
|
||||
<TableCell align="center">Update</TableCell>
|
||||
<TableCell align="center">Status</TableCell>
|
||||
<TableCell align="center">Updated</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{updates.map((update) => (
|
||||
<TableRow key={update.id}>
|
||||
<TableCell align="center">
|
||||
{LocalDateTimeString(update.created)}
|
||||
</TableCell>
|
||||
<TableCell align="center">{`${update.updatepackage.package_name} ${update.updatepackage.version}`}</TableCell>
|
||||
<TableCell align="center">{update.status}</TableCell>
|
||||
<TableCell align="center">
|
||||
{LocalDateTimeString(update.updated)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
</Fade>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -81,6 +81,21 @@ export const UpdatesProvider = ({ children }) => {
|
||||
return result;
|
||||
};
|
||||
|
||||
const getVINUpdates = async (vin, token) => {
|
||||
let result;
|
||||
|
||||
try {
|
||||
setBusy(true);
|
||||
result = await api.getVINUpdates(vin, token);
|
||||
if (result.error)
|
||||
throw new Error(`Get VIN updates error. ${result.message}`);
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
return (
|
||||
<UpdatesContext.Provider
|
||||
value={{
|
||||
@@ -93,6 +108,7 @@ export const UpdatesProvider = ({ children }) => {
|
||||
updatePackage,
|
||||
createCarUpdates,
|
||||
getCarUpdates,
|
||||
getVINUpdates,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
@@ -125,7 +141,7 @@ const validateCreateCarUpdates = (data) => {
|
||||
throw new Error("Package id required");
|
||||
}
|
||||
|
||||
if (!data.car_ids || data.car_ids.length === 0) {
|
||||
if (!data.vins || data.vins.length === 0) {
|
||||
throw new Error("Car ids required");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -7,8 +7,8 @@ import {
|
||||
fireEvent,
|
||||
waitFor,
|
||||
} from "@testing-library/react";
|
||||
import { UpdatesProvider, useUpdatesContext } from "../Contexts/UpdatesContext";
|
||||
import { StatusProvider, useStatusContext } from "../Contexts/StatusContext";
|
||||
import { UpdatesProvider, useUpdatesContext } from "./UpdatesContext";
|
||||
import { StatusProvider, useStatusContext } from "./StatusContext";
|
||||
import { TEST_AUTH_OBJECT } from "../../utils/testing";
|
||||
|
||||
describe("UpdatesContext", () => {
|
||||
@@ -222,7 +222,7 @@ describe("UpdatesContext", () => {
|
||||
data-testid="with-bad-data"
|
||||
onClick={async () => {
|
||||
result = await exec(
|
||||
{ package_id: 1, car_ids: [] },
|
||||
{ package_id: 1, vins: [] },
|
||||
TEST_AUTH_OBJECT
|
||||
);
|
||||
}}
|
||||
@@ -233,7 +233,7 @@ describe("UpdatesContext", () => {
|
||||
result = await exec(
|
||||
{
|
||||
package_id: 1,
|
||||
car_ids: [1, 2, 3],
|
||||
vins: ["FISKER123", "FISKER124", "FISKER125"],
|
||||
},
|
||||
TEST_AUTH_OBJECT
|
||||
);
|
||||
@@ -284,7 +284,7 @@ describe("UpdatesContext", () => {
|
||||
checkState("false", "", {
|
||||
id: 1,
|
||||
package_id: 1,
|
||||
car_ids: [1, 2, 3],
|
||||
vins: ["FISKER123", "FISKER124", "FISKER125"],
|
||||
});
|
||||
});
|
||||
});
|
||||
26
src/components/Contexts/__mocks__/UpdatesContext.jsx
Normal file
26
src/components/Contexts/__mocks__/UpdatesContext.jsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import React from "react";
|
||||
|
||||
const UpdatesContext = React.createContext();
|
||||
|
||||
let busy = false;
|
||||
let packages = [];
|
||||
let totalPackages = 0;
|
||||
let carUpdates = [];
|
||||
let totalCarUpdates = 0;
|
||||
|
||||
export const UpdatesProvider = ({ children }) => {
|
||||
return <div data-testid="mocked-updatesprovider">{children}</div>;
|
||||
};
|
||||
|
||||
export const useUpdatesContext = () => ({
|
||||
busy,
|
||||
packages,
|
||||
totalPackages,
|
||||
carUpdates,
|
||||
totalCarUpdates,
|
||||
getPackages: jest.fn(() => packages),
|
||||
updatePackage: jest.fn((data) => data),
|
||||
createCarUpdates: jest.fn((data) => data),
|
||||
getCarUpdates: jest.fn(() => carUpdates),
|
||||
getVINUpdates: jest.fn(() => carUpdates),
|
||||
});
|
||||
@@ -2,6 +2,7 @@ import React from "react";
|
||||
|
||||
let busy = false;
|
||||
let vehicles = [];
|
||||
let totalVehicles = 0;
|
||||
let error = null;
|
||||
|
||||
export const VehicleProvider = ({ children }) => {
|
||||
@@ -11,6 +12,7 @@ export const VehicleProvider = ({ children }) => {
|
||||
export const useVehicleContext = () => ({
|
||||
busy,
|
||||
vehicles,
|
||||
totalVehicles,
|
||||
getVehicles: jest.fn(() => vehicles),
|
||||
addVehicle: jest.fn(),
|
||||
});
|
||||
|
||||
@@ -2,25 +2,60 @@ import React, { Component } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { Typography } from "@material-ui/core";
|
||||
|
||||
const reload = () => {
|
||||
window.location.reload();
|
||||
};
|
||||
export default class ErrorBoundary extends Component {
|
||||
state = {
|
||||
error: "",
|
||||
errorInfo: "",
|
||||
hasError: false,
|
||||
};
|
||||
|
||||
static getDerivedStateFromError(error) {
|
||||
return { hasError: true, error };
|
||||
}
|
||||
|
||||
componentDidCatch(error, errorInfo) {
|
||||
this.setState({ errorInfo });
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.hasError)
|
||||
if (this.state.hasError) {
|
||||
if (this.state.error && this.state.error.name === "ChunkLoadError") {
|
||||
reload();
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
<Typography variant="h3" align="center">
|
||||
Oops. An React JS Error Occured.
|
||||
</Typography>
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: "50%",
|
||||
left: "50%",
|
||||
transform: "translate(-50%, -50%)",
|
||||
}}
|
||||
>
|
||||
<Typography variant="h3" align="center">
|
||||
Sorry, an error has occured and been logged
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="h5"
|
||||
align="center"
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
textDecorationColor: "Blue",
|
||||
textDecorationStyle: "solid",
|
||||
textDecorationLine: "underline",
|
||||
color: "Blue",
|
||||
}}
|
||||
onClick={reload}
|
||||
>
|
||||
Click to reload
|
||||
</Typography>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return this.props.children;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ const UpdatePackagesForm = React.lazy(() => import("../UpdatePackages/List"));
|
||||
const UpdatePackageEdit = React.lazy(() => import("../UpdatePackages/Edit"));
|
||||
const CarUpdatesDeploy = React.lazy(() => import("../CarUpdates/Deploy"));
|
||||
const CarUpdatesStatus = React.lazy(() => import("../CarUpdates/Status"));
|
||||
const CarUpdates = React.lazy(() => import("../Cars/Status"));
|
||||
const VehiclesList = React.lazy(() => import("../Cars/List"));
|
||||
|
||||
const SiteRoutes = () => {
|
||||
@@ -92,6 +93,14 @@ const SiteRoutes = () => {
|
||||
groups={groups}
|
||||
roles={[Roles.CREATE]}
|
||||
/>
|
||||
<AuthRoute
|
||||
path="/vehicle-status/:vin"
|
||||
render={() => <CarUpdates />}
|
||||
type={TYPES.PROTECTED}
|
||||
token={token}
|
||||
groups={groups}
|
||||
roles={[Roles.READ, Roles.CREATE]}
|
||||
/>
|
||||
<PageNotFound />
|
||||
</Switch>
|
||||
</Suspense>
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
exports[`Sign In Form Should render 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="makeStyles-paper-1"
|
||||
class="makeStyles-paper-3"
|
||||
style="justify-content: center;"
|
||||
>
|
||||
<a
|
||||
aria-disabled="false"
|
||||
class="MuiButtonBase-root MuiButton-root MuiButton-contained makeStyles-submit-4 MuiButton-containedPrimary"
|
||||
class="MuiButtonBase-root MuiButton-root MuiButton-contained makeStyles-submit-6 MuiButton-containedPrimary"
|
||||
href="https://cognito.com/authorize?redirect=https://example.com/callback"
|
||||
tabindex="0"
|
||||
>
|
||||
|
||||
@@ -6,7 +6,7 @@ exports[`File Upload Form Should render 1`] = `
|
||||
data-testid="mocked-fileuploadprovider"
|
||||
>
|
||||
<div
|
||||
class="makeStyles-paper-1"
|
||||
class="makeStyles-paper-3"
|
||||
>
|
||||
<h1
|
||||
class="MuiTypography-root MuiTypography-h5"
|
||||
@@ -15,7 +15,7 @@ exports[`File Upload Form Should render 1`] = `
|
||||
</h1>
|
||||
<form
|
||||
action="{onSubmit}"
|
||||
class="makeStyles-form-3"
|
||||
class="makeStyles-form-5"
|
||||
novalidate=""
|
||||
>
|
||||
<div
|
||||
@@ -51,10 +51,10 @@ exports[`File Upload Form Should render 1`] = `
|
||||
/>
|
||||
<fieldset
|
||||
aria-hidden="true"
|
||||
class="PrivateNotchedOutline-root-21 MuiOutlinedInput-notchedOutline"
|
||||
class="PrivateNotchedOutline-root-24 MuiOutlinedInput-notchedOutline"
|
||||
>
|
||||
<legend
|
||||
class="PrivateNotchedOutline-legendLabelled-23"
|
||||
class="PrivateNotchedOutline-legendLabelled-26"
|
||||
>
|
||||
<span>
|
||||
Package name
|
||||
@@ -97,10 +97,10 @@ exports[`File Upload Form Should render 1`] = `
|
||||
/>
|
||||
<fieldset
|
||||
aria-hidden="true"
|
||||
class="PrivateNotchedOutline-root-21 MuiOutlinedInput-notchedOutline"
|
||||
class="PrivateNotchedOutline-root-24 MuiOutlinedInput-notchedOutline"
|
||||
>
|
||||
<legend
|
||||
class="PrivateNotchedOutline-legendLabelled-23"
|
||||
class="PrivateNotchedOutline-legendLabelled-26"
|
||||
>
|
||||
<span>
|
||||
Version
|
||||
@@ -135,10 +135,10 @@ exports[`File Upload Form Should render 1`] = `
|
||||
/>
|
||||
<fieldset
|
||||
aria-hidden="true"
|
||||
class="PrivateNotchedOutline-root-21 MuiOutlinedInput-notchedOutline"
|
||||
class="PrivateNotchedOutline-root-24 MuiOutlinedInput-notchedOutline"
|
||||
>
|
||||
<legend
|
||||
class="PrivateNotchedOutline-legendLabelled-23"
|
||||
class="PrivateNotchedOutline-legendLabelled-26"
|
||||
>
|
||||
<span>
|
||||
Description
|
||||
@@ -173,10 +173,10 @@ exports[`File Upload Form Should render 1`] = `
|
||||
/>
|
||||
<fieldset
|
||||
aria-hidden="true"
|
||||
class="PrivateNotchedOutline-root-21 MuiOutlinedInput-notchedOutline"
|
||||
class="PrivateNotchedOutline-root-24 MuiOutlinedInput-notchedOutline"
|
||||
>
|
||||
<legend
|
||||
class="PrivateNotchedOutline-legendLabelled-23"
|
||||
class="PrivateNotchedOutline-legendLabelled-26"
|
||||
>
|
||||
<span>
|
||||
Release Notes URL
|
||||
@@ -217,7 +217,7 @@ exports[`File Upload Form Should render 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
class="MuiButtonBase-root MuiButton-root MuiButton-contained makeStyles-submit-4 MuiButton-containedPrimary MuiButton-fullWidth"
|
||||
class="MuiButtonBase-root MuiButton-root MuiButton-contained makeStyles-submit-6 MuiButton-containedPrimary MuiButton-fullWidth"
|
||||
tabindex="0"
|
||||
type="submit"
|
||||
>
|
||||
|
||||
@@ -5,6 +5,17 @@ const MENUITEM_PADDING_TOP = 8;
|
||||
const DRAWER_WIDTH = 240;
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
modal: {
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
},
|
||||
modaldialog: {
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
border: "2px solid #000",
|
||||
boxShadow: theme.shadows[5],
|
||||
padding: theme.spacing(2, 4, 3),
|
||||
},
|
||||
paper: {
|
||||
marginTop: theme.spacing(8),
|
||||
display: "flex",
|
||||
@@ -105,6 +116,13 @@ const useStyles = makeStyles((theme) => ({
|
||||
marginLeft: "auto",
|
||||
marginRight: -12,
|
||||
},
|
||||
link: {
|
||||
cursor: "pointer",
|
||||
textDecorationColor: "Blue",
|
||||
textDecorationStyle: "solid",
|
||||
textDecorationLine: "underline",
|
||||
color: "Blue",
|
||||
},
|
||||
}));
|
||||
|
||||
export default useStyles;
|
||||
|
||||
1
src/services/__mocks__/monitoring.js
Normal file
1
src/services/__mocks__/monitoring.js
Normal file
@@ -0,0 +1 @@
|
||||
// no actual monitoring with mock
|
||||
@@ -41,7 +41,11 @@ const updatesAPI = {
|
||||
},
|
||||
|
||||
getCarUpdates: async (filter, token) => {
|
||||
return [];
|
||||
return { data:[] };
|
||||
},
|
||||
|
||||
getVINUpdates: async (vin, token) => {
|
||||
return { data:[] };
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
12
src/services/monitoring.js
Normal file
12
src/services/monitoring.js
Normal file
@@ -0,0 +1,12 @@
|
||||
import { datadogRum } from '@datadog/browser-rum';
|
||||
|
||||
datadogRum.init({
|
||||
applicationId: '8ecd160c-ad5c-4e06-8d88-3a6b89833246',
|
||||
clientToken: 'pubeb25449bb91773fc993855c7378e375a',
|
||||
site: 'datadoghq.com',
|
||||
service:'ota-portal',
|
||||
// Specify a version number to identify the deployed version of your application in Datadog
|
||||
// version: '1.0.0',
|
||||
sampleRate: 100,
|
||||
trackInteractions: true
|
||||
});
|
||||
@@ -37,6 +37,14 @@ const updatesAPI = {
|
||||
.then(fetchRespHandler);
|
||||
},
|
||||
|
||||
getVINUpdates: async (vin, token) => {
|
||||
var u = addQueryParams(`${API_ENDPOINT}/carupdates`, { vin });
|
||||
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