3
Xdev-app-update.yml
Normal file
3
Xdev-app-update.yml
Normal file
@@ -0,0 +1,3 @@
|
||||
provider: s3
|
||||
bucket: rps-updater
|
||||
region: ca-central-1
|
||||
@@ -80,163 +80,190 @@ async function DecodeEstimate(filePath, includeFilePathInReturnJob = false) {
|
||||
}
|
||||
|
||||
async function DecodeAd1File(extensionlessFilePath) {
|
||||
let dbf = await DBFFile.open(`${extensionlessFilePath}A.AD1`);
|
||||
let records = await dbf.readRecords(1);
|
||||
return _.pick(records[0], [
|
||||
// "INS_CO_ID",
|
||||
"INS_CO_NM",
|
||||
// "INS_ADDR1",
|
||||
// "INS_ADDR2",
|
||||
// "INS_CITY",
|
||||
// "INS_ST",
|
||||
// "INS_ZIP",
|
||||
// "INS_CTRY",
|
||||
let dbf;
|
||||
try {
|
||||
dbf = await DBFFile.open(`${extensionlessFilePath}A.AD1`);
|
||||
} catch (error) {
|
||||
log.error("Error opening AD1 File.", error);
|
||||
dbf = await DBFFile.open(`${extensionlessFilePath}.AD1`);
|
||||
dbf && log.log("Found AD1 file using regular CIECA Id.");
|
||||
} finally {
|
||||
if (!dbf) return {};
|
||||
let records = await dbf.readRecords(1);
|
||||
return _.pick(records[0], [
|
||||
// "INS_CO_ID",
|
||||
"INS_CO_NM",
|
||||
// "INS_ADDR1",
|
||||
// "INS_ADDR2",
|
||||
// "INS_CITY",
|
||||
// "INS_ST",
|
||||
// "INS_ZIP",
|
||||
// "INS_CTRY",
|
||||
|
||||
// "INS_EA",
|
||||
// "POLICY_NO",
|
||||
// "DED_AMT",
|
||||
// "DED_STATUS",
|
||||
// "ASGN_NO",
|
||||
//"ASGN_DATE",
|
||||
// "ASGN_TYPE",
|
||||
"CLM_NO",
|
||||
// "CLM_OFC_ID",
|
||||
// "CLM_OFC_NM",
|
||||
// "CLM_ADDR1",
|
||||
// "CLM_ADDR2",
|
||||
// "CLM_CITY",
|
||||
// "CLM_ST",
|
||||
// "CLM_ZIP",
|
||||
// "CLM_CTRY",
|
||||
// "CLM_PH1",
|
||||
// "CLM_PH1X",
|
||||
// "CLM_PH2",
|
||||
// "CLM_PH2X",
|
||||
// "CLM_FAX",
|
||||
// "CLM_FAXX",
|
||||
// "CLM_CT_LN",
|
||||
// "CLM_CT_FN",
|
||||
// "CLM_TITLE",
|
||||
// "CLM_CT_PH",
|
||||
// "CLM_CT_PHX",
|
||||
// "CLM_EA",
|
||||
// "PAYEE_NMS",
|
||||
// "PAY_TYPE",
|
||||
// "PAY_DATE",
|
||||
// "PAY_CHKNM",
|
||||
// "PAY_AMT",
|
||||
// "AGT_CO_ID",
|
||||
// "AGT_CO_NM",
|
||||
// "AGT_ADDR1",
|
||||
// "AGT_ADDR2",
|
||||
// "AGT_CITY",
|
||||
// "AGT_ST",
|
||||
// "AGT_ZIP",
|
||||
// "AGT_CTRY",
|
||||
// "AGT_PH1",
|
||||
// "AGT_PH1X",
|
||||
// "AGT_PH2",
|
||||
// "AGT_PH2X",
|
||||
// "AGT_FAX",
|
||||
// "AGT_FAXX",
|
||||
// "AGT_CT_LN",
|
||||
// "AGT_CT_FN",
|
||||
// "AGT_CT_PH",
|
||||
// "AGT_CT_PHX",
|
||||
// "AGT_EA",
|
||||
// "AGT_LIC_NO",
|
||||
"LOSS_DATE",
|
||||
// "LOSS_TYPE",
|
||||
// "LOSS_DESC",
|
||||
// "THEFT_IND",
|
||||
// "CAT_NO",
|
||||
// "TLOS_IND",
|
||||
// "CUST_PR",
|
||||
// "INSD_LN",
|
||||
// "INSD_FN",
|
||||
// "INSD_TITLE",
|
||||
// "INSD_CO_NM",
|
||||
// "INSD_ADDR1",
|
||||
// "INSD_ADDR2",
|
||||
// "INSD_CITY",
|
||||
// "INSD_ST",
|
||||
// "INSD_ZIP",
|
||||
// "INSD_CTRY",
|
||||
// "INSD_PH1",
|
||||
// "INSD_PH1X",
|
||||
// "INSD_PH2",
|
||||
// "INSD_PH2X",
|
||||
// "INSD_FAX",
|
||||
// "INSD_FAXX",
|
||||
// "INSD_EA",
|
||||
"OWNR_LN",
|
||||
"OWNR_FN",
|
||||
// "OWNR_TITLE",
|
||||
// "OWNR_CO_NM",
|
||||
// "OWNR_ADDR1",
|
||||
// "OWNR_ADDR2",
|
||||
// "OWNR_CITY",
|
||||
// "OWNR_ST",
|
||||
// "OWNR_ZIP",
|
||||
// "OWNR_CTRY",
|
||||
// "OWNR_PH1",
|
||||
// "OWNR_PH1X",
|
||||
// "OWNR_PH2",
|
||||
// "OWNR_PH2X",
|
||||
// "OWNR_FAX",
|
||||
// "OWNR_FAXX",
|
||||
// "OWNR_EA",
|
||||
// "INS_PH1",
|
||||
// "INS_PH1X",
|
||||
// "INS_PH2",
|
||||
// "INS_PH2X",
|
||||
// "INS_FAX",
|
||||
// "INS_FAXX",
|
||||
// "INS_CT_LN",
|
||||
// "INS_CT_FN",
|
||||
// "INS_TITLE",
|
||||
// "INS_CT_PH",
|
||||
// "INS_CT_PHX",
|
||||
// "LOSS_CAT",
|
||||
]);
|
||||
// "INS_EA",
|
||||
// "POLICY_NO",
|
||||
// "DED_AMT",
|
||||
// "DED_STATUS",
|
||||
// "ASGN_NO",
|
||||
//"ASGN_DATE",
|
||||
// "ASGN_TYPE",
|
||||
"CLM_NO",
|
||||
// "CLM_OFC_ID",
|
||||
// "CLM_OFC_NM",
|
||||
// "CLM_ADDR1",
|
||||
// "CLM_ADDR2",
|
||||
// "CLM_CITY",
|
||||
// "CLM_ST",
|
||||
// "CLM_ZIP",
|
||||
// "CLM_CTRY",
|
||||
// "CLM_PH1",
|
||||
// "CLM_PH1X",
|
||||
// "CLM_PH2",
|
||||
// "CLM_PH2X",
|
||||
// "CLM_FAX",
|
||||
// "CLM_FAXX",
|
||||
// "CLM_CT_LN",
|
||||
// "CLM_CT_FN",
|
||||
// "CLM_TITLE",
|
||||
// "CLM_CT_PH",
|
||||
// "CLM_CT_PHX",
|
||||
// "CLM_EA",
|
||||
// "PAYEE_NMS",
|
||||
// "PAY_TYPE",
|
||||
// "PAY_DATE",
|
||||
// "PAY_CHKNM",
|
||||
// "PAY_AMT",
|
||||
// "AGT_CO_ID",
|
||||
// "AGT_CO_NM",
|
||||
// "AGT_ADDR1",
|
||||
// "AGT_ADDR2",
|
||||
// "AGT_CITY",
|
||||
// "AGT_ST",
|
||||
// "AGT_ZIP",
|
||||
// "AGT_CTRY",
|
||||
// "AGT_PH1",
|
||||
// "AGT_PH1X",
|
||||
// "AGT_PH2",
|
||||
// "AGT_PH2X",
|
||||
// "AGT_FAX",
|
||||
// "AGT_FAXX",
|
||||
// "AGT_CT_LN",
|
||||
// "AGT_CT_FN",
|
||||
// "AGT_CT_PH",
|
||||
// "AGT_CT_PHX",
|
||||
// "AGT_EA",
|
||||
// "AGT_LIC_NO",
|
||||
"LOSS_DATE",
|
||||
// "LOSS_TYPE",
|
||||
// "LOSS_DESC",
|
||||
// "THEFT_IND",
|
||||
// "CAT_NO",
|
||||
// "TLOS_IND",
|
||||
// "CUST_PR",
|
||||
// "INSD_LN",
|
||||
// "INSD_FN",
|
||||
// "INSD_TITLE",
|
||||
// "INSD_CO_NM",
|
||||
// "INSD_ADDR1",
|
||||
// "INSD_ADDR2",
|
||||
// "INSD_CITY",
|
||||
// "INSD_ST",
|
||||
// "INSD_ZIP",
|
||||
// "INSD_CTRY",
|
||||
// "INSD_PH1",
|
||||
// "INSD_PH1X",
|
||||
// "INSD_PH2",
|
||||
// "INSD_PH2X",
|
||||
// "INSD_FAX",
|
||||
// "INSD_FAXX",
|
||||
// "INSD_EA",
|
||||
"OWNR_LN",
|
||||
"OWNR_FN",
|
||||
// "OWNR_TITLE",
|
||||
// "OWNR_CO_NM",
|
||||
// "OWNR_ADDR1",
|
||||
// "OWNR_ADDR2",
|
||||
// "OWNR_CITY",
|
||||
// "OWNR_ST",
|
||||
// "OWNR_ZIP",
|
||||
// "OWNR_CTRY",
|
||||
// "OWNR_PH1",
|
||||
// "OWNR_PH1X",
|
||||
// "OWNR_PH2",
|
||||
// "OWNR_PH2X",
|
||||
// "OWNR_FAX",
|
||||
// "OWNR_FAXX",
|
||||
// "OWNR_EA",
|
||||
// "INS_PH1",
|
||||
// "INS_PH1X",
|
||||
// "INS_PH2",
|
||||
// "INS_PH2X",
|
||||
// "INS_FAX",
|
||||
// "INS_FAXX",
|
||||
// "INS_CT_LN",
|
||||
// "INS_CT_FN",
|
||||
// "INS_TITLE",
|
||||
// "INS_CT_PH",
|
||||
// "INS_CT_PHX",
|
||||
// "LOSS_CAT",
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
async function DecodeAd2File(extensionlessFilePath) {
|
||||
let dbf = await DBFFile.open(`${extensionlessFilePath}B.AD2`);
|
||||
let records = await dbf.readRecords(1);
|
||||
return _.pick(records[0], ["CLMT_LN", "CLMT_FN"]);
|
||||
let dbf;
|
||||
try {
|
||||
dbf = await DBFFile.open(`${extensionlessFilePath}B.AD2`);
|
||||
} catch (error) {
|
||||
log.error("Error opening AD2 File.", error);
|
||||
dbf = await DBFFile.open(`${extensionlessFilePath}.AD2`);
|
||||
dbf && log.log("Found AD2 file using regular CIECA Id.");
|
||||
} finally {
|
||||
if (!dbf) return {};
|
||||
let records = await dbf.readRecords(1);
|
||||
return _.pick(records[0], ["CLMT_LN", "CLMT_FN"]);
|
||||
}
|
||||
}
|
||||
|
||||
async function DecodeVehFile(extensionlessFilePath) {
|
||||
let dbf = await DBFFile.open(`${extensionlessFilePath}V.VEH`);
|
||||
let records = await dbf.readRecords(1);
|
||||
return _.pick(records[0], [
|
||||
// "IMPACT_1",
|
||||
// "IMPACT_2",
|
||||
// "DB_V_CODE",
|
||||
// "PLATE_NO",
|
||||
// "PLATE_ST",
|
||||
"V_VIN",
|
||||
// "V_COND",
|
||||
// "V_PROD_DT",
|
||||
"V_MODEL_YR",
|
||||
// "V_MAKECODE",
|
||||
"V_MAKEDESC",
|
||||
"V_MODEL",
|
||||
"V_TYPE",
|
||||
"V_MILEAGE",
|
||||
// "V_BSTYLE",
|
||||
// "V_TRIMCODE",
|
||||
// "TRIM_COLOR",
|
||||
// "V_MLDGCODE",
|
||||
// "V_ENGINE",
|
||||
// "V_COLOR",
|
||||
// "V_TONE",
|
||||
// "V_STAGE",
|
||||
// "PAINT_CD1",
|
||||
// "PAINT_CD2",
|
||||
// "PAINT_CD3",
|
||||
]);
|
||||
let dbf;
|
||||
try {
|
||||
dbf = await DBFFile.open(`${extensionlessFilePath}V.VEH`);
|
||||
} catch (error) {
|
||||
log.error("Error opening VEH File.", error);
|
||||
dbf = await DBFFile.open(`${extensionlessFilePath}.VEH`);
|
||||
dbf && log.log("Found VEH file using regular CIECA Id.");
|
||||
} finally {
|
||||
if (!dbf) return {};
|
||||
let records = await dbf.readRecords(1);
|
||||
return _.pick(records[0], [
|
||||
// "IMPACT_1",
|
||||
// "IMPACT_2",
|
||||
// "DB_V_CODE",
|
||||
// "PLATE_NO",
|
||||
// "PLATE_ST",
|
||||
"V_VIN",
|
||||
// "V_COND",
|
||||
// "V_PROD_DT",
|
||||
"V_MODEL_YR",
|
||||
// "V_MAKECODE",
|
||||
"V_MAKEDESC",
|
||||
"V_MODEL",
|
||||
"V_TYPE",
|
||||
"V_MILEAGE",
|
||||
// "V_BSTYLE",
|
||||
// "V_TRIMCODE",
|
||||
// "TRIM_COLOR",
|
||||
// "V_MLDGCODE",
|
||||
// "V_ENGINE",
|
||||
// "V_COLOR",
|
||||
// "V_TONE",
|
||||
// "V_STAGE",
|
||||
// "PAINT_CD1",
|
||||
// "PAINT_CD2",
|
||||
// "PAINT_CD3",
|
||||
]);
|
||||
}
|
||||
}
|
||||
async function DecodeTtlFile(extensionlessFilePath) {
|
||||
let dbf = await DBFFile.open(`${extensionlessFilePath}.TTL`);
|
||||
@@ -356,7 +383,8 @@ async function DecodeLinFile(extensionlessFilePath) {
|
||||
!jobline.part_type ||
|
||||
jobline.db_ref.startsWith("900") ||
|
||||
jobline.line_desc.toLowerCase().startsWith("urethane") ||
|
||||
jobline.line_desc.toLowerCase().startsWith("wheel") ||
|
||||
jobline.line_desc.toLowerCase().includes("wheel") ||
|
||||
jobline.line_desc.toLowerCase().includes("tire") ||
|
||||
jobline.line_desc.toLowerCase().startsWith("hazardous") ||
|
||||
jobline.line_desc.toLowerCase().startsWith("detail") ||
|
||||
jobline.line_desc.toLowerCase().startsWith("clean") ||
|
||||
|
||||
@@ -16,6 +16,11 @@ async function GetListOfEstimates() {
|
||||
const FilteredListOfSummarizedEstimates = ListOfSummarizedEstimates.filter(
|
||||
(j) => !j.ERROR
|
||||
);
|
||||
|
||||
log.log(
|
||||
"Number of estimates filtered on file scan due to error: ",
|
||||
ListOfSummarizedEstimates.length - FilteredListOfSummarizedEstimates.length
|
||||
);
|
||||
return FilteredListOfSummarizedEstimates;
|
||||
}
|
||||
|
||||
@@ -71,4 +76,4 @@ async function DeleteAllEms() {
|
||||
}
|
||||
|
||||
exports.GetListOfEstimates = GetListOfEstimates;
|
||||
exports.DeleteAllEms = DeleteAllEms
|
||||
exports.DeleteAllEms = DeleteAllEms;
|
||||
|
||||
122
electron/main.js
122
electron/main.js
@@ -19,6 +19,8 @@ const Nucleus = require("nucleus-nodejs");
|
||||
require("./ipc-main-handler");
|
||||
require("./analytics");
|
||||
|
||||
autoUpdater.autoDownload = false;
|
||||
|
||||
autoUpdater.logger = log;
|
||||
autoUpdater.logger.transports.file.level = "info";
|
||||
log.info("App starting...", app.getVersion());
|
||||
@@ -49,6 +51,12 @@ var menu = Menu.buildFromTemplate([
|
||||
mainWindow.webContents.session.clearStorageData();
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Sign Out",
|
||||
click() {
|
||||
mainWindow.webContents.send(ipcTypes.app.toRenderer.signOut);
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Exit",
|
||||
click() {
|
||||
@@ -70,10 +78,7 @@ var menu = Menu.buildFromTemplate([
|
||||
{
|
||||
label: `Check for Updates (currently ${app.getVersion()})`,
|
||||
click() {
|
||||
autoUpdater.checkForUpdatesAndNotify({
|
||||
title: "ImEX RPS Update Downloaded",
|
||||
body: "Restart ImEX RPS to install.",
|
||||
});
|
||||
checkForUpdates();
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -168,7 +173,6 @@ function createWindow() {
|
||||
}
|
||||
|
||||
mainWindow.maximize();
|
||||
autoUpdater.checkForUpdatesAndNotify();
|
||||
|
||||
globalShortcut.register("CommandOrControl+Shift+I", () => {
|
||||
mainWindow.webContents.toggleDevTools();
|
||||
@@ -242,46 +246,22 @@ function createTray() {
|
||||
return appIcon;
|
||||
}
|
||||
|
||||
autoUpdater.on("checking-for-update", () => {
|
||||
log.log("Checking for update...");
|
||||
});
|
||||
autoUpdater.on("update-available", (ev, info) => {
|
||||
log.log("Update available.");
|
||||
});
|
||||
autoUpdater.on("update-not-available", (ev, info) => {
|
||||
log.log("Update not available.");
|
||||
});
|
||||
autoUpdater.on("error", (ev, err) => {
|
||||
log.log("Error in auto-updater.");
|
||||
});
|
||||
autoUpdater.on("download-progress", (ev, progressObj) => {
|
||||
log.log("Download progress...");
|
||||
});
|
||||
// autoUpdater.on("update-downloaded", (ev, info) => {
|
||||
// console.log("Update downloaded; will install in 5 seconds");
|
||||
// autoUpdater.on("checking-for-update", () => {
|
||||
// log.log("Checking for update...");
|
||||
// });
|
||||
autoUpdater.on("update-downloaded", (ev, info) => {
|
||||
Nucleus.track("UPDATE_DOWNLOADED", info);
|
||||
if (process.env.NODE_ENV === "production") {
|
||||
dialog.showMessageBox(
|
||||
{
|
||||
type: "info",
|
||||
title: "Found Updates",
|
||||
message: "Found updates, do you want update now?",
|
||||
buttons: ["Sure", "No"],
|
||||
},
|
||||
(buttonIndex) => {
|
||||
if (buttonIndex === 0) {
|
||||
const isSilent = true;
|
||||
const isForceRunAfter = true;
|
||||
autoUpdater.quitAndInstall(isSilent, isForceRunAfter);
|
||||
} else {
|
||||
logger.warn("Error");
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
// autoUpdater.on("update-available", (ev, info) => {
|
||||
// log.log("Update available.");
|
||||
// });
|
||||
// autoUpdater.on("update-not-available", (ev, info) => {
|
||||
// log.log("Update not available.");
|
||||
// });
|
||||
// autoUpdater.on("error", (ev, err) => {
|
||||
// log.log("Error in auto-updater.");
|
||||
// });
|
||||
|
||||
// // autoUpdater.on("update-downloaded", (ev, info) => {
|
||||
// // console.log("Update downloaded; will install in 5 seconds");
|
||||
// // });
|
||||
|
||||
function openNoticeWindow() {
|
||||
if (noticeWindow) {
|
||||
@@ -301,3 +281,57 @@ function openNoticeWindow() {
|
||||
noticeWindow = null;
|
||||
});
|
||||
}
|
||||
|
||||
ipcMain.on(ipcTypes.app.toMain.checkForUpdates, (event, args) => {
|
||||
checkForUpdates();
|
||||
});
|
||||
|
||||
ipcMain.on(ipcTypes.app.toMain.downloadUpdates, (event, args) => {
|
||||
autoUpdater.downloadUpdate();
|
||||
});
|
||||
|
||||
ipcMain.on(ipcTypes.app.toMain.installUpdates, (event, args) => {
|
||||
const isSilent = true;
|
||||
const isForceRunAfter = true;
|
||||
autoUpdater.quitAndInstall(isSilent, isForceRunAfter);
|
||||
});
|
||||
|
||||
autoUpdater.on("download-progress", (ev) => {
|
||||
mainWindow.webContents.send(ipcTypes.app.toRenderer.downloadProgress, ev);
|
||||
});
|
||||
|
||||
autoUpdater.on("update-downloaded", (ev, info) => {
|
||||
Nucleus.track("UPDATE_DOWNLOADED", info);
|
||||
// if (process.env.NODE_ENV === "production") {
|
||||
|
||||
dialog.showMessageBox(
|
||||
{
|
||||
type: "info",
|
||||
title: "ImeX RPS Update Manager",
|
||||
message: `ImEX RPS is ready to update to Version ${ev.version}. It is highly recommended that you update immediately. Would you like to update now? RPS will automatically restart.`,
|
||||
buttons: ["Yes", "No"],
|
||||
},
|
||||
(buttonIndex) => {
|
||||
if (buttonIndex === 0) {
|
||||
const isSilent = true;
|
||||
const isForceRunAfter = true;
|
||||
autoUpdater.quitAndInstall(isSilent, isForceRunAfter);
|
||||
} else {
|
||||
logger.error("Error");
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
async function checkForUpdates() {
|
||||
try {
|
||||
log.info("Checking for updates.");
|
||||
const result = await autoUpdater.checkForUpdates();
|
||||
const { updateInfo } = result;
|
||||
mainWindow.webContents.send(
|
||||
ipcTypes.app.toRenderer.updateAvailable,
|
||||
updateInfo
|
||||
);
|
||||
} catch (error) {
|
||||
log.error("Error while checking for update", error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"productName": "ImEX RPS",
|
||||
"author": "ImEX Systems Inc. <support@thinkimex.com>",
|
||||
"description": "ImEX RPS",
|
||||
"version": "1.0.4",
|
||||
"version": "1.0.5",
|
||||
"main": "electron/main.js",
|
||||
"homepage": "./",
|
||||
"dependencies": {
|
||||
|
||||
@@ -78,3 +78,13 @@ body {
|
||||
.ant-tabs-content {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.blink_me {
|
||||
animation: blinker 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes blinker {
|
||||
50% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,8 @@ export function DeleteJobAtom({ setSelectedJobId, jobId }) {
|
||||
});
|
||||
const result = await deleteJob({
|
||||
variables: { jobId: jobId },
|
||||
refetchQueries: ["QUERY_ALL_JOBS_PAGINATED"],
|
||||
awaitRefetchQueries: true,
|
||||
});
|
||||
|
||||
if (result.errors) {
|
||||
|
||||
@@ -37,7 +37,13 @@ export default function JobPartsGraphAtom({
|
||||
}, [job, price]);
|
||||
|
||||
if (loading) return <Skeleton active />;
|
||||
if (!job) return <ErrorResultAtom title="Error displaying job data." />;
|
||||
if (!job)
|
||||
return (
|
||||
<ErrorResultAtom
|
||||
title="Error displaying parts graphs."
|
||||
errorMessage="It looks like this job doesn't exist."
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
.blink_me {
|
||||
animation: blinker 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes blinker {
|
||||
50% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { WarningOutlined } from "@ant-design/icons";
|
||||
import { useMutation } from "@apollo/client";
|
||||
import { DatePicker, message, Spin } from "antd";
|
||||
import moment from "moment";
|
||||
@@ -43,7 +44,14 @@ export default function CloseDateDisplayMolecule({ jobId, close_date }) {
|
||||
|
||||
return (
|
||||
<div style={{ cursor: "pointer" }} onClick={() => setEditMode(true)}>
|
||||
{value && value.isValid() ? value.format(DateFormat) : "No date set"}
|
||||
{value && value.isValid() ? (
|
||||
value.format(DateFormat)
|
||||
) : (
|
||||
<div>
|
||||
<span>No date set.</span>
|
||||
<WarningOutlined style={{ marginLeft: ".5rem", color: "orange" }} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ export function JobGroupMolecule({ bodyshop, jobId, group, job }) {
|
||||
});
|
||||
|
||||
if (!result.errors) {
|
||||
message.success("Close date updated.");
|
||||
message.success("Vehicle group updated.");
|
||||
} else {
|
||||
message.error("Error updating job.");
|
||||
}
|
||||
|
||||
@@ -10,7 +10,13 @@ import DeleteJobAtom from "../../atoms/delete-job/delete-job.atom";
|
||||
export default function JobsDetailDescriptionMolecule({ loading, job }) {
|
||||
if (loading) return <Skeleton active />;
|
||||
|
||||
if (!job) return <ErrorResultAtom title="Error displaying job data." />;
|
||||
if (!job)
|
||||
return (
|
||||
<ErrorResultAtom
|
||||
title="Error displaying job header data."
|
||||
errorMessage="It looks like this job doesn't exist."
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
|
||||
@@ -4,6 +4,7 @@ import React, { useState } from "react";
|
||||
import ipcTypes from "../../../ipc.types";
|
||||
import { alphaSort } from "../../../util/sorters";
|
||||
import CurrencyFormatterAtom from "../../atoms/currency-formatter/currency-formatter.atom";
|
||||
import ErrorResultAtom from "../../atoms/error-result/error-result.atom";
|
||||
import IgnoreJobLine from "../../atoms/ignore-job-line/ignore-job-line.atom";
|
||||
import partTypeConverterAtom from "../../atoms/part-type-converter/part-type-converter.atom";
|
||||
import PriceDiffPcFormatterAtom from "../../atoms/price-diff-pc-formatter/price-diff-pc-formatter.atom";
|
||||
@@ -13,6 +14,14 @@ export default function JobLinesTableMolecule({ loading, job }) {
|
||||
const [searchText, setSearchText] = useState("");
|
||||
const [filters, setFilters] = useState({ ignore: ["false"] });
|
||||
|
||||
if (!job) {
|
||||
return (
|
||||
<ErrorResultAtom
|
||||
title="Error Displaying Job Lines"
|
||||
errorMessage="It looks like this job doesn't exist."
|
||||
/>
|
||||
);
|
||||
}
|
||||
const { joblines } = job;
|
||||
const columns = [
|
||||
{
|
||||
|
||||
@@ -6,6 +6,7 @@ import { setSelectedJobId } from "../../../redux/application/application.actions
|
||||
import { selectSelectedJobId } from "../../../redux/application/application.selectors";
|
||||
import TimeAgoFormatter from "../../atoms/time-ago-formatter/time-ago-formatter.atom";
|
||||
import "./jobs-list-item.styles.scss";
|
||||
import { WarningOutlined } from "@ant-design/icons";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
selectedJobId: selectSelectedJobId,
|
||||
@@ -41,7 +42,15 @@ export function JobsListItemMolecule({
|
||||
justifyContent: "space-between",
|
||||
}}
|
||||
>
|
||||
<strong>{item.clm_no || "No Claim Number"}</strong>
|
||||
<strong>
|
||||
{item.clm_no || "No Claim Number"}
|
||||
{!item.close_date && (
|
||||
<WarningOutlined
|
||||
title="No close date has been set for this job."
|
||||
style={{ marginLeft: ".5rem", color: "orange" }}
|
||||
/>
|
||||
)}
|
||||
</strong>
|
||||
<span style={{ fontStyle: "italic" }}>
|
||||
<TimeAgoFormatter>{item.updated_at}</TimeAgoFormatter>
|
||||
</span>
|
||||
|
||||
@@ -5,7 +5,7 @@ import { createStructuredSelector } from "reselect";
|
||||
import { selectSelectedJobTargetPc } from "../../../redux/application/application.selectors";
|
||||
import {
|
||||
CalculateJobRpsDollars,
|
||||
CalculateJobRpsPc
|
||||
CalculateJobRpsPc,
|
||||
} from "../../../util/CalculateJobRps";
|
||||
import ErrorResultAtom from "../../atoms/error-result/error-result.atom";
|
||||
|
||||
@@ -38,7 +38,13 @@ export function JobsTargetsStatsMolecule({
|
||||
);
|
||||
|
||||
if (loading) return <Skeleton active />;
|
||||
if (!job) return <ErrorResultAtom title="Error displaying job data." />;
|
||||
if (!job)
|
||||
return (
|
||||
<ErrorResultAtom
|
||||
title="Error displaying job totals."
|
||||
errorMessage="It looks like this job doesn't exist."
|
||||
/>
|
||||
);
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
@@ -71,7 +77,7 @@ export function JobsTargetsStatsMolecule({
|
||||
<Statistic
|
||||
title="Target RPS $"
|
||||
style={{ margin: "0rem .5rem" }}
|
||||
value={actPriceSum.percentage(selectedJobTargetPc * 100).toFormat()}
|
||||
value={dbPriceSum.percentage(selectedJobTargetPc * 100).toFormat()}
|
||||
/>
|
||||
<Statistic
|
||||
title="Current RPS $"
|
||||
|
||||
@@ -9,6 +9,7 @@ import { setSelectedJobId } from "../../../redux/application/application.actions
|
||||
import {
|
||||
selectReportData,
|
||||
selectReportLoading,
|
||||
selectScorecard,
|
||||
} from "../../../redux/reporting/reporting.selectors";
|
||||
|
||||
const { ipcRenderer } = window;
|
||||
@@ -16,12 +17,14 @@ const { ipcRenderer } = window;
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
reportingLoading: selectReportLoading,
|
||||
reportData: selectReportData,
|
||||
scoreCard: selectScorecard,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setSelectedJobId: (id) => dispatch(setSelectedJobId(id)),
|
||||
});
|
||||
|
||||
export function ReportingJobsListMolecule({
|
||||
scoreCard,
|
||||
reportingLoading,
|
||||
reportData,
|
||||
setSelectedJobId,
|
||||
@@ -147,6 +150,24 @@ export function ReportingJobsListMolecule({
|
||||
scroll={{
|
||||
x: true,
|
||||
}}
|
||||
summary={() => (
|
||||
<Table.Summary.Row>
|
||||
<Table.Summary.Cell index={0}></Table.Summary.Cell>
|
||||
<Table.Summary.Cell index={1}></Table.Summary.Cell>
|
||||
<Table.Summary.Cell index={2}></Table.Summary.Cell>
|
||||
<Table.Summary.Cell index={3}></Table.Summary.Cell>
|
||||
<Table.Summary.Cell index={4}></Table.Summary.Cell>
|
||||
<Table.Summary.Cell index={5}>
|
||||
{scoreCard && scoreCard.allJobsSumDbPrice.toFormat()}
|
||||
</Table.Summary.Cell>
|
||||
<Table.Summary.Cell index={6}>
|
||||
{" "}
|
||||
{scoreCard && scoreCard.allJobsSumActPrice.toFormat()}
|
||||
</Table.Summary.Cell>
|
||||
<Table.Summary.Cell index={7}></Table.Summary.Cell>
|
||||
<Table.Summary.Cell index={8}></Table.Summary.Cell>
|
||||
</Table.Summary.Row>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -35,38 +35,47 @@ export function ReportingTotalsStatsMolecule({ reportingLoading, scoreCard }) {
|
||||
marginBottom: "1rem",
|
||||
}}
|
||||
>
|
||||
<Statistic
|
||||
title="RPS Total"
|
||||
value={scoreCard.shopRpsTotalDollars.toFormat()}
|
||||
/>
|
||||
<Statistic
|
||||
title="RPS Expectation"
|
||||
value={scoreCard.shopRpsExpectedDollars.toFormat()}
|
||||
/>
|
||||
<Statistic
|
||||
title="RPS Variance $"
|
||||
valueStyle={{
|
||||
color:
|
||||
scoreCard.varianceDollars.getAmount() < 0 ? "tomato" : "seagreen",
|
||||
}}
|
||||
value={scoreCard.varianceDollars.toFormat()}
|
||||
/>
|
||||
<Statistic
|
||||
title="Current RPS %"
|
||||
valueStyle={{
|
||||
color:
|
||||
(scoreCard.currentRpsPc || 0) <= (scoreCard.targetRpsPc || 0)
|
||||
? "tomato"
|
||||
: "seagreen",
|
||||
}}
|
||||
value={((scoreCard.currentRpsPc || 0) * 100).toFixed(1)}
|
||||
suffix="%"
|
||||
/>
|
||||
<Statistic
|
||||
title="Target RPS %"
|
||||
value={((scoreCard.targetRpsPc || 0) * 100).toFixed(1)}
|
||||
suffix="%"
|
||||
/>
|
||||
<div style={{ display: "flex" }}>
|
||||
<Statistic
|
||||
title="Target RPS %"
|
||||
style={{ margin: "0rem .5rem" }}
|
||||
value={((scoreCard.targetRpsPc || 0) * 100).toFixed(1)}
|
||||
suffix="%"
|
||||
/>
|
||||
<Statistic
|
||||
title="Current RPS %"
|
||||
style={{ margin: "0rem .5rem" }}
|
||||
valueStyle={{
|
||||
color:
|
||||
(scoreCard.currentRpsPc || 0) <= (scoreCard.targetRpsPc || 0)
|
||||
? "tomato"
|
||||
: "seagreen",
|
||||
}}
|
||||
value={((scoreCard.currentRpsPc || 0) * 100).toFixed(1)}
|
||||
suffix="%"
|
||||
/>
|
||||
</div>
|
||||
<div style={{ display: "flex" }}>
|
||||
<Statistic
|
||||
title="Target RPS $"
|
||||
style={{ margin: "0rem .5rem" }}
|
||||
value={scoreCard.shopRpsExpectedDollars.toFormat()}
|
||||
/>
|
||||
<Statistic
|
||||
title="Current RPS $"
|
||||
style={{ margin: "0rem .5rem" }}
|
||||
value={scoreCard.shopRpsTotalDollars.toFormat()}
|
||||
/>
|
||||
<Statistic
|
||||
title="RPS Variance $"
|
||||
style={{ margin: "0rem .5rem" }}
|
||||
valueStyle={{
|
||||
color:
|
||||
scoreCard.varianceDollars.getAmount() < 0 ? "tomato" : "seagreen",
|
||||
}}
|
||||
value={scoreCard.varianceDollars.toFormat()}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -35,18 +35,20 @@ export function ScanEstimateListMolecule({ scanLoading, estimates }) {
|
||||
key: "ins_co_nm",
|
||||
sorter: (a, b) => alphaSort(a.ins_co_nm, b.ins_co_nm),
|
||||
},
|
||||
{
|
||||
title: "Last Name",
|
||||
dataIndex: "ownr_ln",
|
||||
key: "ownr_ln",
|
||||
defaultSortOrder: "ascend",
|
||||
sorter: (a, b) => alphaSort(a.ownr_ln, b.ownr_ln),
|
||||
},
|
||||
{
|
||||
title: "First Name",
|
||||
dataIndex: "ownr_fn",
|
||||
key: "ownr_fn",
|
||||
sorter: (a, b) => alphaSort(a.ownr_fn, b.ownr_fn),
|
||||
},
|
||||
{
|
||||
title: "Last Name",
|
||||
dataIndex: "ownr_ln",
|
||||
key: "ownr_ln",
|
||||
sorter: (a, b) => alphaSort(a.ownr_ln, b.ownr_ln),
|
||||
},
|
||||
|
||||
{
|
||||
title: "Vehicle",
|
||||
dataIndex: "vehicle",
|
||||
|
||||
@@ -29,10 +29,10 @@ export function JobsDetailOrganism({ selectedJobId, setSelectedJobTargetPc }) {
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (data)
|
||||
if (data && data.jobs_by_pk)
|
||||
setSelectedJobTargetPc({
|
||||
group: data.jobs_by_pk.group,
|
||||
v_age: data.jobs_by_pk.v_age,
|
||||
group: data.jobs_by_pk && data.jobs_by_pk.group,
|
||||
v_age: data.jobs_by_pk && data.jobs_by_pk.v_age,
|
||||
});
|
||||
}, [data, setSelectedJobTargetPc]);
|
||||
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
import { AlertFilled } from "@ant-design/icons";
|
||||
import { Button, Layout, Progress } from "antd";
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import ipcTypes from "../../../ipc.types";
|
||||
import {
|
||||
selectUpdateAvailable,
|
||||
selectUpdateProgress,
|
||||
} from "../../../redux/application/application.selectors";
|
||||
const { ipcRenderer } = window;
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
//currentUser: selectCurrentUser
|
||||
updateAvailable: selectUpdateAvailable,
|
||||
updateProgress: selectUpdateProgress,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||
});
|
||||
|
||||
export function UpdateManagerOrganism({ updateAvailable, updateProgress }) {
|
||||
if (!updateAvailable) return null;
|
||||
return (
|
||||
<Layout.Footer>
|
||||
{updateAvailable && !updateProgress && (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
<AlertFilled
|
||||
style={{ marginRight: ".5rem", color: "tomato" }}
|
||||
className="blink_me"
|
||||
/>
|
||||
<span>{`An update to ImEX RPS is available. (Version ${updateAvailable.version})`}</span>
|
||||
</div>
|
||||
<Button
|
||||
type="primary"
|
||||
style={{ margin: "0rem .5rem" }}
|
||||
onClick={() =>
|
||||
ipcRenderer.send(ipcTypes.default.app.toMain.downloadUpdates)
|
||||
}
|
||||
>
|
||||
Download
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
{updateAvailable && updateProgress && (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<Progress
|
||||
style={{ flex: 1, margin: "0rem .5rem" }}
|
||||
status={updateProgress.percent === 100 ? "success" : "active"}
|
||||
percent={updateProgress.percent.toFixed(1)}
|
||||
/>
|
||||
{updateProgress.percent === 100 ? (
|
||||
<div tyle={{ margin: "0rem .5rem" }}>
|
||||
<span
|
||||
style={{ margin: "0rem .5rem" }}
|
||||
>{`Updated downloaded.`}</span>
|
||||
<Button
|
||||
type="primary"
|
||||
style={{ margin: "0rem .5rem" }}
|
||||
onClick={() =>
|
||||
ipcRenderer.send(ipcTypes.default.app.toMain.installUpdates)
|
||||
}
|
||||
>
|
||||
Install
|
||||
</Button>
|
||||
</div>
|
||||
) : (
|
||||
<span
|
||||
style={{ margin: "0rem .5rem" }}
|
||||
>{`Downloading update at ${formatBytes(
|
||||
updateProgress.bytesPerSecond
|
||||
)})`}</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</Layout.Footer>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(UpdateManagerOrganism);
|
||||
|
||||
// download Info
|
||||
// {
|
||||
// // bytesPerSecond: 6633258;
|
||||
// // delta: 2479242;
|
||||
// // percent: 100;
|
||||
// // total: 95651575;
|
||||
// // transferred: 95651575;
|
||||
// }
|
||||
|
||||
function formatBytes(bytes) {
|
||||
var marker = 1024; // Change to 1000 if required
|
||||
var decimal = 1; // Change as required
|
||||
var kiloBytes = marker; // One Kilobyte is 1024 bytes
|
||||
var megaBytes = marker * marker; // One MB is 1024 KB
|
||||
var gigaBytes = marker * marker * marker; // One GB is 1024 MB
|
||||
//var teraBytes = marker * marker * marker * marker; // One TB is 1024 GB
|
||||
|
||||
// return bytes if less than a KB
|
||||
if (bytes < kiloBytes) return bytes + " Bytes/sec";
|
||||
// return KB if less than a MB
|
||||
else if (bytes < megaBytes)
|
||||
return (bytes / kiloBytes).toFixed(decimal) + " KB/sec";
|
||||
// return MB if less than a GB
|
||||
else if (bytes < gigaBytes)
|
||||
return (bytes / megaBytes).toFixed(decimal) + " MB/sec";
|
||||
// return GB if less than a TB
|
||||
else return (bytes / gigaBytes).toFixed(decimal) + " GB/sec";
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import { createStructuredSelector } from "reselect";
|
||||
import { selectBodyshop } from "../../../redux/user/user.selectors";
|
||||
import ErrorResultAtom from "../../atoms/error-result/error-result.atom";
|
||||
import SiderMenuOrganism from "../../organisms/sider-menu/sider-menu.organism";
|
||||
import UpdateManagerOrganism from "../../organisms/update-manager/update-manager.organism";
|
||||
import JobsPage from "../jobs/jobs.page";
|
||||
import ReportingPage from "../reporting/reporting.page";
|
||||
import ScanPage from "../scan/scan.page";
|
||||
@@ -39,6 +40,8 @@ export function RoutesPage({ bodyshop }) {
|
||||
<Route exact path="/scan" component={ScanPage} />
|
||||
<Route exact path="/" component={JobsPage} />
|
||||
</Layout.Content>
|
||||
|
||||
<UpdateManagerOrganism />
|
||||
</Layout>
|
||||
</Layout>
|
||||
);
|
||||
|
||||
@@ -26,6 +26,7 @@ export const QUERY_ALL_JOBS_PAGINATED = gql`
|
||||
id
|
||||
ins_co_nm
|
||||
clm_no
|
||||
close_date
|
||||
updated_at
|
||||
}
|
||||
jobs_aggregate {
|
||||
|
||||
@@ -9,6 +9,14 @@ exports.default = {
|
||||
setAcceptableInsCoNm: "setAcceptableInsCoNm",
|
||||
setUserName: "setUserName",
|
||||
track: "analytics_track",
|
||||
checkForUpdates: "app_checkForUpdates",
|
||||
downloadUpdates: "app_downloadUpdates",
|
||||
installUpdates: "app_installupdates",
|
||||
},
|
||||
toRenderer: {
|
||||
updateAvailable: "app_updateAvailable",
|
||||
downloadProgress: "app_downloadProgress",
|
||||
signOut: "app_signOut",
|
||||
},
|
||||
},
|
||||
store: {
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
import ipcTypes from "../ipc.types";
|
||||
import {
|
||||
setSettings,
|
||||
setUpdateAvailable,
|
||||
setUpdateProgress,
|
||||
setWatchedPaths,
|
||||
setWatcherStatus,
|
||||
} from "../redux/application/application.actions";
|
||||
import { store } from "../redux/store";
|
||||
import { UpsertEstimate } from "./ipc-estimate-utils";
|
||||
import { setScanEstimateList } from "../redux/scan/scan.actions";
|
||||
import { signOutStart } from "../redux/user/user.actions";
|
||||
const { ipcRenderer } = window;
|
||||
|
||||
console.log("----Initializing IPC Listeners in React App.");
|
||||
@@ -65,3 +68,24 @@ ipcRenderer.on(
|
||||
store.dispatch(setScanEstimateList(listOfEstimates));
|
||||
}
|
||||
);
|
||||
|
||||
ipcRenderer.on(
|
||||
ipcTypes.default.app.toRenderer.updateAvailable,
|
||||
async (event, updateInfo) => {
|
||||
store.dispatch(setUpdateAvailable(updateInfo));
|
||||
}
|
||||
);
|
||||
|
||||
ipcRenderer.on(
|
||||
ipcTypes.default.app.toRenderer.downloadProgress,
|
||||
async (event, progress) => {
|
||||
store.dispatch(setUpdateProgress(progress));
|
||||
}
|
||||
);
|
||||
|
||||
ipcRenderer.on(
|
||||
ipcTypes.default.app.toRenderer.signOut,
|
||||
async (event, progress) => {
|
||||
store.dispatch(signOutStart());
|
||||
}
|
||||
);
|
||||
|
||||
@@ -48,3 +48,7 @@ export const setUpdateAvailable = (available) => ({
|
||||
type: ApplicationActionTypes.SET_UPDATE_AVAILABLE,
|
||||
payload: available,
|
||||
});
|
||||
export const setUpdateProgress = (progress) => ({
|
||||
type: ApplicationActionTypes.SET_UPDATE_PROGRESS,
|
||||
payload: progress,
|
||||
});
|
||||
|
||||
@@ -8,6 +8,7 @@ const INITIAL_STATE = {
|
||||
selectedJobTargetPc: 0,
|
||||
settings: {},
|
||||
updateAvailable: false,
|
||||
updateProgress: null,
|
||||
};
|
||||
|
||||
const { ipcRenderer } = window;
|
||||
@@ -63,6 +64,8 @@ const applicationReducer = (state = INITIAL_STATE, action) => {
|
||||
return { ...state, settings: { ...state.settings, ...action.payload } };
|
||||
case ApplicationActionTypes.SET_UPDATE_AVAILABLE:
|
||||
return { ...state, updateAvailable: action.payload };
|
||||
case ApplicationActionTypes.SET_UPDATE_PROGRESS:
|
||||
return { ...state, updateProgress: action.payload };
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
||||
@@ -36,3 +36,7 @@ export const selectUpdateAvailable = createSelector(
|
||||
[selectApplication],
|
||||
(application) => application.updateAvailable
|
||||
);
|
||||
export const selectUpdateProgress = createSelector(
|
||||
[selectApplication],
|
||||
(application) => application.updateProgress
|
||||
);
|
||||
|
||||
@@ -9,5 +9,7 @@ const ApplicationActionTypes = {
|
||||
SET_SELECTED_JOB_TARGET_PC_SUCCESS: "SET_SELECTED_JOB_TARGET_PC_SUCCESS",
|
||||
SET_SETTINGS: "SET_SETTINGS",
|
||||
SET_UPDATE_AVAILABLE: "SET_UPDATE_AVAILABLE",
|
||||
SET_UPDATE_PROGRESS: "SET_UPDATE_PROGRESS",
|
||||
|
||||
};
|
||||
export default ApplicationActionTypes;
|
||||
|
||||
@@ -128,6 +128,7 @@ export function* signInSuccessSaga({ payload }) {
|
||||
LogRocket.identify(payload.email, {
|
||||
email: payload.email,
|
||||
});
|
||||
ipcRenderer.send(ipcTypes.default.app.toMain.checkForUpdates);
|
||||
|
||||
ipcRenderer.send(ipcTypes.default.app.toMain.track, {
|
||||
event: "SIGN_IN_SUCCESS",
|
||||
|
||||
Reference in New Issue
Block a user