Package upgrades and cleanup.

This commit is contained in:
Patrick Fic
2026-02-24 15:10:54 -08:00
parent 605e46b1b0
commit e97f644373
10 changed files with 2383 additions and 2454 deletions

View File

@@ -44,7 +44,7 @@ export default defineConfig({
filesToDeleteAfterUpload: ["**.js.map"],
},
release: {
name: `bodyshop-desktop@${process.env.npm_package_version}`,
name: `esdp@${process.env.npm_package_version}`,
},
}),
],

4478
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -22,66 +22,65 @@
"build:mac": "node deploy/set-artifact-name.js electron-vite build --mode imex && node deploy/set-artifact-name.js electron-builder --mac"
},
"dependencies": {
"@apollo/client": "^4.0.9",
"@apollo/client": "^4.1.6",
"@electron-toolkit/preload": "^3.0.2",
"@electron-toolkit/utils": "^4.0.0",
"@sentry/electron": "^7.4.0",
"@sentry/vite-plugin": "^4.6.1",
"axios": "^1.13.2",
"@sentry/electron": "^7.8.0",
"@sentry/vite-plugin": "^5.1.0",
"axios": "^1.13.5",
"dayjs": "^1.11.19",
"electron-log": "^5.4.3",
"electron-updater": "^6.6.2",
"source-map-support": "^0.5.21",
"winax": "^3.6.2"
"electron-updater": "^6.8.3",
"source-map-support": "^0.5.21"
},
"devDependencies": {
"@electron-toolkit/eslint-config-prettier": "^3.0.0",
"@electron-toolkit/eslint-config-ts": "^3.1.0",
"@electron-toolkit/tsconfig": "^2.0.0",
"@playwright/test": "^1.57.0",
"@reduxjs/toolkit": "^2.11.1",
"@playwright/test": "^1.58.2",
"@reduxjs/toolkit": "^2.11.2",
"@types/cors": "^2.8.19",
"@types/express": "^5.0.6",
"@types/lodash": "^4.17.21",
"@types/node": "^24.10.1",
"@types/lodash": "^4.17.24",
"@types/node": "^25.3.0",
"@types/node-cron": "^3.0.11",
"@types/react": "^19.2.7",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"@types/source-map-support": "^0.5.10",
"@types/xml2js": "^0.4.14",
"@vitejs/plugin-react": "^5.1.2",
"antd": "^6.1.0",
"@vitejs/plugin-react": "^5.1.4",
"antd": "^6.3.1",
"archiver": "^7.0.1",
"chokidar": "^5.0.0",
"cors": "^2.8.5",
"cors": "^2.8.6",
"cross-env": "^10.1.0",
"dbffile": "^1.12.0",
"electron": "^39.2.6",
"electron-builder": "^26.0.12",
"electron": "^40.6.0",
"electron-builder": "^26.8.1",
"electron-store": "^11.0.2",
"electron-vite": "^5.0.0",
"eslint": "^9.39.1",
"eslint": "^10.0.2",
"eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.4.24",
"eslint-plugin-react-refresh": "^0.5.2",
"express": "^5.2.1",
"firebase": "^12.6.0",
"graphql": "^16.12.0",
"graphql-request": "^7.3.5",
"i18next": "^25.7.2",
"lodash": "^4.17.21",
"firebase": "^12.9.0",
"graphql": "^16.13.0",
"graphql-request": "^7.4.0",
"i18next": "^25.8.13",
"lodash": "^4.17.23",
"node-cron": "^4.2.1",
"playwright": "^1.57.0",
"prettier": "^3.7.4",
"react": "^19.2.1",
"react-dom": "^19.2.1",
"react-error-boundary": "^6.0.0",
"react-i18next": "^16.4.0",
"playwright": "^1.58.2",
"prettier": "^3.8.1",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"react-error-boundary": "^6.1.1",
"react-i18next": "^16.5.4",
"react-redux": "^9.2.0",
"react-router": "^7.10.1",
"react-router": "^7.13.1",
"redux-logger": "^3.0.6",
"typescript": "^5.9.3",
"vite": "7.2.7",
"vite": "7.3.1",
"xml2js": "^0.6.2",
"xmlbuilder2": "^4.0.3"
}

