Merge pull request #488 from Fisker-Inc/CEC-5443
CEC-5443 - Ability to add Flashpack/ECU mappings
This commit is contained in:
@@ -456,6 +456,28 @@ exports[`App Route / authenticated 1`] = `
|
|||||||
/>
|
/>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
aria-disabled="false"
|
||||||
|
class="MuiButtonBase-root MuiListItem-root MuiListItem-gutters MuiListItem-button"
|
||||||
|
href="/tools/flashpacks"
|
||||||
|
role="button"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="MuiListItemText-root"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="MuiTypography-root MuiListItemText-primary MuiTypography-body1 MuiTypography-displayBlock"
|
||||||
|
>
|
||||||
|
Flashpack
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<span
|
||||||
|
class="MuiTouchRipple-root"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@@ -1853,6 +1875,28 @@ exports[`App Route /home authenticated 1`] = `
|
|||||||
/>
|
/>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
aria-disabled="false"
|
||||||
|
class="MuiButtonBase-root MuiListItem-root MuiListItem-gutters MuiListItem-button"
|
||||||
|
href="/tools/flashpacks"
|
||||||
|
role="button"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="MuiListItemText-root"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="MuiTypography-root MuiListItemText-primary MuiTypography-body1 MuiTypography-displayBlock"
|
||||||
|
>
|
||||||
|
Flashpack
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<span
|
||||||
|
class="MuiTouchRipple-root"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@@ -2627,6 +2671,28 @@ exports[`App Route /issue-info authenticated 1`] = `
|
|||||||
/>
|
/>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
aria-disabled="false"
|
||||||
|
class="MuiButtonBase-root MuiListItem-root MuiListItem-gutters MuiListItem-button"
|
||||||
|
href="/tools/flashpacks"
|
||||||
|
role="button"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="MuiListItemText-root"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="MuiTypography-root MuiListItemText-primary MuiTypography-body1 MuiTypography-displayBlock"
|
||||||
|
>
|
||||||
|
Flashpack
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<span
|
||||||
|
class="MuiTouchRipple-root"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@@ -3239,6 +3305,28 @@ exports[`App Route /issues authenticated 1`] = `
|
|||||||
/>
|
/>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
aria-disabled="false"
|
||||||
|
class="MuiButtonBase-root MuiListItem-root MuiListItem-gutters MuiListItem-button"
|
||||||
|
href="/tools/flashpacks"
|
||||||
|
role="button"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="MuiListItemText-root"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="MuiTypography-root MuiListItemText-primary MuiTypography-body1 MuiTypography-displayBlock"
|
||||||
|
>
|
||||||
|
Flashpack
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<span
|
||||||
|
class="MuiTouchRipple-root"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@@ -4220,6 +4308,28 @@ exports[`App Route /package-deploy authenticated 1`] = `
|
|||||||
/>
|
/>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
aria-disabled="false"
|
||||||
|
class="MuiButtonBase-root MuiListItem-root MuiListItem-gutters MuiListItem-button"
|
||||||
|
href="/tools/flashpacks"
|
||||||
|
role="button"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="MuiListItemText-root"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="MuiTypography-root MuiListItemText-primary MuiTypography-body1 MuiTypography-displayBlock"
|
||||||
|
>
|
||||||
|
Flashpack
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<span
|
||||||
|
class="MuiTouchRipple-root"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@@ -5324,6 +5434,28 @@ exports[`App Route /package-status authenticated 1`] = `
|
|||||||
/>
|
/>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
aria-disabled="false"
|
||||||
|
class="MuiButtonBase-root MuiListItem-root MuiListItem-gutters MuiListItem-button"
|
||||||
|
href="/tools/flashpacks"
|
||||||
|
role="button"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="MuiListItemText-root"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="MuiTypography-root MuiListItemText-primary MuiTypography-body1 MuiTypography-displayBlock"
|
||||||
|
>
|
||||||
|
Flashpack
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<span
|
||||||
|
class="MuiTouchRipple-root"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@@ -6383,6 +6515,28 @@ exports[`App Route /packages authenticated 1`] = `
|
|||||||
/>
|
/>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
aria-disabled="false"
|
||||||
|
class="MuiButtonBase-root MuiListItem-root MuiListItem-gutters MuiListItem-button"
|
||||||
|
href="/tools/flashpacks"
|
||||||
|
role="button"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="MuiListItemText-root"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="MuiTypography-root MuiListItemText-primary MuiTypography-body1 MuiTypography-displayBlock"
|
||||||
|
>
|
||||||
|
Flashpack
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<span
|
||||||
|
class="MuiTouchRipple-root"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@@ -7561,6 +7715,28 @@ exports[`App Route /page-not-found authenticated 1`] = `
|
|||||||
/>
|
/>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
aria-disabled="false"
|
||||||
|
class="MuiButtonBase-root MuiListItem-root MuiListItem-gutters MuiListItem-button"
|
||||||
|
href="/tools/flashpacks"
|
||||||
|
role="button"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="MuiListItemText-root"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="MuiTypography-root MuiListItemText-primary MuiTypography-body1 MuiTypography-displayBlock"
|
||||||
|
>
|
||||||
|
Flashpack
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<span
|
||||||
|
class="MuiTouchRipple-root"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@@ -8189,6 +8365,28 @@ exports[`App Route /tools/certificates/add authenticated 1`] = `
|
|||||||
/>
|
/>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
aria-disabled="false"
|
||||||
|
class="MuiButtonBase-root MuiListItem-root MuiListItem-gutters MuiListItem-button"
|
||||||
|
href="/tools/flashpacks"
|
||||||
|
role="button"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="MuiListItemText-root"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="MuiTypography-root MuiListItemText-primary MuiTypography-body1 MuiTypography-displayBlock"
|
||||||
|
>
|
||||||
|
Flashpack
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<span
|
||||||
|
class="MuiTouchRipple-root"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@@ -9483,6 +9681,28 @@ exports[`App Route /tools/sms/send authenticated 1`] = `
|
|||||||
/>
|
/>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
aria-disabled="false"
|
||||||
|
class="MuiButtonBase-root MuiListItem-root MuiListItem-gutters MuiListItem-button"
|
||||||
|
href="/tools/flashpacks"
|
||||||
|
role="button"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="MuiListItemText-root"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="MuiTypography-root MuiListItemText-primary MuiTypography-body1 MuiTypography-displayBlock"
|
||||||
|
>
|
||||||
|
Flashpack
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<span
|
||||||
|
class="MuiTouchRipple-root"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@@ -10210,6 +10430,28 @@ exports[`App Route /vehicle-add authenticated 1`] = `
|
|||||||
/>
|
/>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
aria-disabled="false"
|
||||||
|
class="MuiButtonBase-root MuiListItem-root MuiListItem-gutters MuiListItem-button"
|
||||||
|
href="/tools/flashpacks"
|
||||||
|
role="button"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="MuiListItemText-root"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="MuiTypography-root MuiListItemText-primary MuiTypography-body1 MuiTypography-displayBlock"
|
||||||
|
>
|
||||||
|
Flashpack
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<span
|
||||||
|
class="MuiTouchRipple-root"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@@ -11392,6 +11634,28 @@ exports[`App Route /vehicle-status authenticated 1`] = `
|
|||||||
/>
|
/>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
aria-disabled="false"
|
||||||
|
class="MuiButtonBase-root MuiListItem-root MuiListItem-gutters MuiListItem-button"
|
||||||
|
href="/tools/flashpacks"
|
||||||
|
role="button"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="MuiListItemText-root"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="MuiTypography-root MuiListItemText-primary MuiTypography-body1 MuiTypography-displayBlock"
|
||||||
|
>
|
||||||
|
Flashpack
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<span
|
||||||
|
class="MuiTouchRipple-root"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@@ -12503,6 +12767,28 @@ exports[`App Route /vehicles authenticated 1`] = `
|
|||||||
/>
|
/>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
aria-disabled="false"
|
||||||
|
class="MuiButtonBase-root MuiListItem-root MuiListItem-gutters MuiListItem-button"
|
||||||
|
href="/tools/flashpacks"
|
||||||
|
role="button"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="MuiListItemText-root"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="MuiTypography-root MuiListItemText-primary MuiTypography-body1 MuiTypography-displayBlock"
|
||||||
|
>
|
||||||
|
Flashpack
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<span
|
||||||
|
class="MuiTouchRipple-root"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -30,6 +30,10 @@ export const VehicleProvider = ({ children }) => {
|
|||||||
const [totalFleets, setTotalFleets] = useState(0);
|
const [totalFleets, setTotalFleets] = useState(0);
|
||||||
const [models, setModels] = useState([]);
|
const [models, setModels] = useState([]);
|
||||||
const [years, setYears] = useState([]);
|
const [years, setYears] = useState([]);
|
||||||
|
const [flashpacks, setFlashpacks] = useState([]);
|
||||||
|
const [totalFlashpacks, setTotalFlashpacks] = useState(0);
|
||||||
|
const [flashpackECUMappings, setFlashpackECUMappings] = useState([])
|
||||||
|
const [totalFlashpackECUMappings, setTotalFlashpackECUMappings] = useState(0)
|
||||||
|
|
||||||
const addConnections = async (cars, token) => {
|
const addConnections = async (cars, token) => {
|
||||||
try {
|
try {
|
||||||
@@ -287,6 +291,90 @@ export const VehicleProvider = ({ children }) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getAllFlashpacks = async (options, token) => {
|
||||||
|
try {
|
||||||
|
setBusy(true);
|
||||||
|
|
||||||
|
const result = await api.getAllFlashpacks(options, token);
|
||||||
|
if (result.error) {
|
||||||
|
throw new Error(`Get all flashpacks error. ${result.message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
setFlashpacks(result.data);
|
||||||
|
if (options && options.offset === 0 && result.total) {
|
||||||
|
setTotalFlashpacks(result.total);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
} finally {
|
||||||
|
setBusy(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getFlashpackECUMappings = async (model, year, flashpack, options, token) => {
|
||||||
|
try {
|
||||||
|
setBusy(true);
|
||||||
|
|
||||||
|
const result = await api.getFlashpackECUMappings(model, year, flashpack, options, token);
|
||||||
|
if (result.error) {
|
||||||
|
throw new Error(`Get flashpack ecu mappings error. ${result.message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
setFlashpackECUMappings(result.data);
|
||||||
|
if (options && options.offset === 0 && result.total) {
|
||||||
|
setTotalFlashpackECUMappings(result.total);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
} finally {
|
||||||
|
setBusy(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const addFlashpackVersion = async (model, year, flashpack, carFlashpackVersions, token) => {
|
||||||
|
try {
|
||||||
|
setBusy(true);
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
"car_model": model,
|
||||||
|
"car_year": year,
|
||||||
|
"flashpack": flashpack,
|
||||||
|
"car_flashpack_versions": carFlashpackVersions,
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await api.addFlashpackVersion(data, token)
|
||||||
|
if (result.error) {
|
||||||
|
throw new Error(`Add flashpack version error. ${result.message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
} finally {
|
||||||
|
setBusy(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteFlashpackVersion = async (model, year, flashpack, token) => {
|
||||||
|
try {
|
||||||
|
setBusy(true);
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
"car_model": model,
|
||||||
|
"car_year": year,
|
||||||
|
"flashpack": flashpack,
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await api.deleteFlashpackVersion(data, token);
|
||||||
|
if (result.error) {
|
||||||
|
throw new Error(`Delete flashpack ecu mappings error. ${result.message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
} finally {
|
||||||
|
setBusy(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<VehicleContext.Provider
|
<VehicleContext.Provider
|
||||||
value={{
|
value={{
|
||||||
@@ -316,6 +404,14 @@ export const VehicleProvider = ({ children }) => {
|
|||||||
getVersionLog,
|
getVersionLog,
|
||||||
uploadConfig,
|
uploadConfig,
|
||||||
addTags,
|
addTags,
|
||||||
|
flashpacks,
|
||||||
|
totalFlashpacks,
|
||||||
|
getAllFlashpacks,
|
||||||
|
totalFlashpackECUMappings,
|
||||||
|
flashpackECUMappings,
|
||||||
|
getFlashpackECUMappings,
|
||||||
|
addFlashpackVersion,
|
||||||
|
deleteFlashpackVersion,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@@ -187,6 +187,44 @@ export const useVehicleContext = () => ({
|
|||||||
total: 2,
|
total: 2,
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
getAllFlashpacks: jest.fn((options, token) => {
|
||||||
|
return {
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
flashpack: "44.4",
|
||||||
|
car_model: "Ocean",
|
||||||
|
car_year: "2023",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
flashpack: "38.4",
|
||||||
|
car_model: "Ocean",
|
||||||
|
car_year: "2023",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
total: 2,
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
getFlashpackECUMappings: jest.fn((model, year, flashpack, options, token) => {
|
||||||
|
return {
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
flashpack: "44.4",
|
||||||
|
car_model: "Ocean",
|
||||||
|
car_year: "2023",
|
||||||
|
car_ecu_name: "ADAS",
|
||||||
|
car_ecu_version: "99",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
flashpack: "44.4",
|
||||||
|
car_model: "Ocean",
|
||||||
|
car_year: "2023",
|
||||||
|
car_ecu_name: "PDI",
|
||||||
|
car_ecu_version: "11",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
total: 2,
|
||||||
|
}
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const setBusy = (val) => {
|
export const setBusy = (val) => {
|
||||||
|
|||||||
293
src/components/Flashpack/Add/__snapshots__/index.test.jsx.snap
Normal file
293
src/components/Flashpack/Add/__snapshots__/index.test.jsx.snap
Normal file
@@ -0,0 +1,293 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`FlashpackAdd Render 1`] = `
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
data-testid="mocked-vehicleprovider"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
data-testid="mocked-statusprovider"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
data-testid="mocked-userprovider"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
data-testid="mocked-vehicleprovider"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="makeStyles-paper-0"
|
||||||
|
>
|
||||||
|
<form
|
||||||
|
action="{onSubmit}"
|
||||||
|
class="makeStyles-form-0"
|
||||||
|
novalidate=""
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="MuiFormControl-root MuiTextField-root MuiFormControl-marginNormal MuiFormControl-fullWidth"
|
||||||
|
>
|
||||||
|
<label
|
||||||
|
class="MuiFormLabel-root MuiInputLabel-root MuiInputLabel-formControl MuiInputLabel-animated MuiInputLabel-outlined Mui-required Mui-required"
|
||||||
|
data-shrink="false"
|
||||||
|
for="carModel"
|
||||||
|
id="carModel-label"
|
||||||
|
>
|
||||||
|
Model
|
||||||
|
<span
|
||||||
|
aria-hidden="true"
|
||||||
|
class="MuiFormLabel-asterisk MuiInputLabel-asterisk"
|
||||||
|
>
|
||||||
|
|
||||||
|
*
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<div
|
||||||
|
class="MuiInputBase-root MuiOutlinedInput-root MuiInputBase-fullWidth MuiInputBase-formControl"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
aria-invalid="false"
|
||||||
|
class="MuiInputBase-input MuiOutlinedInput-input"
|
||||||
|
id="carModel"
|
||||||
|
maxlength="255"
|
||||||
|
name="carModel"
|
||||||
|
required=""
|
||||||
|
type="text"
|
||||||
|
value=""
|
||||||
|
/>
|
||||||
|
<fieldset
|
||||||
|
aria-hidden="true"
|
||||||
|
class="PrivateNotchedOutline-root-0 MuiOutlinedInput-notchedOutline"
|
||||||
|
>
|
||||||
|
<legend
|
||||||
|
class="PrivateNotchedOutline-legendLabelled-0"
|
||||||
|
>
|
||||||
|
<span>
|
||||||
|
Model
|
||||||
|
*
|
||||||
|
</span>
|
||||||
|
</legend>
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="MuiFormControl-root MuiTextField-root MuiFormControl-marginNormal MuiFormControl-fullWidth"
|
||||||
|
>
|
||||||
|
<label
|
||||||
|
class="MuiFormLabel-root MuiInputLabel-root MuiInputLabel-formControl MuiInputLabel-animated MuiInputLabel-outlined Mui-required Mui-required"
|
||||||
|
data-shrink="false"
|
||||||
|
for="carYear"
|
||||||
|
id="carYear-label"
|
||||||
|
>
|
||||||
|
Year
|
||||||
|
<span
|
||||||
|
aria-hidden="true"
|
||||||
|
class="MuiFormLabel-asterisk MuiInputLabel-asterisk"
|
||||||
|
>
|
||||||
|
|
||||||
|
*
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<div
|
||||||
|
class="MuiInputBase-root MuiOutlinedInput-root MuiInputBase-fullWidth MuiInputBase-formControl"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
aria-invalid="false"
|
||||||
|
class="MuiInputBase-input MuiOutlinedInput-input"
|
||||||
|
id="carYear"
|
||||||
|
maxlength="255"
|
||||||
|
name="carYear"
|
||||||
|
required=""
|
||||||
|
type="number"
|
||||||
|
value=""
|
||||||
|
/>
|
||||||
|
<fieldset
|
||||||
|
aria-hidden="true"
|
||||||
|
class="PrivateNotchedOutline-root-0 MuiOutlinedInput-notchedOutline"
|
||||||
|
>
|
||||||
|
<legend
|
||||||
|
class="PrivateNotchedOutline-legendLabelled-0"
|
||||||
|
>
|
||||||
|
<span>
|
||||||
|
Year
|
||||||
|
*
|
||||||
|
</span>
|
||||||
|
</legend>
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="MuiFormControl-root MuiTextField-root MuiFormControl-marginNormal MuiFormControl-fullWidth"
|
||||||
|
>
|
||||||
|
<label
|
||||||
|
class="MuiFormLabel-root MuiInputLabel-root MuiInputLabel-formControl MuiInputLabel-animated MuiInputLabel-outlined Mui-required Mui-required"
|
||||||
|
data-shrink="false"
|
||||||
|
for="flashpack"
|
||||||
|
id="flashpack-label"
|
||||||
|
>
|
||||||
|
Flashpack Number
|
||||||
|
<span
|
||||||
|
aria-hidden="true"
|
||||||
|
class="MuiFormLabel-asterisk MuiInputLabel-asterisk"
|
||||||
|
>
|
||||||
|
|
||||||
|
*
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<div
|
||||||
|
class="MuiInputBase-root MuiOutlinedInput-root MuiInputBase-fullWidth MuiInputBase-formControl"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
aria-invalid="false"
|
||||||
|
class="MuiInputBase-input MuiOutlinedInput-input"
|
||||||
|
id="flashpack"
|
||||||
|
maxlength="255"
|
||||||
|
name="flashpack"
|
||||||
|
required=""
|
||||||
|
type="number"
|
||||||
|
value=""
|
||||||
|
/>
|
||||||
|
<fieldset
|
||||||
|
aria-hidden="true"
|
||||||
|
class="PrivateNotchedOutline-root-0 MuiOutlinedInput-notchedOutline"
|
||||||
|
>
|
||||||
|
<legend
|
||||||
|
class="PrivateNotchedOutline-legendLabelled-0"
|
||||||
|
>
|
||||||
|
<span>
|
||||||
|
Flashpack Number
|
||||||
|
*
|
||||||
|
</span>
|
||||||
|
</legend>
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="container"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="input_container"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="MuiFormControl-root MuiTextField-root MuiFormControl-marginNormal"
|
||||||
|
>
|
||||||
|
<label
|
||||||
|
class="MuiFormLabel-root MuiInputLabel-root MuiInputLabel-formControl MuiInputLabel-animated MuiInputLabel-outlined"
|
||||||
|
data-shrink="false"
|
||||||
|
for="ecuName"
|
||||||
|
id="ecuName-label"
|
||||||
|
>
|
||||||
|
ECU Name
|
||||||
|
</label>
|
||||||
|
<div
|
||||||
|
class="MuiInputBase-root MuiOutlinedInput-root MuiInputBase-formControl"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
aria-invalid="false"
|
||||||
|
class="MuiInputBase-input MuiOutlinedInput-input"
|
||||||
|
id="ecuName"
|
||||||
|
maxlength="255"
|
||||||
|
name="ecuName"
|
||||||
|
type="text"
|
||||||
|
value=""
|
||||||
|
/>
|
||||||
|
<fieldset
|
||||||
|
aria-hidden="true"
|
||||||
|
class="PrivateNotchedOutline-root-0 MuiOutlinedInput-notchedOutline"
|
||||||
|
>
|
||||||
|
<legend
|
||||||
|
class="PrivateNotchedOutline-legendLabelled-0"
|
||||||
|
>
|
||||||
|
<span>
|
||||||
|
ECU Name
|
||||||
|
</span>
|
||||||
|
</legend>
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="MuiFormControl-root MuiTextField-root MuiFormControl-marginNormal"
|
||||||
|
>
|
||||||
|
<label
|
||||||
|
class="MuiFormLabel-root MuiInputLabel-root MuiInputLabel-formControl MuiInputLabel-animated MuiInputLabel-outlined"
|
||||||
|
data-shrink="false"
|
||||||
|
for="ecuVersion"
|
||||||
|
id="ecuVersion-label"
|
||||||
|
>
|
||||||
|
ECU Version
|
||||||
|
</label>
|
||||||
|
<div
|
||||||
|
class="MuiInputBase-root MuiOutlinedInput-root MuiInputBase-formControl"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
aria-invalid="false"
|
||||||
|
class="MuiInputBase-input MuiOutlinedInput-input"
|
||||||
|
id="ecuVersion"
|
||||||
|
maxlength="255"
|
||||||
|
name="ecuVersion"
|
||||||
|
type="text"
|
||||||
|
value=""
|
||||||
|
/>
|
||||||
|
<fieldset
|
||||||
|
aria-hidden="true"
|
||||||
|
class="PrivateNotchedOutline-root-0 MuiOutlinedInput-notchedOutline"
|
||||||
|
>
|
||||||
|
<legend
|
||||||
|
class="PrivateNotchedOutline-legendLabelled-0"
|
||||||
|
>
|
||||||
|
<span>
|
||||||
|
ECU Version
|
||||||
|
</span>
|
||||||
|
</legend>
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
aria-label="Add"
|
||||||
|
class="MuiButtonBase-root MuiIconButton-root MuiIconButton-colorPrimary MuiIconButton-sizeSmall"
|
||||||
|
tabindex="0"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="MuiIconButton-label"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
aria-label="Add"
|
||||||
|
class="MuiSvgIcon-root"
|
||||||
|
focusable="false"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm5 11h-4v4h-2v-4H7v-2h4V7h2v4h4v2z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
class="MuiTouchRipple-root"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
class="MuiButtonBase-root MuiButton-root MuiButton-contained makeStyles-submit-0 MuiButton-containedPrimary MuiButton-fullWidth"
|
||||||
|
tabindex="0"
|
||||||
|
type="submit"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="MuiButton-label"
|
||||||
|
>
|
||||||
|
Submit
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
class="MuiTouchRipple-root"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
239
src/components/Flashpack/Add/index.jsx
Normal file
239
src/components/Flashpack/Add/index.jsx
Normal file
@@ -0,0 +1,239 @@
|
|||||||
|
import {
|
||||||
|
Button,
|
||||||
|
IconButton,
|
||||||
|
TextField
|
||||||
|
} from "@material-ui/core";
|
||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import AddCircleIcon from "@material-ui/icons/AddCircle";
|
||||||
|
import DeleteIcon from "@material-ui/icons/Delete";
|
||||||
|
import { Redirect } from "react-router";
|
||||||
|
import { logger } from "../../../services/monitoring";
|
||||||
|
import { useStatusContext } from "../../Contexts/StatusContext";
|
||||||
|
import { useVehicleContext, VehicleProvider } from "../../Contexts/VehicleContext";
|
||||||
|
import { useUserContext } from "../../Contexts/UserContext";
|
||||||
|
import useStyles from "../../useStyles";
|
||||||
|
|
||||||
|
const MainForm = () => {
|
||||||
|
const {
|
||||||
|
token: {
|
||||||
|
idToken: { jwtToken: token },
|
||||||
|
},
|
||||||
|
} = useUserContext();
|
||||||
|
const classes = useStyles();
|
||||||
|
const [redirect, setRedirect] = useState(null);
|
||||||
|
const { setMessage, setTitle, setSitePath } = useStatusContext();
|
||||||
|
const [carModel, setCarModel] = useState("");
|
||||||
|
const [carYear, setCarYear] = useState();
|
||||||
|
const [flashpack, setFlashpack] = useState();
|
||||||
|
const [mappingInputs, setMappingInputs] = useState([{ ecuName: "", ecuVersion: "" }]);
|
||||||
|
const {
|
||||||
|
addFlashpackVersion,
|
||||||
|
busy,
|
||||||
|
} = useVehicleContext();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setTitle(`Add Flashpack Version`);
|
||||||
|
setSitePath([
|
||||||
|
{
|
||||||
|
label: "Tools",
|
||||||
|
link: "/tools/flashpacks",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Flashpack Versions",
|
||||||
|
link: "/tools/flashpacks",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: `Add Flashpack Version`,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const onCarModelChange = (event) => {
|
||||||
|
setCarModel(event.target.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
const onCarYearChange = (event) => {
|
||||||
|
setCarYear(event.target.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
const onFlashpackChange = (event) => {
|
||||||
|
setFlashpack(event.target.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
const onSubmit = async (event) => {
|
||||||
|
try {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
const carFlashpackVersions = []
|
||||||
|
for (let i = 0; i < mappingInputs.length; i++) {
|
||||||
|
mappingInputs[i] && mappingInputs[i].ecuName && mappingInputs[i].ecuVersion &&
|
||||||
|
carFlashpackVersions.push({
|
||||||
|
"car_model": carModel,
|
||||||
|
"car_year": parseInt(carYear),
|
||||||
|
"flashpack": flashpack,
|
||||||
|
"car_ecu_name": mappingInputs[i].ecuName,
|
||||||
|
"car_ecu_version": mappingInputs[i].ecuVersion,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await addFlashpackVersion(carModel, parseInt(carYear), flashpack, carFlashpackVersions, token);
|
||||||
|
if (!result || result.error) return;
|
||||||
|
|
||||||
|
setMessage(`Added ${carYear} ${carModel} ${flashpack}`);
|
||||||
|
setRedirect(`/tools/flashpack/${carModel}/${carYear}/${flashpack}`);
|
||||||
|
} catch (e) {
|
||||||
|
setMessage(e.message);
|
||||||
|
logger.warn(e.stack);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (redirect && redirect.length > 0) {
|
||||||
|
return <Redirect to={redirect} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
// code for handling the dynamic input fields adapted from
|
||||||
|
// https://blog.stackademic.com/how-to-dynamically-add-input-fields-on-button-click-in-reactjs-ddf8d8fe495b
|
||||||
|
|
||||||
|
const handleAddMappingInput = () => {
|
||||||
|
setMappingInputs([...mappingInputs, { ecuName: "", ecuVersion: "", }]);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleChange = (event, index) => {
|
||||||
|
let { name, value } = event.target;
|
||||||
|
let onChangeValue = [...mappingInputs];
|
||||||
|
onChangeValue[index][name] = value;
|
||||||
|
setMappingInputs(onChangeValue);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDeleteMappingInput = (index) => {
|
||||||
|
const newArray = [...mappingInputs];
|
||||||
|
newArray.splice(index, 1);
|
||||||
|
setMappingInputs(newArray);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={classes.paper}>
|
||||||
|
<form className={classes.form} noValidate action="{onSubmit}">
|
||||||
|
<div>
|
||||||
|
<TextField
|
||||||
|
id="carModel"
|
||||||
|
name="carModel"
|
||||||
|
label="Model"
|
||||||
|
variant="outlined"
|
||||||
|
margin="normal"
|
||||||
|
inputProps={{
|
||||||
|
maxLength: "255",
|
||||||
|
}}
|
||||||
|
required
|
||||||
|
fullWidth
|
||||||
|
value={carModel}
|
||||||
|
onChange={onCarModelChange}
|
||||||
|
type="text"
|
||||||
|
/>
|
||||||
|
<TextField
|
||||||
|
id="carYear"
|
||||||
|
name="carYear"
|
||||||
|
label="Year"
|
||||||
|
variant="outlined"
|
||||||
|
margin="normal"
|
||||||
|
inputProps={{
|
||||||
|
maxLength: "255",
|
||||||
|
}}
|
||||||
|
required
|
||||||
|
fullWidth
|
||||||
|
value={carYear}
|
||||||
|
onChange={onCarYearChange}
|
||||||
|
type="number"
|
||||||
|
/>
|
||||||
|
<TextField
|
||||||
|
id="flashpack"
|
||||||
|
name="flashpack"
|
||||||
|
label="Flashpack Number"
|
||||||
|
variant="outlined"
|
||||||
|
margin="normal"
|
||||||
|
inputProps={{
|
||||||
|
maxLength: "255",
|
||||||
|
}}
|
||||||
|
required
|
||||||
|
fullWidth
|
||||||
|
value={flashpack}
|
||||||
|
onChange={onFlashpackChange}
|
||||||
|
type="number"
|
||||||
|
/>
|
||||||
|
<div className="container">
|
||||||
|
{mappingInputs.map((item, index) => (
|
||||||
|
<div className="input_container" key={index}>
|
||||||
|
<TextField
|
||||||
|
id="ecuName"
|
||||||
|
name="ecuName"
|
||||||
|
label="ECU Name"
|
||||||
|
variant="outlined"
|
||||||
|
margin="normal"
|
||||||
|
inputProps={{
|
||||||
|
maxLength: "255",
|
||||||
|
}}
|
||||||
|
value={item.ecuName}
|
||||||
|
onChange={(event) => handleChange(event, index)}
|
||||||
|
type="text"
|
||||||
|
/>
|
||||||
|
<TextField
|
||||||
|
id="ecuVersion"
|
||||||
|
name="ecuVersion"
|
||||||
|
label="ECU Version"
|
||||||
|
variant="outlined"
|
||||||
|
margin="normal"
|
||||||
|
inputProps={{
|
||||||
|
maxLength: "255",
|
||||||
|
}}
|
||||||
|
value={item.ecuVersion}
|
||||||
|
onChange={(event) => handleChange(event, index)}
|
||||||
|
type="text"
|
||||||
|
/>
|
||||||
|
{mappingInputs.length > 1 && (
|
||||||
|
<IconButton
|
||||||
|
onClick={() => handleDeleteMappingInput(index)}
|
||||||
|
aria-label={`Delete`}
|
||||||
|
size="small"
|
||||||
|
color="primary"
|
||||||
|
>
|
||||||
|
<DeleteIcon aria-label={`Delete`} />
|
||||||
|
</IconButton>
|
||||||
|
)}
|
||||||
|
{index === mappingInputs.length - 1 && (
|
||||||
|
<IconButton
|
||||||
|
onClick={() => handleAddMappingInput()}
|
||||||
|
aria-label={`Add`}
|
||||||
|
size="small"
|
||||||
|
color="primary"
|
||||||
|
>
|
||||||
|
<AddCircleIcon aria-label={`Add`} />
|
||||||
|
</IconButton>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
disabled={busy}
|
||||||
|
fullWidth
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
className={classes.submit}
|
||||||
|
onClick={onSubmit}
|
||||||
|
>
|
||||||
|
{busy ? "Submitting..." : "Submit"}
|
||||||
|
</Button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const FlashpackAdd = () => (
|
||||||
|
<VehicleProvider>
|
||||||
|
<MainForm />
|
||||||
|
</VehicleProvider>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default FlashpackAdd;
|
||||||
46
src/components/Flashpack/Add/index.test.jsx
Normal file
46
src/components/Flashpack/Add/index.test.jsx
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
jest.mock("../../Contexts/VehicleContext");
|
||||||
|
jest.mock("../../Contexts/StatusContext");
|
||||||
|
jest.mock("../../Contexts/UserContext");
|
||||||
|
jest.mock("@material-ui/core/utils/unstable_useId", () =>
|
||||||
|
jest.fn().mockReturnValue("mui-test-id")
|
||||||
|
);
|
||||||
|
import { render, waitFor } from "@testing-library/react";
|
||||||
|
import { MemoryRouter, Route } from "react-router-dom";
|
||||||
|
import { VehicleProvider } from "../../Contexts/VehicleContext";
|
||||||
|
import { StatusProvider } from "../../Contexts/StatusContext";
|
||||||
|
import { UserProvider, setToken } from "../../Contexts/UserContext";
|
||||||
|
import { TEST_AUTH_OBJECT_FISKER } from "../../../utils/testing";
|
||||||
|
import MainForm from "./index";
|
||||||
|
import addSnapshotSerializer from "../../../utils/snapshot";
|
||||||
|
|
||||||
|
const renderFlashpackAdd = async () => {
|
||||||
|
const { container } = render(
|
||||||
|
<VehicleProvider>
|
||||||
|
<StatusProvider>
|
||||||
|
<UserProvider>
|
||||||
|
<MemoryRouter initialEntries={["/tools/flashpack/add"]}>
|
||||||
|
<Route path="/tools/flashpack/add">
|
||||||
|
<MainForm />
|
||||||
|
</Route>
|
||||||
|
</MemoryRouter>
|
||||||
|
</UserProvider>
|
||||||
|
</StatusProvider>
|
||||||
|
</VehicleProvider>
|
||||||
|
);
|
||||||
|
await waitFor(() => {
|
||||||
|
/* render */
|
||||||
|
});
|
||||||
|
return container;
|
||||||
|
};
|
||||||
|
|
||||||
|
describe("FlashpackAdd", () => {
|
||||||
|
beforeAll(() => {
|
||||||
|
addSnapshotSerializer(expect);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Render", async () => {
|
||||||
|
setToken(TEST_AUTH_OBJECT_FISKER);
|
||||||
|
const container = await renderFlashpackAdd();
|
||||||
|
expect(container).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`FlashpackDetails Render 1`] = `
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
data-testid="mocked-vehicleprovider"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
data-testid="mocked-statusprovider"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
data-testid="mocked-userprovider"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
data-testid="mocked-vehicleprovider"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<table
|
||||||
|
class="MuiTable-root"
|
||||||
|
>
|
||||||
|
<thead
|
||||||
|
class="MuiTableHead-root"
|
||||||
|
>
|
||||||
|
<tr
|
||||||
|
class="MuiTableRow-root MuiTableRow-head"
|
||||||
|
>
|
||||||
|
<th
|
||||||
|
class="MuiTableCell-root MuiTableCell-head MuiTableCell-alignCenter"
|
||||||
|
scope="col"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
aria-disabled="false"
|
||||||
|
class="MuiButtonBase-root MuiTableSortLabel-root"
|
||||||
|
role="button"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
ECU Name
|
||||||
|
<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"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
aria-disabled="false"
|
||||||
|
class="MuiButtonBase-root MuiTableSortLabel-root"
|
||||||
|
role="button"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
ECU Version
|
||||||
|
<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>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody
|
||||||
|
class="MuiTableBody-root"
|
||||||
|
/>
|
||||||
|
<tfoot
|
||||||
|
class="MuiTableFooter-root"
|
||||||
|
>
|
||||||
|
<tr
|
||||||
|
class="MuiTableRow-root MuiTableRow-footer"
|
||||||
|
>
|
||||||
|
<td
|
||||||
|
class="MuiTableCell-root MuiTableCell-footer MuiTableCell-alignCenter"
|
||||||
|
colspan="8"
|
||||||
|
>
|
||||||
|
No Flashpack ECU Mappings
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tfoot>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
173
src/components/Flashpack/Details/index.jsx
Normal file
173
src/components/Flashpack/Details/index.jsx
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import { useParams } from "react-router-dom";
|
||||||
|
import {
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableFooter,
|
||||||
|
TablePagination,
|
||||||
|
TableRow,
|
||||||
|
} from "@material-ui/core";
|
||||||
|
import { logger } from "../../../services/monitoring";
|
||||||
|
import { useVehicleContext, VehicleProvider } from "../../Contexts/VehicleContext";
|
||||||
|
import { useStatusContext } from "../../Contexts/StatusContext";
|
||||||
|
import { useUserContext } from "../../Contexts/UserContext";
|
||||||
|
import TableHeaderSortable from "../../Table/HeaderSortable";
|
||||||
|
import { useLocalStorage } from "../../useLocalStorage";
|
||||||
|
import useStyles from "../../useStyles";
|
||||||
|
|
||||||
|
const tableColumns = [
|
||||||
|
{
|
||||||
|
id: "car_ecu_name",
|
||||||
|
label: "ECU Name",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "car_ecu_version",
|
||||||
|
label: "ECU Version",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const PAGE_SIZE = "FLASHPACK_MAPPINGS_TABLE_PAGE_SIZE";
|
||||||
|
|
||||||
|
const MainForm = () => {
|
||||||
|
const { model, year, flashpack } = useParams();
|
||||||
|
const classes = useStyles();
|
||||||
|
const { setMessage, setTitle, setSitePath } = useStatusContext();
|
||||||
|
const [pageSize, setPageSize] = useLocalStorage(PAGE_SIZE, 10);
|
||||||
|
const [pageIndex, setPageIndex] = useState(0);
|
||||||
|
const [orderBy, setOrderBy] = useState("flashpack");
|
||||||
|
const [order, setOrder] = useState("desc");
|
||||||
|
const {
|
||||||
|
getFlashpackECUMappings,
|
||||||
|
flashpackECUMappings,
|
||||||
|
totalFlashpackECUMappings,
|
||||||
|
} = useVehicleContext();
|
||||||
|
const {
|
||||||
|
token: {
|
||||||
|
idToken: { jwtToken: token },
|
||||||
|
},
|
||||||
|
} = useUserContext();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setTitle(`${year} ${model} Flashpack Version ${flashpack}`);
|
||||||
|
setSitePath([
|
||||||
|
{
|
||||||
|
label: "Tools",
|
||||||
|
link: "/tools/flashpacks",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Flashpack Versions",
|
||||||
|
link: "/tools/flashpacks",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: `Flashpack ${flashpack}`,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
loadFlashpackECUMappings();
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [token, token, pageIndex, pageSize, orderBy, order]);
|
||||||
|
|
||||||
|
const loadFlashpackECUMappings = async () => {
|
||||||
|
try {
|
||||||
|
if (!token) return;
|
||||||
|
await getFlashpackECUMappings(
|
||||||
|
model,
|
||||||
|
year,
|
||||||
|
flashpack,
|
||||||
|
{
|
||||||
|
limit: pageSize,
|
||||||
|
offset: pageSize * pageIndex,
|
||||||
|
order: `${orderBy} ${order}`,
|
||||||
|
},
|
||||||
|
token
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
setMessage(e.message);
|
||||||
|
logger.warn(e.stack);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleChangePageIndex = (event, newIndex) => {
|
||||||
|
setPageIndex(newIndex);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleChangePageSize = (event) => {
|
||||||
|
setPageSize(parseInt(event.target.value, 10));
|
||||||
|
setPageIndex(0);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSort = (event, property) => {
|
||||||
|
try {
|
||||||
|
if (property === orderBy) {
|
||||||
|
if (order === "asc") {
|
||||||
|
setOrder("desc");
|
||||||
|
} else {
|
||||||
|
setOrder("asc");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setOrderBy(property);
|
||||||
|
setOrder("asc");
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
logger.warn(e.stack);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Table>
|
||||||
|
<TableHeaderSortable
|
||||||
|
classes={classes}
|
||||||
|
orderBy={orderBy}
|
||||||
|
order={order}
|
||||||
|
columnData={tableColumns}
|
||||||
|
onSortRequest={handleSort}
|
||||||
|
/>
|
||||||
|
<TableBody>
|
||||||
|
{flashpackECUMappings && flashpackECUMappings.map((row, index) => (
|
||||||
|
<TableRow key={index}>
|
||||||
|
<TableCell align="center">
|
||||||
|
{row.car_ecu_name}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell align="center">
|
||||||
|
{row.car_ecu_version}
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableBody>
|
||||||
|
<TableFooter>
|
||||||
|
<TableRow>
|
||||||
|
{!flashpackECUMappings || flashpackECUMappings.length === 0 ? (
|
||||||
|
<TableCell colSpan={8} align="center">No Flashpack ECU Mappings</TableCell>
|
||||||
|
) : (
|
||||||
|
<TablePagination
|
||||||
|
rowsPerPageOptions={[5, 10, 25, 100]}
|
||||||
|
colSpan={2}
|
||||||
|
count={totalFlashpackECUMappings}
|
||||||
|
rowsPerPage={pageSize}
|
||||||
|
page={pageIndex}
|
||||||
|
SelectProps={{
|
||||||
|
inputProps: { "aria-label": "rows per page" },
|
||||||
|
native: true,
|
||||||
|
}}
|
||||||
|
onPageChange={handleChangePageIndex}
|
||||||
|
onRowsPerPageChange={handleChangePageSize}
|
||||||
|
/>)}
|
||||||
|
</TableRow>
|
||||||
|
</TableFooter>
|
||||||
|
</Table>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const FlashpackDetails = () => (
|
||||||
|
<VehicleProvider>
|
||||||
|
<MainForm />
|
||||||
|
</VehicleProvider>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default FlashpackDetails;
|
||||||
46
src/components/Flashpack/Details/index.test.jsx
Normal file
46
src/components/Flashpack/Details/index.test.jsx
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
jest.mock("../../Contexts/VehicleContext");
|
||||||
|
jest.mock("../../Contexts/StatusContext");
|
||||||
|
jest.mock("../../Contexts/UserContext");
|
||||||
|
jest.mock("@material-ui/core/utils/unstable_useId", () =>
|
||||||
|
jest.fn().mockReturnValue("mui-test-id")
|
||||||
|
);
|
||||||
|
import { render, waitFor } from "@testing-library/react";
|
||||||
|
import { MemoryRouter, Route } from "react-router-dom";
|
||||||
|
import { VehicleProvider } from "../../Contexts/VehicleContext";
|
||||||
|
import { StatusProvider } from "../../Contexts/StatusContext";
|
||||||
|
import { UserProvider, setToken } from "../../Contexts/UserContext";
|
||||||
|
import { TEST_AUTH_OBJECT_FISKER } from "../../../utils/testing";
|
||||||
|
import MainForm from "./index";
|
||||||
|
import addSnapshotSerializer from "../../../utils/snapshot";
|
||||||
|
|
||||||
|
const renderFlashpackDetails = async () => {
|
||||||
|
const { container } = render(
|
||||||
|
<VehicleProvider>
|
||||||
|
<StatusProvider>
|
||||||
|
<UserProvider>
|
||||||
|
<MemoryRouter initialEntries={["/tools/flashpack/Ocean/2023/44.4"]}>
|
||||||
|
<Route path="/tools/flashpack/:carModel/:carYear/:flashpack">
|
||||||
|
<MainForm carModel="Ocean" carYear="2023" flashpack="44.4" />
|
||||||
|
</Route>
|
||||||
|
</MemoryRouter>
|
||||||
|
</UserProvider>
|
||||||
|
</StatusProvider>
|
||||||
|
</VehicleProvider>
|
||||||
|
);
|
||||||
|
await waitFor(() => {
|
||||||
|
/* render */
|
||||||
|
});
|
||||||
|
return container;
|
||||||
|
};
|
||||||
|
|
||||||
|
describe("FlashpackDetails", () => {
|
||||||
|
beforeAll(() => {
|
||||||
|
addSnapshotSerializer(expect);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Render", async () => {
|
||||||
|
setToken(TEST_AUTH_OBJECT_FISKER);
|
||||||
|
const container = await renderFlashpackDetails();
|
||||||
|
expect(container).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
||||||
175
src/components/Flashpack/__snapshots__/index.test.jsx.snap
Normal file
175
src/components/Flashpack/__snapshots__/index.test.jsx.snap
Normal file
@@ -0,0 +1,175 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`Flashpack Render 1`] = `
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
data-testid="mocked-vehicleprovider"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
data-testid="mocked-statusprovider"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
data-testid="mocked-userprovider"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
data-testid="mocked-vehicleprovider"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="MuiGrid-root makeStyles-root-0 MuiGrid-container MuiGrid-spacing-xs-2"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="MuiGrid-root makeStyles-textJustifyAlign-0 makeStyles-actionsBar-0 MuiGrid-item MuiGrid-grid-md-4"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
class="makeStyles-labelInline-0"
|
||||||
|
href="/tools/flashpack/add"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class="MuiSvgIcon-root MuiSvgIcon-fontSizeLarge"
|
||||||
|
focusable="false"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm5 11h-4v4h-2v-4H7v-2h4V7h2v4h4v2z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<table
|
||||||
|
class="MuiTable-root"
|
||||||
|
>
|
||||||
|
<thead
|
||||||
|
class="MuiTableHead-root"
|
||||||
|
>
|
||||||
|
<tr
|
||||||
|
class="MuiTableRow-root MuiTableRow-head"
|
||||||
|
>
|
||||||
|
<th
|
||||||
|
aria-sort="descending"
|
||||||
|
class="MuiTableCell-root MuiTableCell-head MuiTableCell-alignCenter"
|
||||||
|
scope="col"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
aria-disabled="false"
|
||||||
|
class="MuiButtonBase-root MuiTableSortLabel-root MuiTableSortLabel-active"
|
||||||
|
role="button"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
Flashpack Number
|
||||||
|
<span
|
||||||
|
class="makeStyles-hiddenSortSpan-0"
|
||||||
|
>
|
||||||
|
sorted descending
|
||||||
|
</span>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class="MuiSvgIcon-root MuiTableSortLabel-icon MuiTableSortLabel-iconDirectionDesc"
|
||||||
|
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"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
aria-disabled="false"
|
||||||
|
class="MuiButtonBase-root MuiTableSortLabel-root"
|
||||||
|
role="button"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
Model
|
||||||
|
<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"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
aria-disabled="false"
|
||||||
|
class="MuiButtonBase-root MuiTableSortLabel-root"
|
||||||
|
role="button"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
Year
|
||||||
|
<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"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
aria-disabled="false"
|
||||||
|
class="MuiButtonBase-root MuiTableSortLabel-root"
|
||||||
|
role="button"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
Delete
|
||||||
|
<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>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody
|
||||||
|
class="MuiTableBody-root"
|
||||||
|
/>
|
||||||
|
<tfoot
|
||||||
|
class="MuiTableFooter-root"
|
||||||
|
>
|
||||||
|
<tr
|
||||||
|
class="MuiTableRow-root MuiTableRow-footer"
|
||||||
|
>
|
||||||
|
<td
|
||||||
|
class="MuiTableCell-root MuiTableCell-footer MuiTableCell-alignCenter"
|
||||||
|
colspan="8"
|
||||||
|
>
|
||||||
|
No Flashpack Numbers
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tfoot>
|
||||||
|
</table>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
219
src/components/Flashpack/index.jsx
Normal file
219
src/components/Flashpack/index.jsx
Normal file
@@ -0,0 +1,219 @@
|
|||||||
|
import {
|
||||||
|
Grid,
|
||||||
|
IconButton,
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableFooter,
|
||||||
|
TablePagination,
|
||||||
|
TableRow,
|
||||||
|
} from "@material-ui/core";
|
||||||
|
import AddCircleIcon from "@material-ui/icons/AddCircle";
|
||||||
|
import DeleteIcon from "@material-ui/icons/Delete";
|
||||||
|
import clsx from "clsx";
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
|
import { logger } from "../../services/monitoring";
|
||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import { useVehicleContext, VehicleProvider } from "../Contexts/VehicleContext";
|
||||||
|
import { useStatusContext } from "../Contexts/StatusContext";
|
||||||
|
import { useUserContext } from "../Contexts/UserContext";
|
||||||
|
import TableHeaderSortable from "../Table/HeaderSortable";
|
||||||
|
import { useLocalStorage } from "../useLocalStorage";
|
||||||
|
import DeleteConfirmation from "../DeleteConfirmation";
|
||||||
|
import { RoleWrap } from "../Controls/RoleWrap";
|
||||||
|
import useStyles from "../useStyles";
|
||||||
|
|
||||||
|
const tableColumns = [
|
||||||
|
{
|
||||||
|
id: "flashpack",
|
||||||
|
label: "Flashpack Number",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "car_model",
|
||||||
|
label: "Model",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "car_year",
|
||||||
|
label: "Year",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "delete",
|
||||||
|
label: "Delete",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const PAGE_SIZE = "FLASHPACKS_TABLE_PAGE_SIZE";
|
||||||
|
|
||||||
|
const MainForm = () => {
|
||||||
|
const classes = useStyles();
|
||||||
|
const { setMessage, setTitle, setSitePath } = useStatusContext();
|
||||||
|
const [pageSize, setPageSize] = useLocalStorage(PAGE_SIZE, 10);
|
||||||
|
const [pageIndex, setPageIndex] = useState(0);
|
||||||
|
const [showDeleteModal, setShowDeleteModal] = useState(false);
|
||||||
|
const [rowToDelete, setRowToDelete] = useState({});
|
||||||
|
const {
|
||||||
|
getAllFlashpacks,
|
||||||
|
flashpacks,
|
||||||
|
totalFlashpacks,
|
||||||
|
deleteFlashpackVersion,
|
||||||
|
} = useVehicleContext();
|
||||||
|
const {
|
||||||
|
token: {
|
||||||
|
idToken: { jwtToken: token },
|
||||||
|
},
|
||||||
|
groups,
|
||||||
|
providers,
|
||||||
|
} = useUserContext();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setTitle("Flashpack Versions");
|
||||||
|
setSitePath([
|
||||||
|
{
|
||||||
|
label: "Tools",
|
||||||
|
link: "/tools/flashpacks",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Flashpack Versions",
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
loadFlashpacks();
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [token, token, pageIndex, pageSize]);
|
||||||
|
|
||||||
|
const loadFlashpacks = async () => {
|
||||||
|
try {
|
||||||
|
if (!token) return;
|
||||||
|
await getAllFlashpacks(
|
||||||
|
{
|
||||||
|
limit: pageSize,
|
||||||
|
offset: pageSize * pageIndex,
|
||||||
|
},
|
||||||
|
token
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
setMessage(e.message);
|
||||||
|
logger.warn(e.stack);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleChangePageIndex = (event, newIndex) => {
|
||||||
|
setPageIndex(newIndex);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleChangePageSize = (event) => {
|
||||||
|
setPageSize(parseInt(event.target.value, 10));
|
||||||
|
setPageIndex(0);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onDeleteClick = (row) => {
|
||||||
|
setRowToDelete(row);
|
||||||
|
setShowDeleteModal(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
const sendDelete = async () => {
|
||||||
|
if (rowToDelete) {
|
||||||
|
try {
|
||||||
|
const result = await deleteFlashpackVersion(rowToDelete.car_model, rowToDelete.car_year, rowToDelete.flashpack, token);
|
||||||
|
if (!result || result.error) return;
|
||||||
|
|
||||||
|
setMessage(`Deleted ${rowToDelete.car_year} ${rowToDelete.car_model} ${rowToDelete.flashpack}`);
|
||||||
|
loadFlashpacks();
|
||||||
|
} catch (e) {
|
||||||
|
setMessage(e.message);
|
||||||
|
logger.warn(e.stack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Grid container className={classes.root} spacing={2}>
|
||||||
|
<Grid item md={4} className={clsx(classes.textJustifyAlign, classes.actionsBar)}>
|
||||||
|
<Link to={`/tools/flashpack/add`} className={classes.labelInline}>
|
||||||
|
<AddCircleIcon fontSize="large" />
|
||||||
|
</Link>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
<Table>
|
||||||
|
<TableHeaderSortable
|
||||||
|
classes={classes}
|
||||||
|
orderBy={"flashpack"}
|
||||||
|
order={"desc"}
|
||||||
|
columnData={tableColumns}
|
||||||
|
onSortRequest={() => { return null }}
|
||||||
|
/>
|
||||||
|
<TableBody>
|
||||||
|
{flashpacks && flashpacks.map((row, index) => (
|
||||||
|
<TableRow key={row.flashpack + row.car_model}>
|
||||||
|
<TableCell align="center">
|
||||||
|
<Link to={`/tools/flashpack/${row.car_model}/${row.car_year}/${row.flashpack}`}>
|
||||||
|
{row.flashpack}
|
||||||
|
</Link>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell align="center">
|
||||||
|
{row.car_model}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell align="center">
|
||||||
|
{row.car_year}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell align="center">
|
||||||
|
<RoleWrap
|
||||||
|
groups={groups}
|
||||||
|
providers={providers}
|
||||||
|
rolesPerProvider={Permissions.FiskerDelete}
|
||||||
|
>
|
||||||
|
<IconButton
|
||||||
|
onClick={() => onDeleteClick(row)}
|
||||||
|
aria-label={`Send delete for ${row.car_year} ${row.car_model} ${row.flashpack}`}
|
||||||
|
size="small"
|
||||||
|
color="primary"
|
||||||
|
>
|
||||||
|
<DeleteIcon aria-label={`Delete ${row.car_year} ${row.car_model} ${row.flashpack}`} />
|
||||||
|
</IconButton>
|
||||||
|
</RoleWrap>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableBody>
|
||||||
|
<TableFooter>
|
||||||
|
<TableRow>
|
||||||
|
{!flashpacks || flashpacks.length === 0 ? (
|
||||||
|
<TableCell colSpan={8} align="center">No Flashpack Numbers</TableCell>
|
||||||
|
) : (
|
||||||
|
<TablePagination
|
||||||
|
rowsPerPageOptions={[5, 10, 25, 100]}
|
||||||
|
colSpan={4}
|
||||||
|
count={totalFlashpacks}
|
||||||
|
rowsPerPage={pageSize}
|
||||||
|
page={pageIndex}
|
||||||
|
SelectProps={{
|
||||||
|
inputProps: { "aria-label": "rows per page" },
|
||||||
|
native: true,
|
||||||
|
}}
|
||||||
|
onPageChange={handleChangePageIndex}
|
||||||
|
onRowsPerPageChange={handleChangePageSize}
|
||||||
|
/>)}
|
||||||
|
</TableRow>
|
||||||
|
</TableFooter>
|
||||||
|
</Table>
|
||||||
|
<DeleteConfirmation
|
||||||
|
message={rowToDelete && rowToDelete.car_year + " " + rowToDelete.car_model + " " + rowToDelete.flashpack}
|
||||||
|
open={showDeleteModal}
|
||||||
|
close={() => setShowDeleteModal(false)}
|
||||||
|
deleteFunction={sendDelete}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const Flashpacks = () => (
|
||||||
|
<VehicleProvider>
|
||||||
|
<MainForm />
|
||||||
|
</VehicleProvider>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default Flashpacks;
|
||||||
46
src/components/Flashpack/index.test.jsx
Normal file
46
src/components/Flashpack/index.test.jsx
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
jest.mock("../Contexts/VehicleContext");
|
||||||
|
jest.mock("../Contexts/StatusContext");
|
||||||
|
jest.mock("../Contexts/UserContext");
|
||||||
|
jest.mock("@material-ui/core/utils/unstable_useId", () =>
|
||||||
|
jest.fn().mockReturnValue("mui-test-id")
|
||||||
|
);
|
||||||
|
import { render, waitFor } from "@testing-library/react";
|
||||||
|
import { MemoryRouter, Route } from "react-router-dom";
|
||||||
|
import { VehicleProvider } from "../Contexts/VehicleContext";
|
||||||
|
import { StatusProvider } from "../Contexts/StatusContext";
|
||||||
|
import { UserProvider, setToken } from "../Contexts/UserContext";
|
||||||
|
import { TEST_AUTH_OBJECT_FISKER } from "../../utils/testing";
|
||||||
|
import MainForm from "./index";
|
||||||
|
import addSnapshotSerializer from "../../utils/snapshot";
|
||||||
|
|
||||||
|
const renderFlashpack = async () => {
|
||||||
|
const { container } = render(
|
||||||
|
<VehicleProvider>
|
||||||
|
<StatusProvider>
|
||||||
|
<UserProvider>
|
||||||
|
<MemoryRouter initialEntries={["/tools/flashpacks"]} >
|
||||||
|
<Route path="/tools/flashpacks">
|
||||||
|
<MainForm />
|
||||||
|
</Route>
|
||||||
|
</MemoryRouter>
|
||||||
|
</UserProvider>
|
||||||
|
</StatusProvider>
|
||||||
|
</VehicleProvider >
|
||||||
|
);
|
||||||
|
await waitFor(() => {
|
||||||
|
/* render */
|
||||||
|
});
|
||||||
|
return container;
|
||||||
|
};
|
||||||
|
|
||||||
|
describe("Flashpack", () => {
|
||||||
|
beforeAll(() => {
|
||||||
|
addSnapshotSerializer(expect);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Render", async () => {
|
||||||
|
setToken(TEST_AUTH_OBJECT_FISKER);
|
||||||
|
const container = await renderFlashpack();
|
||||||
|
expect(container).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -79,6 +79,11 @@ const menuData = [
|
|||||||
to: "/tools/sms/send",
|
to: "/tools/sms/send",
|
||||||
rolesPerProvider: Permissions.FiskerCreate,
|
rolesPerProvider: Permissions.FiskerCreate,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: "Flashpack",
|
||||||
|
to: "/tools/flashpacks",
|
||||||
|
rolesPerProvider: Permissions.FiskerRead,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -375,6 +375,28 @@ exports[`SideMenu Authenticated 1`] = `
|
|||||||
/>
|
/>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
aria-disabled="false"
|
||||||
|
class="MuiButtonBase-root MuiListItem-root MuiListItem-gutters MuiListItem-button"
|
||||||
|
href="/tools/flashpacks"
|
||||||
|
role="button"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="MuiListItemText-root"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="MuiTypography-root MuiListItemText-primary MuiTypography-body1 MuiTypography-displayBlock"
|
||||||
|
>
|
||||||
|
Flashpack
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<span
|
||||||
|
class="MuiTouchRipple-root"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -40,6 +40,9 @@ const SecurityDLL = React.lazy(() => import("../Magna/SecurityDLL"));
|
|||||||
const SMSSend = React.lazy(() => import("../SMS/Send"));
|
const SMSSend = React.lazy(() => import("../SMS/Send"));
|
||||||
const SuppliersList = React.lazy(() => import("../Suppliers/List"));
|
const SuppliersList = React.lazy(() => import("../Suppliers/List"));
|
||||||
const SupplierDetails = React.lazy(() => import("../Suppliers/Details"));
|
const SupplierDetails = React.lazy(() => import("../Suppliers/Details"));
|
||||||
|
const Flashpacks = React.lazy(() => import("../Flashpack"));
|
||||||
|
const FlashpackDetails = React.lazy(() => import("../Flashpack/Details"));
|
||||||
|
const FlashpackAdd = React.lazy(() => import("../Flashpack/Add"))
|
||||||
const Datascope = React.lazy(() => import("../Dashboard"));
|
const Datascope = React.lazy(() => import("../Dashboard"));
|
||||||
const SumsRxSwin = React.lazy(() => import("../SUMS"));
|
const SumsRxSwin = React.lazy(() => import("../SUMS"));
|
||||||
const SumsRxSwinAdd = React.lazy(() => import("../SUMS/Add"));
|
const SumsRxSwinAdd = React.lazy(() => import("../SUMS/Add"));
|
||||||
@@ -280,6 +283,33 @@ const SiteRoutes = () => {
|
|||||||
rolesPerGroup={Permissions.FiskerCreate}
|
rolesPerGroup={Permissions.FiskerCreate}
|
||||||
providers={providers}
|
providers={providers}
|
||||||
/>
|
/>
|
||||||
|
<AuthRoute
|
||||||
|
path="/tools/flashpacks"
|
||||||
|
render={() => <Flashpacks />}
|
||||||
|
type={TYPES.PROTECTED}
|
||||||
|
token={token}
|
||||||
|
groups={groups}
|
||||||
|
rolesPerGroup={Permissions.FiskerRead}
|
||||||
|
providers={providers}
|
||||||
|
/>
|
||||||
|
<AuthRoute
|
||||||
|
path="/tools/flashpack/:model/:year/:flashpack"
|
||||||
|
render={() => <FlashpackDetails />}
|
||||||
|
type={TYPES.PROTECTED}
|
||||||
|
token={token}
|
||||||
|
groups={groups}
|
||||||
|
rolesPerGroup={Permissions.FiskerRead}
|
||||||
|
providers={providers}
|
||||||
|
/>
|
||||||
|
<AuthRoute
|
||||||
|
path="/tools/flashpack/add"
|
||||||
|
render={() => <FlashpackAdd />}
|
||||||
|
type={TYPES.PROTECTED}
|
||||||
|
token={token}
|
||||||
|
groups={groups}
|
||||||
|
rolesPerGroup={Permissions.FiskerCreate}
|
||||||
|
providers={providers}
|
||||||
|
/>
|
||||||
<AuthRoute
|
<AuthRoute
|
||||||
path="/suppliers"
|
path="/suppliers"
|
||||||
render={() => <SuppliersList />}
|
render={() => <SuppliersList />}
|
||||||
|
|||||||
@@ -199,6 +199,36 @@ const vehiclesAPI = {
|
|||||||
sendDiagnosticCommand: async (search) => ({
|
sendDiagnosticCommand: async (search) => ({
|
||||||
Message: `remote diagnostic command sent to ${search.vins.length} vehicles`
|
Message: `remote diagnostic command sent to ${search.vins.length} vehicles`
|
||||||
}),
|
}),
|
||||||
|
getAllFlashpacks: async (token) => {
|
||||||
|
return {
|
||||||
|
data: ["41.14", "43.19"],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
getFlashpackECUMappings: async (model, year, flashpack, token) => {
|
||||||
|
return {
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"flashpack": "41.14",
|
||||||
|
"car_model": "Ocean",
|
||||||
|
"car_year": 2023,
|
||||||
|
"car_ecu_name": "ADAS",
|
||||||
|
"car_ecu_version": "ADASVersion"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"flashpack": "41.14",
|
||||||
|
"car_model": "Ocean",
|
||||||
|
"car_year": 2023, "car_ecu_name": "ACUN",
|
||||||
|
"car_ecu_version": "ACUNVersion"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
addFlashpackECUMapping: async (data, token) => {
|
||||||
|
return { message: "Created" };
|
||||||
|
},
|
||||||
|
deleteFlashpackECUMapping: async (data, token) => {
|
||||||
|
return { message: "Deleted" };
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default vehiclesAPI;
|
export default vehiclesAPI;
|
||||||
|
|||||||
@@ -260,6 +260,52 @@ const vehiclesAPI = {
|
|||||||
}).then(fetchRespHandler)
|
}).then(fetchRespHandler)
|
||||||
.catch(errorHandler)
|
.catch(errorHandler)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getAllFlashpacks: async (options, token) => {
|
||||||
|
return fetch(addQueryParams(`${API_ENDPOINT}/flashpack_versions`, options), {
|
||||||
|
method: "GET",
|
||||||
|
headers: Object.assign(
|
||||||
|
{ "Content-Type": "application/json" },
|
||||||
|
getAuthHeaderOptions(token)
|
||||||
|
),
|
||||||
|
}).then(fetchRespHandler)
|
||||||
|
.catch(errorHandler)
|
||||||
|
},
|
||||||
|
|
||||||
|
getFlashpackECUMappings: async (model, year, flashpack, options, token) => {
|
||||||
|
return fetch(addQueryParams(`${API_ENDPOINT}/flashpack_version_ecu_mappings/${model}/${year}/${flashpack}`, options), {
|
||||||
|
method: "GET",
|
||||||
|
headers: Object.assign(
|
||||||
|
{ "Content-Type": "application/json" },
|
||||||
|
getAuthHeaderOptions(token)
|
||||||
|
),
|
||||||
|
}).then(fetchRespHandler)
|
||||||
|
.catch(errorHandler)
|
||||||
|
},
|
||||||
|
|
||||||
|
addFlashpackVersion: async (data, token) => {
|
||||||
|
return fetch(`${API_ENDPOINT}/flashpack_version`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: Object.assign(
|
||||||
|
{ "Content-Type": "application/json" },
|
||||||
|
getAuthHeaderOptions(token),
|
||||||
|
),
|
||||||
|
body: JSON.stringify(data),
|
||||||
|
}).then(fetchRespHandler)
|
||||||
|
.catch(errorHandler)
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteFlashpackVersion: async (data, token) => {
|
||||||
|
return fetch(`${API_ENDPOINT}/flashpack_version`, {
|
||||||
|
method: "DELETE",
|
||||||
|
headers: Object.assign(
|
||||||
|
{ "Content-Type": "application/json" },
|
||||||
|
getAuthHeaderOptions(token)
|
||||||
|
),
|
||||||
|
body: JSON.stringify(data),
|
||||||
|
}).then(fetchRespHandler)
|
||||||
|
.catch(errorHandler)
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default vehiclesAPI;
|
export default vehiclesAPI;
|
||||||
|
|||||||
Reference in New Issue
Block a user