diff --git a/src/main/decoder/decode-lin.ts b/src/main/decoder/decode-lin.ts index 166eba0..ac855c6 100644 --- a/src/main/decoder/decode-lin.ts +++ b/src/main/decoder/decode-lin.ts @@ -2,8 +2,9 @@ import { DBFFile } from "dbffile"; import log from "electron-log/main"; import _ from "lodash"; import deepLowerCaseKeys from "../../util/deepLowercaseKeys"; -import { DecodedLin, DecodedLinLine } from "./decode-lin.interface"; import errorTypeCheck from "../../util/errorTypeCheck"; +import store from "../store/store"; +import { DecodedLin, DecodedLinLine } from "./decode-lin.interface"; const DecodeLin = async ( extensionlessFilePath: string @@ -25,6 +26,7 @@ const DecodeLin = async ( //AD2 will always have only 1 row. //Commented lines have been cross referenced with existing partner fields. + const opCodeData = store.get("app.masterdata.opcodes"); //TODO: Type the op codes const rawLinData: DecodedLinLine[] = rawDBFRecord.map((record) => { const singleLineData: DecodedLinLine = deepLowerCaseKeys( @@ -81,17 +83,7 @@ const DecodeLin = async ( ]) ); //Apply line by line adjustments. - singleLineData.op_code_desc = ""; //TODO: Implement the OP Code Lookup. - //Partner previously queried this on login and stored it. Then referenced it and added here. - // Sample Code: - // try - // { - // lin.op_code_desc = Utils.AppMetaData.CiecaOpCodes[lin.lbr_op.Value]["desc"].Value; - // } - // catch (Exception Ex) - // { - // logger.Warn(Ex, "Couldnt find OpCodeDesc from {0} ", lin.lbr_op.Value); - // } + singleLineData.op_code_desc = opCodeData[singleLineData.lbr_op]?.desc; return singleLineData; }); diff --git a/src/main/decoder/decoder.ts b/src/main/decoder/decoder.ts index 806fb7e..e03fbeb 100644 --- a/src/main/decoder/decoder.ts +++ b/src/main/decoder/decoder.ts @@ -28,6 +28,8 @@ import { DecodedVeh } from "./decode-veh.interface"; import { DecodedEnv } from "./decode-env.interface"; import DecodeEnv from "./decode-env"; import fs from "fs"; +import store from "../store/store"; +import client from "../graphql/graphql-client"; async function ImportJob(filepath: string): Promise { const parsedFilePath = path.parse(filepath); @@ -90,6 +92,52 @@ async function ImportJob(filepath: string): Promise { log.debug("Job Object", { jobObject, }); + + //Build the request object + + //Insert it + const newAvailableJob = { + // newJob.uploaded_by = Auth.authlink.User.Email; + // newJob.bodyshopid = AppMetaData.ActiveShopId; + // newJob.cieca_id = item.Job.ciecaid; + // newJob.est_data = item.Job; + // newJob.ownr_name = item.Job.ownr_fn?.Value + " " + item.Job.ownr_ln?.Value + " " + item.Job.ownr_co_nm?.Value; + // newJob.ins_co_nm = item.Job.ins_co_nm?.Value; + // newJob.vehicle_info = item.Job.vehicle.data.v_model_yr?.Value + " " + item.Job.vehicle.data.v_make_desc?.Value + " " + item.Job.vehicle.data.v_model_desc?.Value; + // newJob.clm_no = item.Job.clm_no?.Value; + // newJob.clm_amt = item.Job.clm_total?.Value; + // newJob.source_system = item.Job.source_system?.Value; + uploaded_by: store.get("app.user.email"), + bodyshopid: store.get("app.bodyshop.id"), + cieca_id: jobObject.ciecaid, + est_data: jobObject, + ownr_name: `${jobObject.ownr_fn} ${jobObject.ownr_ln} ${jobObject.ownr_co_nm}`, + ins_co_nm: jobObject.ins_co_nm, + vehicle_info: `${jobObject.v_model_yr} ${jobObject.v_make_desc} ${jobObject.v_model_desc}`, + clm_no: jobObject.clm_no, + clm_amt: jobObject.clm_total, + // source_system: jobObject.source_system, //TODO: Add back source system if needed. + }; + + const existingVehicleId: uuid = await client.query () + // var vehuuid = await Utils.Queries.VehicleQueries.GetVehicleUuidByVin(item?.Job?.vehicle?.data?.v_vin?.Value ?? ""); + // if (!string.IsNullOrEmpty(vehuuid)) + // { + // newJob.est_data.vehicle = null; + // newJob.est_data.vehicleid = vehuuid; + // } + + + // string jobId = await Utils.Queries.JobsQueries.CheckSupplementByClaimNo(item.Job.clm_no?.Value ?? ""); + + // if (!string.IsNullOrEmpty(jobId)) + // { + // newJob.issupplement = true; + // newJob.jobid = jobId; + // }; + + //Check if the vehicle exists, if it does, use that UUID, if not, keep it to insert it. + } catch (error) { log.error("Error encountered while decoding job. ", errorTypeCheck(error)); } diff --git a/src/main/graphql/graphql-client.ts b/src/main/graphql/graphql-client.ts index efd2f1c..69a9ac5 100644 --- a/src/main/graphql/graphql-client.ts +++ b/src/main/graphql/graphql-client.ts @@ -6,6 +6,13 @@ import ipcTypes from "../../util/ipcTypes.json"; const requestMiddleware: RequestMiddleware = async (request) => { const token = await getTokenFromRenderer(); + log.info( + `%c[Graphql Request]%c${request.operationName}`, + "color: red", + "color: green", + request + ); + return { ...request, headers: { ...request.headers, Authorization: `Bearer ${token}` }, diff --git a/src/main/graphql/queries.ts b/src/main/graphql/queries.ts new file mode 100644 index 0000000..135cafb --- /dev/null +++ b/src/main/graphql/queries.ts @@ -0,0 +1,73 @@ +import { parse, TypedQueryDocumentNode } from "graphql"; +import { gql } from "graphql-request"; + +// Define types for the query result and variables +export interface ActiveBodyshopQueryResult { + bodyshops: Array<{ + id: string; + shopname: string; + region_config: string; + }>; +} + +// No variables needed for this query +interface ActiveBodyshopQueryVariables {} + +// Transform the string query into a TypedQueryDocumentNode +export const QUERY_ACTIVE_BODYSHOP_TYPED: TypedQueryDocumentNode< + ActiveBodyshopQueryResult, + ActiveBodyshopQueryVariables +> = parse(gql` + query QUERY_ACTIVE_BODYSHOP { + bodyshops(where: { associations: { active: { _eq: true } } }) { + id + shopname + region_config + } + } +`); + +export interface MasterdataQueryResult { + masterdata: Array<{ + value: string; + key: string; + }>; +} + +interface MasterdataQueryVariables { + key: string; +} + +export const QUERY_MASTERDATA_TYPED: TypedQueryDocumentNode< + MasterdataQueryResult, + MasterdataQueryVariables +> = parse(gql` + query QUERY_MASTERDATA($key: String!) { + masterdata(where: { key: { _eq: $key } }) { + value + key + } + } +`); + +export interface VehicleQueryResult { + masterdata: Array<{ + value: string; + key: string; + }>; +} + +interface VehicleQueryVariables { + vin: string; +} + +export const QUERY_VEHICLE_BY_VIN_TYPED: TypedQueryDocumentNode< + VehicleQueryResult, + VehicleQueryVariables +> = parse(gql` + query QUERY_VEHICLE_BY_VIN($vin: String!) { + vehicles(where: { v_vin: { _eq: $vin } }) { + id + } + } +`); diff --git a/src/main/index.ts b/src/main/index.ts index 6369bad..c2d9720 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -150,6 +150,14 @@ function createWindow(): void { shell.openPath(path.dirname(store.path)); }, }, + { + label: "Log the Store", + click: (): void => { + log.debug( + "Store Contents" + JSON.stringify(store.store, null, 4) + ); + }, + }, { type: "separator", }, diff --git a/src/main/ipc/ipcMainConfig.ts b/src/main/ipc/ipcMainConfig.ts index 52cfedf..0ce7bbf 100644 --- a/src/main/ipc/ipcMainConfig.ts +++ b/src/main/ipc/ipcMainConfig.ts @@ -49,17 +49,15 @@ if (import.meta.env.DEV) { log.debug("[IPC Debug Functions] Adding Debug Handlers"); ipcMain.on(ipcTypes.toMain.debug.decodeEstimate, async (): Promise => { - // 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); + 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); - // 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); diff --git a/src/main/ipc/ipcMainHandler.user.ts b/src/main/ipc/ipcMainHandler.user.ts index 1899009..c15ed85 100644 --- a/src/main/ipc/ipcMainHandler.user.ts +++ b/src/main/ipc/ipcMainHandler.user.ts @@ -1,12 +1,38 @@ import { IpcMainEvent } from "electron"; import { User } from "firebase/auth"; +import client from "../graphql/graphql-client"; +import { + ActiveBodyshopQueryResult, + MasterdataQueryResult, + QUERY_ACTIVE_BODYSHOP_TYPED, + QUERY_MASTERDATA_TYPED, +} from "../graphql/queries"; import Store from "../store/store"; +import log from "electron-log/main"; const ipcMainHandleAuthStateChanged = async ( event: IpcMainEvent, user: User | null ): Promise => { Store.set("user", user); + + //Need to query the currently active shop, and store the metadata as well. + //Also need to query the OP Codes for decoding reference. + const activeBodyshop: ActiveBodyshopQueryResult = await client.request( + QUERY_ACTIVE_BODYSHOP_TYPED + ); + + Store.set("app.bodyshop", activeBodyshop.bodyshops[0]); + + const OpCodes: MasterdataQueryResult = await client.request( + QUERY_MASTERDATA_TYPED, + { + key: `${activeBodyshop.bodyshops[0].region_config}_ciecaopcodes`, + } + ); + Store.set("app.masterdata.opcodes", JSON.parse(OpCodes.masterdata[0]?.value)); + log.debug("Received authentication state change from Renderer.", user); + log.debug("Requery shop information & master data."); }; export { ipcMainHandleAuthStateChanged }; diff --git a/src/main/store/store.ts b/src/main/store/store.ts index dc2fd5b..d6b6ea2 100644 --- a/src/main/store/store.ts +++ b/src/main/store/store.ts @@ -17,8 +17,9 @@ const store = new Store({ y: undefined, }, user: null, - bodyshop: { - id: "6089913a-7522-49e7-8c96-786a488b738d", //TODO: Remove hard coded default. + bodyshop: {}, + masterdata: { + opcodes: null, }, }, }, diff --git a/src/renderer/src/App.tsx b/src/renderer/src/App.tsx index 0d3d864..20788b0 100644 --- a/src/renderer/src/App.tsx +++ b/src/renderer/src/App.tsx @@ -1,33 +1,39 @@ import "@ant-design/v5-patch-for-react-19"; import { Layout } from "antd"; import { User } from "firebase/auth"; -import { useState } from "react"; -import { BrowserRouter, Route, Routes } from "react-router"; -import ipcTypes from "../../util/ipcTypes.json"; -import NavigationHeader from "./components/NavigationHeader/Navigationheader"; -import SignInForm from "./components/SignInForm/SignInForm"; -import { auth } from "./util/firebase"; +import { useEffect, useState } from "react"; import { ErrorBoundary } from "react-error-boundary"; +import { Provider } from "react-redux"; +import { BrowserRouter, Route, Routes } from "react-router"; +import ipcTypes from "../../util/ipcTypes"; import ErrorBoundaryFallback from "./components/ErrorBoundaryFallback/ErrorBoundaryFallback"; import Home from "./components/Home/Home"; +import NavigationHeader from "./components/NavigationHeader/Navigationheader"; import Settings from "./components/Settings/Settings"; -import { Provider } from "react-redux"; +import SignInForm from "./components/SignInForm/SignInForm"; import reduxStore from "./redux/redux-store"; +import { auth } from "./util/firebase"; const App: React.FC = () => { const [user, setUser] = useState(null); - auth.onAuthStateChanged((user: User | null) => { - setUser(user); - //Send back to the main process so that it knows we are authenticated. - if (user) { - window.electron.ipcRenderer.send( - ipcTypes.toMain.authStateChanged, - user.toJSON() - ); - window.electron.ipcRenderer.send(ipcTypes.toMain.watcher.start); - } - }); + useEffect(() => { + // Only set up the listener once when component mounts + const unsubscribe = auth.onAuthStateChanged((user: User | null) => { + setUser(user); + //Send back to the main process so that it knows we are authenticated. + if (user) { + window.electron.ipcRenderer.send( + ipcTypes.toMain.authStateChanged, + user.toJSON() + ); + window.electron.ipcRenderer.send(ipcTypes.toMain.watcher.start); + } + }); + + // Clean up the listener when component unmounts + return (): void => unsubscribe(); + }, []); return (