View File

@@ -1,144 +0,0 @@
import { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda';
import axios, { AxiosError } from 'axios';
import FormData from 'form-data';
import { GraphQLClient, gql } from 'graphql-request';
import { ESJobObject, RawJobDataObject } from '../../../shared/types';
import { transformJobForEstimateScrubber } from '../lib/transformEstimate';
import { getVehicleType } from '../lib/vehicleTypes/vehicleType';
import { UUID } from 'node:crypto';
const ES_USER = process.env.ES_USER || '';
const ES_PASSWORD = process.env.ES_PASSWORD || '';
const ES_ENDPOINT = process.env.ES_ENDPOINT || '';
const HASURA_URL = process.env.HASURA_URL || '';
interface ScrubRequest {
esApiKey: string;
rawJob: RawJobDataObject;
}
interface ScrubResponse {
report_link?: string;
identified_item?: unknown;
}
export const handler = async (event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> => {
try {
const { esApiKey, rawJob } = JSON.parse(event.body || '{}') as ScrubRequest;
await uploadJobToHasura(rawJob, esApiKey);
// Transform the raw job object to ES format
const estimate: ESJobObject = await transformJobForEstimateScrubber(rawJob);
// Set vehicle type and sending entity ID
estimate.v_type = getVehicleType(estimate.v_model || '').type;
estimate.sending_entity_id = '87330f61-412b-4251-baaa-d026565b23c5';
const fileName = `${esApiKey}-${rawJob.clm_no}-${Date.now()}`;
const formData = new FormData();
const jsonString = JSON.stringify(estimate);
formData.append('file', Buffer.from(jsonString), {
filename: `${fileName}.json`,
contentType: 'application/json',
});
return {
statusCode: 200,
body: JSON.stringify({
message: 'Debug - skipping ES scrub call',
// Uncomment below to enable actual ES scrub call
// ...await callEstimateScrubber(esApiKey, formData, fileName),
}),
};
const result = await axios.post<ScrubResponse>(`${ES_ENDPOINT}/api/sendems`, formData, {
auth: {
username: ES_USER,
password: ES_PASSWORD,
},
headers: {
APIkey: esApiKey,
},
});
const resultPDFUrl = result?.data?.report_link;
const reportIssueUrl = `https://insurtechtoolkit.com/pcontactUs.aspx?apiKey=${esApiKey}&file=${fileName}.json`;
return {
statusCode: 200,
body: JSON.stringify({
resultPDFUrl,
reportIssueUrl,
identified_item: result.data?.identified_item,
}),
};
} catch (error) {
console.log('Error in scrub handler:', error);
return {
statusCode: 500,
body: JSON.stringify({
message: 'Internal server error.',
error: (error as Error).message,
stack: (error as Error).stack,
}),
};
const axiosError = error as AxiosError;
const errorMessage = axiosError.response?.data || axiosError.message || 'Unknown error';
return {
statusCode: 400,
body: JSON.stringify({
message: 'Error scrubbing estimate.',
error: errorMessage,
}),
};
}
};
const uploadJobToHasura = async (rawJob: RawJobDataObject, esApiKey: string): Promise<void> => {
const graphQLClient = new GraphQLClient(HASURA_URL, {
headers: {
'x-hasura-admin-secret': 'UXWqeUlNMc2dd2SD7DTOKgjEQlVkZkaW',
},
});
//Fetch to get the latest version of this based on the es api key and clm no
const version = await graphQLClient.request<{
jobs: { version: number; id: UUID; clm_no: string }[];
}>(LATEST_VERSION_BY_CLM_NO, {
esApiKey,
clm_no: rawJob.clm_no || '',
});
//Manipulate the raw job to insert to the database.
rawJob.version = version.jobs.length > 0 ? version.jobs[0].version + 1 : 1;
await graphQLClient.request(INSERT_JOB, {
job: rawJob,
});
};
const LATEST_VERSION_BY_CLM_NO = gql`
query LATEST_VERSION_BY_CLM_NO($esApiKey: String!, $clm_no: String!) {
jobs(
where: { clm_no: { _eq: $clm_no }, shop: { es_api_key: { _eq: $esApiKey } } }
order_by: { version: desc }
limit: 1
) {
id
clm_no
version
}
}
`;
const INSERT_JOB = gql`
mutation INSERT_JOB($job: jobs_insert_input!) {
insert_jobs_one(object: $job) {
id
}
}
`;

View File

@@ -1,10 +1,124 @@
import { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda';
import axios, { AxiosError } from 'axios';
import FormData from 'form-data';
import { GraphQLClient, gql } from 'graphql-request';
import { ESJobObject, RawJobDataObject } from '../../../shared/types';
import { transformJobForEstimateScrubber } from '../lib/transformEstimate';
import { getVehicleType } from '../lib/vehicleTypes/vehicleType';
import { UUID } from 'node:crypto';
const ES_USER = process.env.ES_USER || '';
const ES_PASSWORD = process.env.ES_PASSWORD || '';
const ES_ENDPOINT = process.env.ES_ENDPOINT || '';
const HASURA_URL = process.env.HASURA_URL || '';
interface ScrubRequest {
esApiKey: string;
rawJob: RawJobDataObject;
}
interface ScrubResponse {
report_link?: string;
identified_item?: unknown;
}
export const handler = async (event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> => {
return {
statusCode: 200,
body: JSON.stringify({
message: 'Scrub handler is under construction',
}),
};
try {
const { esApiKey, rawJob } = JSON.parse(event.body || '{}') as ScrubRequest;
//await uploadJobToHasura(rawJob, esApiKey);
// Transform the raw job object to ES format
const estimate: ESJobObject = await transformJobForEstimateScrubber(rawJob);
// Set vehicle type and sending entity ID
estimate.v_type = getVehicleType(estimate.v_model || '').type;
estimate.sending_entity_id = '87330f61-412b-4251-baaa-d026565b23c5';
const fileName = `${esApiKey}-${rawJob.clm_no}-${Date.now()}`;
const formData = new FormData();
const jsonString = JSON.stringify(estimate);
formData.append('file', Buffer.from(jsonString), {
filename: `${fileName}.json`,
contentType: 'application/json',
});
const result = await axios.post<ScrubResponse>(`${ES_ENDPOINT}/api/sendems`, formData, {
auth: {
username: ES_USER,
password: ES_PASSWORD,
},
headers: {
APIkey: esApiKey,
},
});
const resultPDFUrl = result?.data?.report_link;
const reportIssueUrl = `https://insurtechtoolkit.com/pcontactUs.aspx?apiKey=${esApiKey}&file=${fileName}.json`;
return {
statusCode: 200,
body: JSON.stringify({
resultPDFUrl,
reportIssueUrl,
identified_item: result.data?.identified_item,
}),
};
} catch (error) {
console.log('Error in scrub handler:', error);
return {
statusCode: 500,
body: JSON.stringify({
message: 'Internal server error.',
error: (error as Error).message,
stack: (error as Error).stack,
}),
};
}
};
const uploadJobToHasura = async (rawJob: RawJobDataObject, esApiKey: string): Promise<void> => {
const graphQLClient = new GraphQLClient(HASURA_URL, {
headers: {
'x-hasura-admin-secret': 'UXWqeUlNMc2dd2SD7DTOKgjEQlVkZkaW',
},
});
//Fetch to get the latest version of this based on the es api key and clm no
const version = await graphQLClient.request<{
jobs: { version: number; id: UUID; clm_no: string }[];
}>(LATEST_VERSION_BY_CLM_NO, {
esApiKey,
clm_no: rawJob.clm_no || '',
});
//Manipulate the raw job to insert to the database.
rawJob.version = version.jobs.length > 0 ? version.jobs[0].version + 1 : 1;
await graphQLClient.request(INSERT_JOB, {
job: rawJob,
});
};
const LATEST_VERSION_BY_CLM_NO = gql`
query LATEST_VERSION_BY_CLM_NO($esApiKey: String!, $clm_no: String!) {
jobs(
where: { clm_no: { _eq: $clm_no }, shop: { es_api_key: { _eq: $esApiKey } } }
order_by: { version: desc }
limit: 1
) {
id
clm_no
version
}
}
`;
const INSERT_JOB = gql`
mutation INSERT_JOB($job: jobs_insert_input!) {
insert_jobs_one(object: $job) {
id
}
}
`;

View File

@@ -1,4 +1,4 @@
import axios from "axios";
import axios, { AxiosError } from "axios";
import { BrowserWindow } from "electron";
import log from "electron-log";
import { autoUpdater } from "electron-updater";
@@ -6,6 +6,7 @@ import { promises as fsPromises } from "fs";
import path from "path";
import { RawJobDataObject } from "../decoder/decoder";
import store from "../store/store";
import ipcTypes from "../../util/ipcTypes.json";
// Function to write job object to logs subfolder
async function writeJobToLogsFolder(job, fileName): Promise<string> {
@@ -110,21 +111,21 @@ async function ScrubEstimate({
pdfWindow.focus();
return resultPDFUrl;
} catch (error) {
log.error("Error while scrubbing estimate:", error, error.stack);
log.error("Error Response Data:", error.response?.data);
const err = error as AxiosError;
log.error("Error while scrubbing estimate:", error, err.stack);
log.error("Error Response Data:", err.response?.data);
const mainWindow = BrowserWindow.getAllWindows()[0];
if (error.status === 400) {
mainWindow.webContents.send(ipcTypes.app.toRenderer.scrubError, {
mainWindow.webContents.send(ipcTypes.toRenderer.scrub.scrubError, {
message:
error.response?.data ||
"Error encountered sending estimate to Estimate Scrubber.",
});
} else if (error.status === 401) {
mainWindow.webContents.send(ipcTypes.app.toRenderer.scrubError, {
mainWindow.webContents.send(ipcTypes.toRenderer.scrub.scrubError, {
message:
"Authentication with Estimate Scrubber failed." ||
error.response?.data,
err.response?.data || "Authentication with Estimate Scrubber failed."ta,
});
}
return "Error: Unable to scrub estimate.";

View File

@@ -154,7 +154,7 @@ const Home: FC = () => {
return (
<div style={{ maxWidth: "1400px", margin: "0 auto" }}>
<Space
direction="vertical"
orientation="vertical"
size="large"
style={{ width: "100%", display: "flex" }}
>

View File

@@ -60,7 +60,7 @@ const SettingsConfig: FC = () => {
layout="vertical"
size="large"
>
<Space direction="vertical" size="middle" style={{ width: "100%" }}>
<Space orientation="vertical" size="middle" style={{ width: "100%" }}>
{settingFields.map((field) => (
<Form.Item
key={field.name}

View File

@@ -43,7 +43,7 @@ const UpdateAvailable: FC = () => {
boxShadow: "0 4px 12px rgba(0, 0, 0, 0.15)",
}}
>
<Space direction="vertical" style={{ width: "100%" }}>
<Space orientation="vertical" style={{ width: "100%" }}>
{updateProgress === 0 && (
<Button onClick={handleDownload}>{t("updates.download")}</Button>
)}

View File

@@ -55,6 +55,9 @@
"error": "toRenderer_watcher_error",
"polling": "toRenderer_watcher_polling"
},
"scrub": {
"scrubError": "toRenderer_scrubError"
},
"updates": {
"checking": "toRenderer_updates_checking",
"available": "toRenderer_updates_available",