Almost matching export.

This commit is contained in:
Patrick Fic
2025-03-20 12:50:47 -07:00
parent 2e5fe7c99d
commit 45209bd9e4
32 changed files with 490 additions and 120 deletions

View File

@@ -28,7 +28,7 @@ export interface DecodedAd1 {
ded_amt?: string;
ded_status?: string;
asgn_no?: string;
asgn_date?: string;
asgn_date?: Date | string;
asgn_type?: string;
// Claim information

View File

@@ -3,6 +3,7 @@ import log from "electron-log/main";
import _ from "lodash";
import deepLowerCaseKeys from "../../util/deepLowercaseKeys";
import errorTypeCheck from "../../util/errorTypeCheck";
import store from "../store/store";
import { DecodedAd1, OwnerRecordInterface } from "./decode-ad1.interface";
const DecodeAD1 = async (
@@ -26,7 +27,9 @@ const DecodeAD1 = async (
//AD1 will always have only 1 row.
//Commented lines have been cross referenced with existing partner fields.
const d = rawDBFRecord[0].ASGN_DATE;
console.log(d);
console.log(typeof rawDBFRecord[0].ASGN_DATE);
const rawAd1Data: DecodedAd1 = deepLowerCaseKeys(
_.pick(rawDBFRecord[0], [
//TODO: Add typings for EMS File Formats.
@@ -149,6 +152,11 @@ const DecodeAD1 = async (
//Copy specific logic for manipulation.
//If ownr_ph1 is missing, use ownr_ph2
if (rawAd1Data.asgn_date) {
const newAsgnDate = new Date(rawAd1Data.asgn_date);
rawAd1Data.asgn_date = newAsgnDate.toISOString().split("T")[0];
}
if (!rawAd1Data.ownr_ph1) {
rawAd1Data.ownr_ph1 = rawAd1Data.ownr_ph2;
}
@@ -161,7 +169,8 @@ const DecodeAD1 = async (
_.isEmpty(rawAd1Data.ownr_co_nm)
) {
//They're all empty. Using the insured information as a fallback.
// //Build up the owner record to insert it alongside the job.
// Build up the owner record to insert it alongside the job.
//TODO: Verify that this should be the insured, and not the claimant.
ownerRecord = {
ownr_ln: rawAd1Data.insd_ln,
ownr_fn: rawAd1Data.insd_fn,
@@ -176,8 +185,7 @@ const DecodeAD1 = async (
ownr_ph1: rawAd1Data.insd_ph1,
ownr_ph2: rawAd1Data.insd_ph2,
ownr_ea: rawAd1Data.insd_ea,
shopid: "UUID", //TODO: Need to add the shop uuid to this set of functions.
shopid: store.get("app.bodyshop.id"),
};
} else {
//Use the owner information.
@@ -195,10 +203,11 @@ const DecodeAD1 = async (
ownr_ph1: rawAd1Data.ownr_ph1,
ownr_ph2: rawAd1Data.ownr_ph2,
ownr_ea: rawAd1Data.ownr_ea,
shopid: "UUID", //TODO: Need to add the shop uuid to this set of functions.
shopid: store.get("app.bodyshop.id"),
};
}
const s = store.get("app");
console.log(s);
return { ...rawAd1Data, owner: { data: ownerRecord } };
};
export default DecodeAD1;

View File

@@ -30,24 +30,24 @@ const DecodeAD2 = async (
const rawAd2Data: DecodedAD2 = deepLowerCaseKeys(
_.pick(rawDBFRecord[0], [
//TODO: Add typings for EMS File Formats.
"CLMT_LN",
"CLMT_FN",
"CLMT_TITLE",
"CLMT_CO_NM",
"CLMT_ADDR1",
"CLMT_ADDR2",
"CLMT_CITY",
"CLMT_ST",
"CLMT_ZIP",
"CLMT_CTRY",
"CLMT_PH1",
// "CLMT_LN", //TODO: This claimant info shouldnt be passed back. Just for the owner info.
// "CLMT_FN",
// "CLMT_TITLE",
// "CLMT_CO_NM",
// "CLMT_ADDR1",
// "CLMT_ADDR2",
// "CLMT_CITY",
// "CLMT_ST",
// "CLMT_ZIP",
// "CLMT_CTRY",
// "CLMT_PH1",
//"CLMT_PH1X",
"CLMT_PH2",
//"CLMT_PH2",
//"CLMT_PH2X",
//"CLMT_FAX",
//"CLMT_FAXX",
"CLMT_EA",
"EST_CO_ID",
//"CLMT_EA",
//"EST_CO_ID",
"EST_CO_NM",
"EST_ADDR1",
"EST_ADDR2",

View File

@@ -1,4 +1,5 @@
export interface DecodedEnv {
est_system: string;
estfile_id: string;
est_system?: string;
estfile_id?: string;
ciecaid?: string;
}

View File

@@ -30,10 +30,12 @@ const DecodeEnv = async (
_.pick(rawDBFRecord[0], [
//TODO: Add typings for EMS File Formats.
//TODO: Several of these fields will fail. Should extend schema to capture them.
"EST_SYSTEM",
//"EST_SYSTEM",
"ESTFILE_ID",
])
);
rawEnvData.ciecaid = rawEnvData.estfile_id;
delete rawEnvData.estfile_id;
//Apply business logic transfomrations.

View File

@@ -1,4 +1,4 @@
export interface DecodedLin {
export interface DecodedLinLine {
line_no?: string;
line_ind?: string;
line_ref?: string;
@@ -46,3 +46,9 @@ export interface DecodedLin {
bett_tax?: boolean;
op_code_desc?: string;
}
export interface DecodedLin {
joblines: {
data: DecodedLinLine[];
};
}

View File

@@ -2,12 +2,12 @@ import { DBFFile } from "dbffile";
import log from "electron-log/main";
import _ from "lodash";
import deepLowerCaseKeys from "../../util/deepLowercaseKeys";
import { DecodedLin } from "./decode-lin.interface";
import { DecodedLin, DecodedLinLine } from "./decode-lin.interface";
import errorTypeCheck from "../../util/errorTypeCheck";
const DecodeLin = async (
extensionlessFilePath: string
): Promise<DecodedLin[]> => {
): Promise<DecodedLin> => {
let dbf: DBFFile | null = null;
try {
dbf = await DBFFile.open(`${extensionlessFilePath}.LIN`);
@@ -26,8 +26,8 @@ const DecodeLin = async (
//AD2 will always have only 1 row.
//Commented lines have been cross referenced with existing partner fields.
const rawLinData: DecodedLin[] = rawDBFRecord.map((record) => {
const singleLineData: DecodedLin = deepLowerCaseKeys(
const rawLinData: DecodedLinLine[] = rawDBFRecord.map((record) => {
const singleLineData: DecodedLinLine = deepLowerCaseKeys(
_.pick(record, [
//TODO: Add typings for EMS File Formats.
"LINE_NO",
@@ -99,6 +99,6 @@ const DecodeLin = async (
//Apply business logic transfomrations.
//We don't have an inspection date, we instead have `date_estimated`
return rawLinData;
return { joblines: { data: rawLinData } };
};
export default DecodeLin;

View File

@@ -31,7 +31,7 @@ const DecodePfh = async (
_.pick(rawDBFRecord[0], [
//TODO: Add typings for EMS File Formats.
//TODO: Several of these fields will fail. Should extend schema to capture them.
"ID_PRO_NAM", //Remove
//"ID_PRO_NAM", //Remove
"TAX_PRETHR",
"TAX_THRAMT", //Remove
"TAX_PSTTHR",
@@ -48,7 +48,7 @@ const DecodePfh = async (
"ADJ_G_DISC",
"ADJ_TOWDIS",
"ADJ_STRDIS",
"ADJ_BTR_IN", //Remove
//"ADJ_BTR_IN", //Remove
"TAX_PREDIS",
])
);
@@ -57,13 +57,13 @@ const DecodePfh = async (
//Standardize some of the numbers and divide by 100.
rawPfhData.tax_prethr = rawPfhData.tax_prethr ?? 0 / 100;
rawPfhData.tax_pstthr = rawPfhData.tax_pstthr ?? 0 / 100;
rawPfhData.tax_tow_rt = rawPfhData.tax_tow_rt ?? 0 / 100;
rawPfhData.tax_str_rt = rawPfhData.tax_str_rt ?? 0 / 100;
rawPfhData.tax_sub_rt = rawPfhData.tax_sub_rt ?? 0 / 100;
rawPfhData.tax_lbr_rt = rawPfhData.tax_lbr_rt ?? 0 / 100;
rawPfhData.federal_tax_rate = rawPfhData.tax_gst_rt ?? 0 / 100;
rawPfhData.tax_prethr = (rawPfhData.tax_prethr ?? 0) / 100;
rawPfhData.tax_pstthr = (rawPfhData.tax_pstthr ?? 0) / 100;
rawPfhData.tax_tow_rt = (rawPfhData.tax_tow_rt ?? 0) / 100;
rawPfhData.tax_str_rt = (rawPfhData.tax_str_rt ?? 0) / 100;
rawPfhData.tax_sub_rt = (rawPfhData.tax_sub_rt ?? 0) / 100;
rawPfhData.tax_lbr_rt = (rawPfhData.tax_lbr_rt ?? 0) / 100;
rawPfhData.federal_tax_rate = (rawPfhData.tax_gst_rt ?? 0) / 100;
delete rawPfhData.tax_gst_rt;
return rawPfhData;

View File

@@ -42,5 +42,9 @@ export interface JobMaterialRateFields {
}
export interface DecodedPfm extends JobMaterialRateFields {
materials: {
mapa?: DecodedPfmLine;
mash?: DecodedPfmLine;
};
cieca_pfm: DecodedPfmLine[];
}

View File

@@ -3,12 +3,12 @@ import log from "electron-log/main";
import _ from "lodash";
import deepLowerCaseKeys from "../../util/deepLowercaseKeys";
import errorTypeCheck from "../../util/errorTypeCheck";
import YNBoolConverter from "../../util/ynBoolConverter";
import {
DecodedPfm,
DecodedPfmLine,
JobMaterialRateFields,
} from "./decode-pfm.interface";
import YNBoolConverter from "../../util/ynBoolConverter";
const DecodePfm = async (
extensionlessFilePath: string
@@ -98,7 +98,7 @@ const DecodePfm = async (
if (mapaLine) {
jobMaterialRates.rate_mapa =
mapaLine.cal_lbrrte || mapaLine.cal_prethr || 0;
jobMaterialRates.tax_paint_mat_rt = mapaLine.mat_taxp ?? 0 / 100;
jobMaterialRates.tax_paint_mat_rt = (mapaLine.mat_taxp ?? 0) / 100;
}
const mashLine: DecodedPfmLine | undefined = rawPfmData.find(
@@ -107,7 +107,7 @@ const DecodePfm = async (
if (mashLine) {
jobMaterialRates.rate_mash =
mashLine.cal_lbrrte || mashLine.cal_prethr || 0;
jobMaterialRates.tax_shop_mat_rt = mashLine.mat_taxp ?? 0 / 100;
jobMaterialRates.tax_shop_mat_rt = (mashLine.mat_taxp ?? 0) / 100;
}
const mahwLine: DecodedPfmLine | undefined = rawPfmData.find(
@@ -116,7 +116,7 @@ const DecodePfm = async (
if (mahwLine) {
jobMaterialRates.rate_mahw =
mahwLine.cal_lbrrte || mahwLine.cal_prethr || 0;
jobMaterialRates.tax_levies_rt = mahwLine.mat_taxp ?? 0 / 100;
jobMaterialRates.tax_levies_rt = (mahwLine.mat_taxp ?? 0) / 100;
}
const additionalMaterials = ["MA2S", "MA2T", "MA3S", "MACS", "MABL"];
@@ -133,7 +133,14 @@ const DecodePfm = async (
//Apply business logic transfomrations.
//We don't have an inspection date, we instead have `date_estimated`
return { ...jobMaterialRates, cieca_pfm: rawPfmData };
return {
...jobMaterialRates,
materials: {
mash: mashLine,
mapa: mapaLine,
},
cieca_pfm: rawPfmData,
};
};
export default DecodePfm;

View File

@@ -1,4 +1,4 @@
export interface DecodedPfo {
export interface DecodedPfoLine {
tx_tow_ty?: string;
tow_t_ty1?: string;
tow_t_in1?: boolean;
@@ -26,3 +26,7 @@ export interface DecodedPfo {
stor_t_ty6?: string;
stor_t_in6?: boolean;
}
export interface DecodedPfo {
cieca_pfo: DecodedPfoLine;
}

View File

@@ -3,8 +3,8 @@ import log from "electron-log/main";
import _ from "lodash";
import deepLowerCaseKeys from "../../util/deepLowercaseKeys";
import errorTypeCheck from "../../util/errorTypeCheck";
import { DecodedPfo } from "./decode-pfo.interface";
import YNBoolConverter from "../../util/ynBoolConverter";
import { DecodedPfo, DecodedPfoLine } from "./decode-pfo.interface";
const DecodePfo = async (
extensionlessFilePath: string
@@ -28,7 +28,7 @@ const DecodePfo = async (
//PFO will always have only 1 row.
//Commented lines have been cross referenced with existing partner fields.
const rawPfoData: DecodedPfo = YNBoolConverter(
const rawPfoData: DecodedPfoLine = YNBoolConverter(
deepLowerCaseKeys(
_.pick(rawDBFRecord[0], [
//TODO: Add typings for EMS File Formats.
@@ -64,6 +64,6 @@ const DecodePfo = async (
//Apply business logic transfomrations.
return rawPfoData;
return { cieca_pfo: rawPfoData };
};
export default DecodePfo;

View File

@@ -17,7 +17,7 @@ export interface DecodedPfpLine {
prt_tx_in5: boolean;
}
export interface DecodedPfp {
export interface DecodedPfpLinesByType {
PAA: DecodedPfpLine;
PAC: DecodedPfpLine;
PAL: DecodedPfpLine;
@@ -31,3 +31,7 @@ export interface DecodedPfp {
PASL: DecodedPfpLine;
PAT: DecodedPfpLine;
}
export interface DecodedPfp {
parts_tax_rates: DecodedPfpLinesByType;
}

View File

@@ -4,7 +4,11 @@ import _ from "lodash";
import deepLowerCaseKeys from "../../util/deepLowercaseKeys";
import errorTypeCheck from "../../util/errorTypeCheck";
import YNBoolConverter from "../../util/ynBoolConverter";
import { DecodedPfp, DecodedPfpLine } from "./decode-pfp.interface";
import {
DecodedPfp,
DecodedPfpLine,
DecodedPfpLinesByType,
} from "./decode-pfp.interface";
const DecodePfp = async (
extensionlessFilePath: string
@@ -57,15 +61,15 @@ const DecodePfp = async (
//Apply business logic transfomrations.
//Convert array of lines to a hash object.
const parsedPfpFile: DecodedPfp = rawPfpData.reduce(
(acc: DecodedPfp, line: DecodedPfpLine) => {
const parsedPfpFile: DecodedPfpLinesByType = rawPfpData.reduce(
(acc: DecodedPfpLinesByType, line: DecodedPfpLine) => {
acc[line.prt_type] = line;
return acc;
},
{} as DecodedPfp
{} as DecodedPfpLinesByType
);
return parsedPfpFile;
return { parts_tax_rates: parsedPfpFile };
};
export default DecodePfp;

View File

@@ -2,7 +2,7 @@
* Interface representing decoded data from a PFT file
* Contains tax type information with up to 6 tax types and 5 tiers each
*/
export interface DecodedPft {
export interface DecodedPftLine {
// Tax Type 1
tax_type1?: string;
ty1_tier1?: number;
@@ -141,3 +141,7 @@ export interface DecodedPft {
ty6_rate5?: number;
ty6_sur5?: number;
}
export interface DecodedPft {
cieca_pft: DecodedPftLine;
}

View File

@@ -3,7 +3,7 @@ import log from "electron-log/main";
import _ from "lodash";
import deepLowerCaseKeys from "../../util/deepLowercaseKeys";
import errorTypeCheck from "../../util/errorTypeCheck";
import { DecodedPft } from "./decode-pft.interface";
import { DecodedPft, DecodedPftLine } from "./decode-pft.interface";
const DecodePft = async (
extensionlessFilePath: string
@@ -25,7 +25,7 @@ const DecodePft = async (
//PFT will always have only 1 row.
//Commented lines have been cross referenced with existing partner fields.
const rawPftData: DecodedPft = deepLowerCaseKeys(
const rawPftData: DecodedPftLine = deepLowerCaseKeys(
_.pick(rawDBFRecord[0], [
//TODO: Add typings for EMS File Formats.
"TAX_TYPE1", //The below is is taken from a CCC estimate. Will require validation to ensure it is also accurate for Audatex/Mitchell
@@ -160,6 +160,6 @@ const DecodePft = async (
//Apply business logic transfomrations.
//We don't have an inspection date, we instead have `date_estimated`
return rawPftData;
return { cieca_pft: rawPftData };
};
export default DecodePft;

View File

@@ -1,4 +1,4 @@
export interface DecodedStl {
export interface DecodedStlLine {
ttl_type?: string;
ttl_typecd?: string;
t_amt?: number;
@@ -18,3 +18,6 @@ export interface DecodedStl {
ttl_hrs?: number;
ttl_amt?: number;
}
export interface DecodedStl {
cieca_stl: DecodedStlLine[];
}

View File

@@ -3,11 +3,11 @@ import log from "electron-log/main";
import _ from "lodash";
import deepLowerCaseKeys from "../../util/deepLowercaseKeys";
import errorTypeCheck from "../../util/errorTypeCheck";
import { DecodedStl } from "./decode-stl.interface";
import { DecodedStl, DecodedStlLine } from "./decode-stl.interface";
const DecodeStl = async (
extensionlessFilePath: string
): Promise<DecodedStl[]> => {
): Promise<DecodedStl> => {
let dbf: DBFFile | null = null;
try {
dbf = await DBFFile.open(`${extensionlessFilePath}.STL`);
@@ -25,8 +25,8 @@ const DecodeStl = async (
//AD2 will always have only 1 row.
//Commented lines have been cross referenced with existing partner fields.
const rawStlData: DecodedStl[] = rawDBFRecord.map((record) => {
const singleLineData: DecodedStl = deepLowerCaseKeys(
const rawStlData: DecodedStlLine[] = rawDBFRecord.map((record) => {
const singleLineData: DecodedStlLine = deepLowerCaseKeys(
_.pick(record, [
//TODO: Add typings for EMS File Formats.
"TTL_TYPE",
@@ -57,6 +57,6 @@ const DecodeStl = async (
//Apply business logic transfomrations.
//We don't have an inspection date, we instead have `date_estimated`
return rawStlData;
return { cieca_stl: rawStlData };
};
export default DecodeStl;

View File

@@ -48,8 +48,8 @@ const DecodeTtl = async (
//Apply business logic transfomrations.
return {
clm_total: 0,
depreciation_taxes: 0,
clm_total: rawTtlData.g_ttl_amt || 0,
depreciation_taxes: 0, //TODO: Find where this needs to be filled from
cieca_ttl: { data: rawTtlData },
};
};

View File

@@ -10,14 +10,17 @@ export interface DecodedVeh {
v_model_desc?: string;
v_color?: string;
kmin?: number;
area_of_damage?: {
impact1?: string;
impact2?: string;
};
// Complete vehicle data object
vehicle: { data: VehicleRecordInterface };
}
export interface VehicleRecordInterface {
// Area of damage information
area_of_damage: {
area_of_damage?: {
impact1?: string;
impact2?: string;
};
@@ -45,7 +48,7 @@ export interface VehicleRecordInterface {
trim_color?: string;
v_mldgcode?: string;
v_engine?: string;
v_mileage?: string;
v_mileage?: number; //TODO: This can sometimes come in as UNK.
v_color?: string;
v_tone?: string;
v_stage?: string;

View File

@@ -4,6 +4,7 @@ import _ from "lodash";
import deepLowerCaseKeys from "../../util/deepLowercaseKeys";
import { DecodedVeh, VehicleRecordInterface } from "./decode-veh.interface";
import errorTypeCheck from "../../util/errorTypeCheck";
import store from "../store/store";
const DecodeVeh = async (
extensionlessFilePath: string
@@ -85,10 +86,10 @@ const DecodeVeh = async (
delete rawVehData.paint_cd2;
delete rawVehData.paint_cd3;
rawVehData.shopid = "UUID"; //TODO: Pass down the shopid for generation.
rawVehData.shopid = store.get("app.bodyshop.id");
//Aggregate the vehicle data to be stamped onto the job record.
const jobVehiclData = {
const jobVehicleData: DecodedVeh = {
plate_no: rawVehData.plate_no,
plate_st: rawVehData.plate_st,
v_vin: rawVehData.v_vin,
@@ -97,14 +98,13 @@ const DecodeVeh = async (
v_model_desc: rawVehData.v_model_desc,
v_color: rawVehData.v_color,
kmin: rawVehData.v_mileage,
};
return {
...jobVehiclData,
area_of_damage: rawVehData.area_of_damage,
vehicle: {
data: rawVehData,
},
};
return jobVehicleData;
};
export default DecodeVeh;

View File

@@ -27,6 +27,7 @@ import DecodeVeh from "./decode-veh";
import { DecodedVeh } from "./decode-veh.interface";
import { DecodedEnv } from "./decode-env.interface";
import DecodeEnv from "./decode-env";
import fs from "fs";
async function ImportJob(filepath: string): Promise<void> {
const parsedFilePath = path.parse(filepath);
@@ -43,30 +44,51 @@ async function ImportJob(filepath: string): Promise<void> {
const ad1: DecodedAd1 = await DecodeAD1(extensionlessFilePath);
const ad2: DecodedAD2 = await DecodeAD2(extensionlessFilePath);
const veh: DecodedVeh = await DecodeVeh(extensionlessFilePath);
const lin: DecodedLin[] = await DecodeLin(extensionlessFilePath);
const lin: DecodedLin = await DecodeLin(extensionlessFilePath);
const pfh: DecodedPfh = await DecodePfh(extensionlessFilePath);
const pfl: DecodedPfl = await DecodePfl(extensionlessFilePath);
const pft: DecodedPft = await DecodePft(extensionlessFilePath);
const pfm: DecodedPfm = await DecodePfm(extensionlessFilePath);
const pfo: DecodedPfo = await DecodePfo(extensionlessFilePath); // TODO: This will be the `cieca_pfo` object
const stl: DecodedStl[] = await DecodeStl(extensionlessFilePath); // TODO: This will be the `cieca_stl` object
const stl: DecodedStl = await DecodeStl(extensionlessFilePath); // TODO: This will be the `cieca_stl` object
const ttl: DecodedTtl = await DecodeTtl(extensionlessFilePath);
const pfp: DecodedPfp = await DecodePfp(extensionlessFilePath);
log.debug("Job Object", {
const jobObject = {
...env,
...ad1,
...ad2,
...veh,
joblines: { data: lin },
...lin,
...pfh,
cieca_pfl: pfl,
cieca_pft: pft,
materials: pfm,
cieca_pfo: pfo,
...pfl,
...pft,
...pfm,
...pfo,
...stl,
...ttl,
parts_tax_rates: pfp,
...pfp,
};
// Save jobObject to a timestamped JSON file
const timestamp = new Date()
.toISOString()
.replace(/:/g, "-")
.replace(/\..+/, "");
const fileName = `job_${timestamp}_${parsedFilePath.name}.json`;
const logsDir = path.join(process.cwd(), "logs");
// Create logs directory if it doesn't exist
if (!fs.existsSync(logsDir)) {
fs.mkdirSync(logsDir, { recursive: true });
}
const filePath = path.join(logsDir, fileName);
fs.writeFileSync(filePath, JSON.stringify(jobObject, null, 2), "utf8");
log.info(`Job data saved to: ${filePath}`);
log.debug("Job Object", {
jobObject,
});
} catch (error) {
log.error("Error encountered while decoding job. ", errorTypeCheck(error));

View File

@@ -1,12 +1,13 @@
import { electronApp, is, optimizer } from "@electron-toolkit/utils";
import { app, BrowserWindow, shell, webContents } from "electron";
import { app, BrowserWindow, Menu, shell } from "electron";
import log from "electron-log/main";
import { join } from "path";
import path, { join } from "path";
import icon from "../../resources/icon.png?asset";
import ErrorTypeCheck from "../util/errorTypeCheck";
import store from "./store/store";
log.initialize();
const isMac = process.platform === "darwin";
function createWindow(): void {
// Create the browser window.
const { width, height, x, y } = store.get("app.windowBounds") as {
@@ -22,7 +23,7 @@ function createWindow(): void {
x,
y,
show: false,
autoHideMenuBar: true,
//autoHideMenuBar: true,
...(process.platform === "linux" ? { icon } : {}),
webPreferences: {
preload: join(__dirname, "../preload/index.js"),
@@ -31,6 +32,131 @@ function createWindow(): void {
},
});
const template = [
// { role: 'appMenu' }
...(isMac
? [
{
label: app.name,
submenu: [
{ role: "about" },
{ type: "separator" },
{ role: "services" },
{ type: "separator" },
{ role: "hide" },
{ role: "hideOthers" },
{ role: "unhide" },
{ type: "separator" },
{ role: "quit" },
],
},
]
: []),
// { role: 'fileMenu' }
{
label: "File",
submenu: [isMac ? { role: "close" } : { role: "quit" }],
},
// { role: 'editMenu' }
{
label: "Edit",
submenu: [
{ role: "undo" },
{ role: "redo" },
{ type: "separator" },
{ role: "cut" },
{ role: "copy" },
{ role: "paste" },
...(isMac
? [
{ role: "pasteAndMatchStyle" },
{ role: "delete" },
{ role: "selectAll" },
{ type: "separator" },
{
label: "Speech",
submenu: [{ role: "startSpeaking" }, { role: "stopSpeaking" }],
},
]
: [{ role: "delete" }, { type: "separator" }, { role: "selectAll" }]),
],
},
// { role: 'viewMenu' }
{
label: "View",
submenu: [
{ role: "reload" },
{ role: "forceReload" },
{ role: "toggleDevTools" },
{ type: "separator" },
{ role: "resetZoom" },
{ role: "zoomIn" },
{ role: "zoomOut" },
{ type: "separator" },
{ role: "togglefullscreen" },
],
},
// { role: 'windowMenu' }
{
label: "Window",
submenu: [
{ role: "minimize" },
{ role: "zoom" },
...(isMac
? [
{ type: "separator" },
{ role: "front" },
{ type: "separator" },
{ role: "window" },
]
: [{ role: "close" }]),
],
},
{
role: "help",
submenu: [
{
label: "Learn More",
click: async () => {
const { shell } = require("electron");
await shell.openExternal("https://electronjs.org");
},
},
],
},
...(import.meta.env.DEV
? [
{
label: "Development",
submenu: [
{
label: "Open Log Folder",
click: (): void => {
/* action for item 1 */
shell.openPath(log.transports.file.getFile().path);
},
},
{
label: "Clear Log",
click: (): void => {
log.transports.file.getFile().clear();
},
},
{
label: "Open Config",
click: (): void => {
shell.openPath(path.dirname(store.path));
},
},
],
},
]
: []),
];
const menu: Electron.Menu = Menu.buildFromTemplate(template);
Menu.setApplicationMenu(menu);
// Store window properties for later
const storeWindowState = (): void => {
const [width, height] = mainWindow.getSize();

View File

@@ -49,13 +49,20 @@ if (import.meta.env.DEV) {
log.debug("[IPC Debug Functions] Adding Debug Handlers");
ipcMain.on(ipcTypes.toMain.debug.decodeEstimate, async (): Promise<void> => {
const relativeEmsFilepath = `_reference/ems/MPI_1/3698420.ENV`;
// Get the app's root directory and create an absolute path
const rootDir = app.getAppPath();
const absoluteFilepath = path.join(rootDir, relativeEmsFilepath);
// const relativeEmsFilepath = `_reference/ems/MPI_1/3698420.ENV`;
// // Get the app's root directory and create an absolute path
// const rootDir = app.getAppPath();
// const absoluteFilepath = path.join(rootDir, relativeEmsFilepath);
// console.log("*** ~ ipcMain.on ~ absoluteFilepath:", absoluteFilepath);
log.debug("[IPC Debug Function] Decode test Estimate", absoluteFilepath);
await ImportJob(absoluteFilepath);
// log.debug("[IPC Debug Function] Decode test Estimate", absoluteFilepath);
// await ImportJob(absoluteFilepath);
const job2 = `/Users/pfic/Downloads/12285264/2285264.ENV`;
const job3 = `/Users/pfic/Downloads/14033376/4033376.ENV`;
await ImportJob(job2);
await ImportJob(job3);
});
}

View File

@@ -17,6 +17,9 @@ const store = new Store({
y: undefined,
},
user: null,
bodyshop: {
id: "6089913a-7522-49e7-8c96-786a488b738d", //TODO: Remove hard coded default.
},
},
},
});

View File

@@ -7,16 +7,17 @@ import ipcTypes from "../../util/ipcTypes.json";
import NavigationHeader from "./components/NavigationHeader/Navigationheader";
import SignInForm from "./components/SignInForm/SignInForm";
import { auth } from "./util/firebase";
import {} from "react-error-boundary";
import { ErrorBoundary } from "react-error-boundary";
import ErrorBoundaryFallback from "./components/ErrorBoundaryFallback/ErrorBoundaryFallback";
import Settings from "./components/Settings/Settings";
import Home from "./components/Home/Home";
import Settings from "./components/Settings/Settings";
import { Provider } from "react-redux";
import reduxStore from "./redux/redux-store";
const App: React.FC = () => {
const [user, setUser] = useState<User | null>(null);
auth.onAuthStateChanged((user) => {
auth.onAuthStateChanged((user: User | null) => {
setUser(user);
//Send back to the main process so that it knows we are authenticated.
if (user) {
@@ -24,27 +25,30 @@ const App: React.FC = () => {
ipcTypes.toMain.authStateChanged,
user.toJSON()
);
window.electron.ipcRenderer.send(ipcTypes.toMain.watcher.start);
}
});
return (
<BrowserRouter>
<ErrorBoundary FallbackComponent={ErrorBoundaryFallback}>
<Layout>
{!user ? (
<SignInForm />
) : (
<>
<NavigationHeader />
<Routes>
<Route path="/" element={<Home />} />
<Route path="settings" element={<Settings />} />
</Routes>
</>
)}
</Layout>
</ErrorBoundary>
</BrowserRouter>
<Provider store={reduxStore}>
<BrowserRouter>
<ErrorBoundary FallbackComponent={ErrorBoundaryFallback}>
<Layout>
{!user ? (
<SignInForm />
) : (
<>
<NavigationHeader />
<Routes>
<Route path="/" element={<Home />} />
<Route path="settings" element={<Settings />} />
</Routes>
</>
)}
</Layout>
</ErrorBoundary>
</BrowserRouter>
</Provider>
);
};

View File

@@ -0,0 +1,37 @@
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import type { RootState } from "./redux-store";
// Define a type for the slice state
interface AppState {
value: number;
}
// Define the initial state using that type
const initialState: AppState = {
value: 0,
};
export const appSlice = createSlice({
name: "counter",
// `createSlice` will infer the state type from the `initialState` argument
initialState,
reducers: {
increment: (state) => {
state.value += 1;
},
decrement: (state) => {
state.value -= 1;
},
// Use the PayloadAction type to declare the contents of `action.payload`
incrementByAmount: (state, action: PayloadAction<number>) => {
state.value += action.payload;
},
},
});
export const { increment, decrement, incrementByAmount } = appSlice.actions;
// Other code such as selectors can use the imported `RootState` type
export const selectCount = (state: RootState): number => state.app.value;
export default appSlice.reducer;

View File

@@ -0,0 +1,13 @@
import { configureStore } from "@reduxjs/toolkit";
import appReducer from "./app.slice";
const store = configureStore({
reducer: { app: appReducer },
});
// Infer the `RootState` and `AppDispatch` types from the store itself
export type RootState = ReturnType<typeof store.getState>;
// Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState}
export type AppDispatch = typeof store.dispatch;
export type AppStore = typeof store;
export default store;

View File

@@ -0,0 +1,8 @@
import type { TypedUseSelectorHook } from "react-redux";
import { useDispatch, useSelector, useStore } from "react-redux";
import type { AppDispatch, AppStore, RootState } from "./redux-store";
// Use throughout your app instead of plain `useDispatch` and `useSelector`
export const useAppDispatch: () => AppDispatch = useDispatch;
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
export const useAppStore: () => AppStore = useStore;

View File

@@ -22,7 +22,9 @@ function deepLowerCaseKeys<T = any>(obj: any): T {
const lowercaseKey = key.toLowerCase();
result[lowercaseKey] =
typeof value === "object" && value !== null
typeof value === "object" &&
value !== null &&
Object.keys(value).length > 0
? deepLowerCaseKeys(value)
: value;