@@ -1,3 +1,3 @@
|
||||
REACT_APP_FIREBASE_CONFIG={ "apiKey": "AIzaSyCXg148Ma82Qa7dK-2EL4sE0tJhKVnh1rY", "authDomain": "rps-prod-b53c8.firebaseapp.com", "databaseURL": "https://rps-prod-b53c8.firebaseio.com", "projectId": "rps-prod-b53c8", "storageBucket": "rps-prod-b53c8.appspot.com", "messagingSenderId": "361220226954", "appId": "1:361220226954:web:bf3a38d196e4fd8c921273", "measurementId": "G-W3BHH420EC"}
|
||||
REACT_APP_GRAPHQL_ENDPOINT=https://rps.bodyshop.app/v1/graphql
|
||||
REACT_APP_GRAPHQL_ENDPOINT_WS=wss://rps.bodyshop.app/v1/graphql
|
||||
REACT_APP_GRAPHQL_ENDPOINT=https://db.rps.imex.online/v1/graphql
|
||||
REACT_APP_GRAPHQL_ENDPOINT_WS=wss://db.rps.imex.online/v1/graphql
|
||||
@@ -4,7 +4,10 @@ const log = require("electron-log");
|
||||
const Nucleus = require("nucleus-nodejs");
|
||||
const { default: ipcTypes } = require("../src/ipc.types");
|
||||
|
||||
Nucleus.init("5f91b569b95bac34eefdb63a", { debug: true });
|
||||
Nucleus.init("5f91b569b95bac34eefdb63a", {
|
||||
disableInDev: true,
|
||||
debug: false,
|
||||
});
|
||||
|
||||
Nucleus.setProps({
|
||||
version: app.getVersion().toString(),
|
||||
|
||||
@@ -5,6 +5,7 @@ const store = new Store({
|
||||
enableNotifications: true,
|
||||
filePaths: [],
|
||||
accepted_ins_co: [],
|
||||
runWatcherOnStartup: true,
|
||||
polling: {
|
||||
enabled: false,
|
||||
pollingInterval: 1000,
|
||||
|
||||
@@ -2,7 +2,7 @@ const { ipcMain } = require("electron");
|
||||
const Nucleus = require("nucleus-nodejs");
|
||||
const ipcTypes = require("../../src/ipc.types");
|
||||
const { ImportJob } = require("../decoder/decoder");
|
||||
const { GetListOfEstimates } = require("./file-scan");
|
||||
const { GetListOfEstimates, DeleteAllEms } = require("./file-scan");
|
||||
|
||||
ipcMain.on(
|
||||
ipcTypes.default.fileScan.toMain.scanFilePaths,
|
||||
@@ -22,3 +22,16 @@ ipcMain.on(
|
||||
await ImportJob(filePath);
|
||||
}
|
||||
);
|
||||
|
||||
ipcMain.on(
|
||||
ipcTypes.default.fileScan.toMain.deleteAllEms,
|
||||
async (event, filePath) => {
|
||||
Nucleus.track("DELETE_ALLEMS");
|
||||
await DeleteAllEms();
|
||||
const ret = await GetListOfEstimates();
|
||||
event.reply(
|
||||
ipcTypes.default.fileScan.toRenderer.scanFilePathsResponse,
|
||||
ret
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
@@ -6,6 +6,7 @@ const fsPromises = fs.promises;
|
||||
const _ = require("lodash");
|
||||
const { DecodeEstimate } = require("../decoder/decoder");
|
||||
const Nucleus = require("nucleus-nodejs");
|
||||
const { format } = require("path");
|
||||
|
||||
async function GetListOfEstimates() {
|
||||
Nucleus.track("SCAN_ALL_ESTIMATES");
|
||||
@@ -42,4 +43,32 @@ async function GetEnvFiles() {
|
||||
return allFilePaths;
|
||||
}
|
||||
|
||||
async function DeleteAllEms() {
|
||||
try {
|
||||
const filePaths = store.get("filePaths");
|
||||
const allFilePaths = [];
|
||||
await Promise.all(
|
||||
filePaths.map(async (fp) => {
|
||||
const allFilesinDir = await fsPromises.readdir(fp);
|
||||
|
||||
allFilesinDir.map((envFileName) => {
|
||||
allFilePaths.push(path.join(fp, envFileName));
|
||||
return null;
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
await Promise.all(
|
||||
allFilePaths.map(async (file) => {
|
||||
await fsPromises.unlink(file);
|
||||
})
|
||||
);
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
exports.GetListOfEstimates = GetListOfEstimates;
|
||||
exports.DeleteAllEms = DeleteAllEms
|
||||
@@ -17,7 +17,10 @@ ipcMain.on(
|
||||
);
|
||||
|
||||
ipcMain.on(ipcTypes.default.fileWatcher.toMain.start, async (event, arg) => {
|
||||
StartWatcher();
|
||||
if ((arg && arg.startup && store.get("runWatcherOnStartup")) || !arg) {
|
||||
StartWatcher();
|
||||
}
|
||||
|
||||
// event.sender.send(ipcTypes.default.fileWatcher.toRenderer.startSuccess);
|
||||
event.sender.send(
|
||||
ipcTypes.default.fileWatcher.toRenderer.filepathsList,
|
||||
|
||||
@@ -94,14 +94,16 @@ function onWatcherReady() {
|
||||
}
|
||||
|
||||
async function StopWatcher() {
|
||||
await watcher.close();
|
||||
log.info("Watcher stopped.");
|
||||
const b = BrowserWindow.getAllWindows()[0];
|
||||
b.webContents.send(ipcTypes.default.fileWatcher.toRenderer.stopSuccess);
|
||||
NewNotification({
|
||||
title: "RPS Watcher Stopped",
|
||||
body: "Estimates will not be automatically uploaded.",
|
||||
});
|
||||
if (watcher) {
|
||||
await watcher.close();
|
||||
log.info("Watcher stopped.");
|
||||
const b = BrowserWindow.getAllWindows()[0];
|
||||
b.webContents.send(ipcTypes.default.fileWatcher.toRenderer.stopSuccess);
|
||||
NewNotification({
|
||||
title: "RPS Watcher Stopped",
|
||||
body: "Estimates will not be automatically uploaded.",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
exports.StartWatcher = StartWatcher;
|
||||
|
||||
23006
electron/licenses.txt
Normal file
23006
electron/licenses.txt
Normal file
File diff suppressed because one or more lines are too long
147
electron/main.js
147
electron/main.js
@@ -70,7 +70,10 @@ var menu = Menu.buildFromTemplate([
|
||||
{
|
||||
label: `Check for Updates (currently ${app.getVersion()})`,
|
||||
click() {
|
||||
autoUpdater.checkForUpdatesAndNotify();
|
||||
autoUpdater.checkForUpdatesAndNotify({
|
||||
title: "ImEX RPS Update Downloaded",
|
||||
body: "Restart ImEX RPS to install.",
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -85,14 +88,20 @@ var menu = Menu.buildFromTemplate([
|
||||
shell.openPath(path.join(app.getPath("appData"), "ImeX RPS\\logs"));
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Third Party Notices",
|
||||
click() {
|
||||
openNoticeWindow();
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
let mainWindow;
|
||||
let noticeWindow;
|
||||
let tray = null;
|
||||
function createWindow() {
|
||||
makeSingleInstance();
|
||||
// Create the browser window.
|
||||
Menu.setApplicationMenu(menu);
|
||||
mainWindow = new BrowserWindow({
|
||||
@@ -109,44 +118,62 @@ function createWindow() {
|
||||
preload: path.join(__dirname, "preload.js"), // use a preload script
|
||||
},
|
||||
});
|
||||
// and load the index.html of the app.
|
||||
// win.loadFile("index.html");
|
||||
mainWindow.loadURL(
|
||||
isDev
|
||||
? "http://localhost:3000"
|
||||
: `file://${path.join(__dirname, "/../build/index.html")}`
|
||||
);
|
||||
|
||||
// mainWindow.on("close", function (event) {
|
||||
// event.preventDefault();
|
||||
// mainWindow.hide();
|
||||
// tray = createTray();
|
||||
// });
|
||||
|
||||
mainWindow.on("minimize", function (event) {
|
||||
event.preventDefault();
|
||||
mainWindow.hide();
|
||||
tray = createTray();
|
||||
});
|
||||
|
||||
ipcMain.on(ipcTypes.quit, (event, arg) => {
|
||||
app.isQuiting = true;
|
||||
const gotTheLock = app.requestSingleInstanceLock();
|
||||
if (!gotTheLock) {
|
||||
app.quit();
|
||||
});
|
||||
} else {
|
||||
app.on("second-instance", (event, commandLine, workingDirectory) => {
|
||||
// Someone tried to run a second instance, we should focus our window.
|
||||
if (mainWindow) {
|
||||
if (mainWindow.isMinimized()) mainWindow.restore();
|
||||
mainWindow.focus();
|
||||
}
|
||||
});
|
||||
|
||||
// Open the DevTools.
|
||||
if (isDev) {
|
||||
mainWindow.webContents.openDevTools({
|
||||
// mode: "detach"
|
||||
// and load the index.html of the app.
|
||||
// win.loadFile("index.html");
|
||||
mainWindow.loadURL(
|
||||
isDev
|
||||
? "http://localhost:3000"
|
||||
: `file://${path.join(__dirname, "/../build/index.html")}`
|
||||
);
|
||||
|
||||
// mainWindow.on("close", function (event) {
|
||||
// event.preventDefault();
|
||||
// mainWindow.hide();
|
||||
// if (!tray) {
|
||||
// tray = createTray();
|
||||
// }
|
||||
// });
|
||||
|
||||
mainWindow.on("minimize", function (event) {
|
||||
event.preventDefault();
|
||||
mainWindow.hide();
|
||||
if (!tray) {
|
||||
tray = createTray();
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on(ipcTypes.quit, (event, arg) => {
|
||||
app.isQuiting = true;
|
||||
app.quit();
|
||||
});
|
||||
|
||||
// Open the DevTools.
|
||||
if (isDev) {
|
||||
mainWindow.webContents.openDevTools({
|
||||
// mode: "detach"
|
||||
});
|
||||
}
|
||||
|
||||
mainWindow.maximize();
|
||||
autoUpdater.checkForUpdatesAndNotify();
|
||||
|
||||
globalShortcut.register("CommandOrControl+Shift+I", () => {
|
||||
mainWindow.webContents.toggleDevTools();
|
||||
});
|
||||
}
|
||||
|
||||
mainWindow.maximize();
|
||||
autoUpdater.checkForUpdatesAndNotify();
|
||||
|
||||
globalShortcut.register("CommandOrControl+Shift+I", () => {
|
||||
mainWindow.webContents.toggleDevTools();
|
||||
});
|
||||
}
|
||||
|
||||
exports.mainWindow = mainWindow;
|
||||
@@ -189,21 +216,6 @@ app.on("activate", () => {
|
||||
}
|
||||
});
|
||||
|
||||
// In this file you can include the rest of your app's specific main process
|
||||
// code. You can also put them in separate files and require them here.
|
||||
function makeSingleInstance() {
|
||||
if (process.mas) return;
|
||||
|
||||
app.requestSingleInstanceLock();
|
||||
|
||||
app.on("second-instance", () => {
|
||||
if (mainWindow) {
|
||||
if (mainWindow.isMinimized()) mainWindow.restore();
|
||||
mainWindow.focus();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function createTray() {
|
||||
let appIcon = new Tray(path.join(__dirname, "../src/assets/logo192.png"));
|
||||
const contextMenu = Menu.buildFromTemplate([
|
||||
@@ -231,23 +243,23 @@ function createTray() {
|
||||
}
|
||||
|
||||
autoUpdater.on("checking-for-update", () => {
|
||||
console.log("Checking for update...");
|
||||
log.log("Checking for update...");
|
||||
});
|
||||
autoUpdater.on("update-available", (ev, info) => {
|
||||
console.log("Update available.");
|
||||
log.log("Update available.");
|
||||
});
|
||||
autoUpdater.on("update-not-available", (ev, info) => {
|
||||
console.log("Update not available.");
|
||||
log.log("Update not available.");
|
||||
});
|
||||
autoUpdater.on("error", (ev, err) => {
|
||||
console.log("Error in auto-updater.");
|
||||
log.log("Error in auto-updater.");
|
||||
});
|
||||
autoUpdater.on("download-progress", (ev, progressObj) => {
|
||||
console.log("Download progress...");
|
||||
});
|
||||
autoUpdater.on("update-downloaded", (ev, info) => {
|
||||
console.log("Update downloaded; will install in 5 seconds");
|
||||
log.log("Download progress...");
|
||||
});
|
||||
// autoUpdater.on("update-downloaded", (ev, info) => {
|
||||
// console.log("Update downloaded; will install in 5 seconds");
|
||||
// });
|
||||
autoUpdater.on("update-downloaded", (ev, info) => {
|
||||
Nucleus.track("UPDATE_DOWNLOADED", info);
|
||||
if (process.env.NODE_ENV === "production") {
|
||||
@@ -270,3 +282,22 @@ autoUpdater.on("update-downloaded", (ev, info) => {
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
function openNoticeWindow() {
|
||||
if (noticeWindow) {
|
||||
noticeWindow.focus();
|
||||
return;
|
||||
}
|
||||
|
||||
noticeWindow = new BrowserWindow({
|
||||
height: 600,
|
||||
width: 800,
|
||||
title: "ImEX RPS - Third Party Notices",
|
||||
});
|
||||
|
||||
noticeWindow.loadURL("file://" + __dirname + "/licenses.txt");
|
||||
|
||||
noticeWindow.on("closed", function () {
|
||||
noticeWindow = null;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
[]
|
||||
@@ -0,0 +1,6 @@
|
||||
- args:
|
||||
cascade: true
|
||||
read_only: false
|
||||
sql: "create trigger calculate_updated_job_line_insert\r\nbefore\r\ninsert\r\n
|
||||
\ on\r\n public.joblines for each row execute function calculate_job_line();"
|
||||
type: run_sql
|
||||
7867
package-lock.json
generated
7867
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
20
package.json
20
package.json
@@ -3,13 +3,12 @@
|
||||
"productName": "ImEX RPS",
|
||||
"author": "ImEX Systems Inc. <support@thinkimex.com>",
|
||||
"description": "ImEX RPS",
|
||||
"version": "1.0.3",
|
||||
"version": "1.0.4",
|
||||
"main": "electron/main.js",
|
||||
"homepage": "./",
|
||||
"dependencies": {
|
||||
"@apollo/client": "^3.2.4",
|
||||
"@fingerprintjs/fingerprintjs": "^2.1.4",
|
||||
"antd": "^4.7.2",
|
||||
"antd": "^4.7.3",
|
||||
"apollo-link-logger": "^2.0.0",
|
||||
"chokidar": "^3.4.2",
|
||||
"dbffile": "^1.4.3",
|
||||
@@ -21,20 +20,19 @@
|
||||
"electron-store": "^6.0.1",
|
||||
"electron-updater": "^4.3.5",
|
||||
"firebase": "^7.24.0",
|
||||
"graphql": "^15.3.0",
|
||||
"graphql": "^15.4.0",
|
||||
"lodash": "^4.17.20",
|
||||
"logrocket": "^1.0.14",
|
||||
"moment": "^2.29.1",
|
||||
"node-notifier": "^8.0.0",
|
||||
"node-sass": "^4.14.1",
|
||||
"nucleus-nodejs": "^3.0.6",
|
||||
"query-string": "^6.13.6",
|
||||
"react": "^16.14.0",
|
||||
"react-dom": "^16.14.0",
|
||||
"react": "^17.0.1",
|
||||
"react-dom": "^17.0.1",
|
||||
"react-infinite-scroller": "^1.2.4",
|
||||
"react-redux": "^7.2.1",
|
||||
"react-router-dom": "^5.2.0",
|
||||
"react-scripts": "3.4.3",
|
||||
"react-scripts": "4.0.0",
|
||||
"recharts": "^1.8.5",
|
||||
"redux": "^4.0.5",
|
||||
"redux-logger": "^3.0.6",
|
||||
@@ -53,8 +51,8 @@
|
||||
"pack": "electron-builder --dir",
|
||||
"dist": "npm run build && electron-builder",
|
||||
"distp": "npm run build && electron-builder --publish always",
|
||||
"distpnb": "npm run build && electron-builder --publish always",
|
||||
"postinstall": "npm run build && electron-builder install-app-deps"
|
||||
"distpnb": "lectron-builder --publish always",
|
||||
"postinstall": "electron-builder install-app-deps"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": "react-app"
|
||||
@@ -73,7 +71,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"concurrently": "^5.3.0",
|
||||
"electron": "^10.1.3",
|
||||
"electron": "^10.1.5",
|
||||
"electron-builder": "^22.9.1",
|
||||
"electron-devtools-installer": "^3.1.1",
|
||||
"enzyme": "^3.11.0",
|
||||
|
||||
@@ -47,23 +47,23 @@ body {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
::-webkit-scrollbar-track {
|
||||
-webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
|
||||
border-radius: 0.2rem;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
// ::-webkit-scrollbar-track {
|
||||
// -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
|
||||
// border-radius: 0.2rem;
|
||||
// background-color: #f5f5f5;
|
||||
// }
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 0.25rem;
|
||||
max-height: 0.25rem;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
// ::-webkit-scrollbar {
|
||||
// width: 0.25rem;
|
||||
// max-height: 0.25rem;
|
||||
// background-color: #f5f5f5;
|
||||
// }
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
border-radius: 0.2rem;
|
||||
-webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
|
||||
background-color: #188fff;
|
||||
}
|
||||
// ::-webkit-scrollbar-thumb {
|
||||
// border-radius: 0.2rem;
|
||||
// -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
|
||||
// background-color: #188fff;
|
||||
// }
|
||||
.jobs-list-container {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
17
src/components/atoms/delete-all-ems/delete-all-ems.atom.jsx
Normal file
17
src/components/atoms/delete-all-ems/delete-all-ems.atom.jsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import { Button, Popconfirm } from "antd";
|
||||
import React from "react";
|
||||
import ipcTypes from "../../../ipc.types";
|
||||
|
||||
const { ipcRenderer } = window;
|
||||
export default function DeleteAllEmsAtom() {
|
||||
return (
|
||||
<Popconfirm
|
||||
title="Are you sure you want to delete all EMS files? This cannot be undone."
|
||||
onConfirm={() =>
|
||||
ipcRenderer.send(ipcTypes.default.fileScan.toMain.deleteAllEms)
|
||||
}
|
||||
>
|
||||
<Button>Delete All EMS</Button>
|
||||
</Popconfirm>
|
||||
);
|
||||
}
|
||||
@@ -43,7 +43,7 @@ export function DeleteJobAtom({ setSelectedJobId, jobId }) {
|
||||
onConfirm={handleDelete}
|
||||
>
|
||||
<Button loading={loading}>
|
||||
<DeleteFilled />
|
||||
<DeleteFilled /> <span>Delete</span>
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
</div>
|
||||
|
||||
@@ -1,11 +1,19 @@
|
||||
import { Button, Result } from "antd";
|
||||
import React from "react";
|
||||
import ipcTypes from "../../../ipc.types";
|
||||
|
||||
const { ipcRenderer } = window;
|
||||
export default function ErrorResultAtom({
|
||||
title,
|
||||
errorMessage,
|
||||
tryAgainCallback,
|
||||
}) {
|
||||
ipcRenderer.send(ipcTypes.default.app.toMain.track, {
|
||||
event: "ERROR_RESULT_ATOM_DISPLAYED",
|
||||
title,
|
||||
errorMessage,
|
||||
});
|
||||
|
||||
return (
|
||||
<Result
|
||||
status="500"
|
||||
|
||||
@@ -9,15 +9,16 @@ export default function IgnoreJobLineAtom({ ignore, lineId, line_desc }) {
|
||||
const [updateJobLine] = useMutation(UPDATE_JOB_LINE);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const handleChange = async (checked) => {
|
||||
console.log("handleChange -> checked", checked);
|
||||
setLoading(true);
|
||||
ipcRenderer.send(ipcTypes.default.app.toMain.track, {
|
||||
event: "TOGGLE_IGNORE_LINE",
|
||||
line_desc: line_desc,
|
||||
ignore: checked,
|
||||
ignore: !checked,
|
||||
});
|
||||
|
||||
const result = await updateJobLine({
|
||||
variables: { lineId: lineId, line: { ignore: checked } },
|
||||
variables: { lineId: lineId, line: { ignore: !checked } },
|
||||
});
|
||||
if (result.errors) {
|
||||
message.error("Error updating line.");
|
||||
@@ -27,5 +28,5 @@ export default function IgnoreJobLineAtom({ ignore, lineId, line_desc }) {
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
return <Switch checked={ignore} onChange={handleChange} loading={loading} />;
|
||||
return <Switch checked={!ignore} onChange={handleChange} loading={loading} />;
|
||||
}
|
||||
|
||||
@@ -48,9 +48,6 @@ export default function JobPartsGraphAtom({
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<Typography.Title level={4}>
|
||||
{price === "act_price" ? "Actual Price" : "Database Price"}
|
||||
</Typography.Title>
|
||||
<ResponsiveContainer>
|
||||
<PieChart>
|
||||
<Pie
|
||||
@@ -71,6 +68,9 @@ export default function JobPartsGraphAtom({
|
||||
</Pie>
|
||||
</PieChart>
|
||||
</ResponsiveContainer>
|
||||
<Typography.Title level={4}>
|
||||
{price === "act_price" ? "Actual Price" : "Database Price"}
|
||||
</Typography.Title>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -15,8 +15,13 @@ const mapDispatchToProps = (dispatch) => ({
|
||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||
});
|
||||
|
||||
export function WatcherPollingMolecule({ appSettings }) {
|
||||
export function NotificationsToggleAtom({ appSettings }) {
|
||||
const handleChange = (val) => {
|
||||
ipcRenderer.send(ipcTypes.default.app.toMain.track, {
|
||||
event: "TOGGLE_NOTIFICATION",
|
||||
enabled: val,
|
||||
});
|
||||
|
||||
ipcRenderer.send(ipcTypes.default.store.set, {
|
||||
enableNotifications: val,
|
||||
});
|
||||
@@ -36,4 +41,4 @@ export function WatcherPollingMolecule({ appSettings }) {
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(WatcherPollingMolecule);
|
||||
)(NotificationsToggleAtom);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export default (part_type) => {
|
||||
const converter = (part_type) => {
|
||||
switch (part_type) {
|
||||
case "PAA":
|
||||
return "A/M";
|
||||
@@ -19,3 +19,5 @@ export default (part_type) => {
|
||||
return part_type;
|
||||
}
|
||||
};
|
||||
|
||||
export default converter;
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectUpdateAvailable } from "../../../redux/application/application.selectors";
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
//scanLoading: selectScanLoading,
|
||||
updateAvailable: selectUpdateAvailable,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({});
|
||||
|
||||
export function UpdateAvailableAtom({ available }) {
|
||||
return <div>Update Available!</div>;
|
||||
}
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(UpdateAvailableAtom);
|
||||
@@ -0,0 +1,41 @@
|
||||
import { Switch } from "antd";
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import ipcTypes from "../../../ipc.types";
|
||||
import { selectSettings } from "../../../redux/application/application.selectors";
|
||||
import DataLabel from "../../atoms/data-label/data-label.atom";
|
||||
const { ipcRenderer } = window;
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
//currentUser: selectCurrentUser
|
||||
appSettings: selectSettings,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||
});
|
||||
|
||||
export function WatcherStartupAtom({ appSettings }) {
|
||||
const handleChange = (val) => {
|
||||
ipcRenderer.send(ipcTypes.default.app.toMain.track, {
|
||||
event: "TOGGLE_WATCHER_ON_STARTUP",
|
||||
enabled: val,
|
||||
});
|
||||
|
||||
ipcRenderer.send(ipcTypes.default.store.set, {
|
||||
runWatcherOnStartup: val,
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<DataLabel label="Run Watcher on Startup?">
|
||||
<Switch
|
||||
onChange={handleChange}
|
||||
checked={appSettings && appSettings.runWatcherOnStartup}
|
||||
/>
|
||||
</DataLabel>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(WatcherStartupAtom);
|
||||
@@ -3,8 +3,9 @@ import { DatePicker, message, Spin } from "antd";
|
||||
import moment from "moment";
|
||||
import React, { useState } from "react";
|
||||
import { UPDATE_JOB } from "../../../graphql/jobs.queries";
|
||||
import ipcTypes from "../../../ipc.types";
|
||||
import { DateFormat } from "../../../util/constants";
|
||||
|
||||
const { ipcRenderer } = window;
|
||||
export default function CloseDateDisplayMolecule({ jobId, close_date }) {
|
||||
const [editMode, setEditMode] = useState(false);
|
||||
const [value, setValue] = useState(moment(close_date));
|
||||
@@ -12,6 +13,9 @@ export default function CloseDateDisplayMolecule({ jobId, close_date }) {
|
||||
const [updateJob] = useMutation(UPDATE_JOB);
|
||||
|
||||
const handleChange = async (newDate) => {
|
||||
ipcRenderer.send(ipcTypes.default.app.toMain.track, {
|
||||
event: "SET_CLOSED_DATE",
|
||||
});
|
||||
setLoading(true);
|
||||
setValue(newDate);
|
||||
const result = await updateJob({
|
||||
|
||||
@@ -54,7 +54,7 @@ export function JobGroupMolecule({ bodyshop, jobId, group, job }) {
|
||||
<Dropdown overlay={menu} trigger={["click"]}>
|
||||
<a href=" #" onClick={(e) => e.preventDefault()}>
|
||||
{group}
|
||||
<DownOutlined />
|
||||
<DownOutlined style={{ marginLeft: ".2rem" }} />
|
||||
{loading && <LoadingOutlined />}
|
||||
</a>
|
||||
</Dropdown>
|
||||
|
||||
@@ -5,6 +5,7 @@ import ErrorResultAtom from "../../atoms/error-result/error-result.atom";
|
||||
import TimeAgoFormatter from "../../atoms/time-ago-formatter/time-ago-formatter.atom";
|
||||
import CloseDateDisplayMolecule from "../close-date-display/close-date-display.molecule";
|
||||
import JobGroupMolecule from "../job-group/job-group.molecule";
|
||||
import DeleteJobAtom from "../../atoms/delete-job/delete-job.atom";
|
||||
|
||||
export default function JobsDetailDescriptionMolecule({ loading, job }) {
|
||||
if (loading) return <Skeleton active />;
|
||||
@@ -13,7 +14,12 @@ export default function JobsDetailDescriptionMolecule({ loading, job }) {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<PageHeader ghost={false} title={job.clm_no} subTitle={job.ins_co_nm}>
|
||||
<PageHeader
|
||||
ghost={false}
|
||||
title={job.clm_no}
|
||||
subTitle={job.ins_co_nm}
|
||||
extra={[<DeleteJobAtom key="delete" jobId={job.id} />]}
|
||||
>
|
||||
<Descriptions column={{ xxl: 5, xl: 4, lg: 3, md: 3, sm: 2, xs: 1 }}>
|
||||
<Descriptions.Item label="Owner">{`${job.ownr_fn} ${job.ownr_ln}`}</Descriptions.Item>
|
||||
<Descriptions.Item label="Vehicle">{`${job.v_model_yr} ${job.v_makedesc} ${job.v_model} (${job.v_type})`}</Descriptions.Item>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { CalculatorOutlined } from "@ant-design/icons";
|
||||
import { Input, Table } from "antd";
|
||||
import React, { useState } from "react";
|
||||
import ipcTypes from "../../../ipc.types";
|
||||
@@ -6,11 +7,11 @@ import CurrencyFormatterAtom from "../../atoms/currency-formatter/currency-forma
|
||||
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";
|
||||
|
||||
const { ipcRenderer } = window;
|
||||
|
||||
export default function JobLinesTableMolecule({ loading, job }) {
|
||||
const [searchText, setSearchText] = useState("");
|
||||
const [filters, setFilters] = useState({ ignore: ["false"] });
|
||||
|
||||
const { joblines } = job;
|
||||
const columns = [
|
||||
@@ -19,23 +20,27 @@ export default function JobLinesTableMolecule({ loading, job }) {
|
||||
dataIndex: "line_no",
|
||||
key: "line_no",
|
||||
sorter: (a, b) => a.line_no - b.line_no,
|
||||
width: "5%",
|
||||
},
|
||||
{
|
||||
title: "S#",
|
||||
dataIndex: "line_ind",
|
||||
key: "line_ind",
|
||||
width: "5%",
|
||||
sorter: (a, b) => alphaSort(a.line_ind, b.line_ind),
|
||||
},
|
||||
{
|
||||
title: "Line Description",
|
||||
dataIndex: "line_desc",
|
||||
key: "line_desc",
|
||||
width: "25%",
|
||||
sorter: (a, b) => alphaSort(a.line_desc, b.line_desc),
|
||||
},
|
||||
{
|
||||
title: "Part Type",
|
||||
dataIndex: "part_type",
|
||||
key: "part_type",
|
||||
width: "5%",
|
||||
sorter: (a, b) => alphaSort(a.part_type, b.part_type),
|
||||
render: (text, record) => partTypeConverterAtom(text),
|
||||
},
|
||||
@@ -43,12 +48,14 @@ export default function JobLinesTableMolecule({ loading, job }) {
|
||||
title: "Part Number",
|
||||
dataIndex: "oem_partno",
|
||||
key: "oem_partno",
|
||||
width: "15%",
|
||||
sorter: (a, b) => alphaSort(a.oem_partno, b.oem_partno),
|
||||
},
|
||||
{
|
||||
title: "Database Price",
|
||||
dataIndex: "db_price",
|
||||
key: "db_price",
|
||||
width: "10%",
|
||||
sorter: (a, b) => a.db_price - b.db_price,
|
||||
render: (text, record) => (
|
||||
<CurrencyFormatterAtom>{record.db_price}</CurrencyFormatterAtom>
|
||||
@@ -58,6 +65,7 @@ export default function JobLinesTableMolecule({ loading, job }) {
|
||||
title: "Actual Price",
|
||||
dataIndex: "act_price",
|
||||
key: "act_price",
|
||||
width: "10%",
|
||||
sorter: (a, b) => a.act_price - b.act_price,
|
||||
render: (text, record) => (
|
||||
<CurrencyFormatterAtom>{record.act_price}</CurrencyFormatterAtom>
|
||||
@@ -67,6 +75,7 @@ export default function JobLinesTableMolecule({ loading, job }) {
|
||||
title: "Price Diff.",
|
||||
dataIndex: "price_diff",
|
||||
key: "price_diff",
|
||||
width: "10%",
|
||||
sorter: (a, b) => a.price_diff - b.price_diff,
|
||||
render: (text, record) => (
|
||||
<CurrencyFormatterAtom>{record.price_diff}</CurrencyFormatterAtom>
|
||||
@@ -76,6 +85,7 @@ export default function JobLinesTableMolecule({ loading, job }) {
|
||||
title: "Price Diff. %",
|
||||
dataIndex: "price_diff_pc",
|
||||
key: "price_diff_pc",
|
||||
width: "10%",
|
||||
sorter: (a, b) => a.price_diff_pc - b.price_diff_pc,
|
||||
render: (text, record) => (
|
||||
<PriceDiffPcFormatterAtom
|
||||
@@ -86,13 +96,15 @@ export default function JobLinesTableMolecule({ loading, job }) {
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Ignore?",
|
||||
title: <CalculatorOutlined />,
|
||||
dataIndex: "ignore",
|
||||
key: "ignore",
|
||||
filters: [
|
||||
{ text: "True", value: true },
|
||||
{ text: "False", value: false },
|
||||
{ text: "Eligible for RPS Calculation", value: false },
|
||||
{ text: "Ineligible RPS Calculation", value: true },
|
||||
],
|
||||
width: "5%",
|
||||
filteredValue: filters.ignore || null,
|
||||
onFilter: (value, record) => value === record.ignore,
|
||||
render: (text, record) => (
|
||||
<IgnoreJobLine
|
||||
@@ -111,33 +123,50 @@ export default function JobLinesTableMolecule({ loading, job }) {
|
||||
)
|
||||
: joblines;
|
||||
|
||||
const handleChange = (pagination, filters, sorter) => {
|
||||
console.log("Various parameters", pagination, filters, sorter);
|
||||
setFilters(filters);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Input.Search
|
||||
placeholder="Search"
|
||||
onSearch={(val) => {
|
||||
ipcRenderer.send(ipcTypes.default.app.toMain.track, {
|
||||
event: "JOB_LINES_SEARCH",
|
||||
query: val,
|
||||
});
|
||||
setSearchText(val);
|
||||
}}
|
||||
enterButton
|
||||
allowClear
|
||||
/>
|
||||
<Table
|
||||
title={() => (
|
||||
<Input.Search
|
||||
placeholder="Search"
|
||||
onSearch={(val) => {
|
||||
ipcRenderer.send(ipcTypes.default.app.toMain.track, {
|
||||
event: "JOB_LINES_SEARCH",
|
||||
query: val,
|
||||
});
|
||||
setSearchText(val);
|
||||
}}
|
||||
enterButton
|
||||
allowClear
|
||||
/>
|
||||
)}
|
||||
columns={columns}
|
||||
rowKey="id"
|
||||
loading={loading}
|
||||
size="small"
|
||||
pagination={false}
|
||||
dataSource={data}
|
||||
onChange={handleChange}
|
||||
scroll={{
|
||||
x: true,
|
||||
y: "20rem",
|
||||
}}
|
||||
// summary={
|
||||
// () => (
|
||||
// <Table.Summary.Row>
|
||||
// <Table.Summary.Cell index={0}>Summary</Table.Summary.Cell>
|
||||
// <Table.Summary.Cell index={5}>
|
||||
// This is a summary content
|
||||
// </Table.Summary.Cell>
|
||||
// <Table.Summary.Cell index={6}>
|
||||
// This is a summary content
|
||||
// </Table.Summary.Cell>
|
||||
// </Table.Summary.Row>
|
||||
// )
|
||||
// }
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,22 +1,21 @@
|
||||
|
||||
.jobs-list-item {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
|
||||
.jobs-list-item-content {
|
||||
&-selected {
|
||||
border-left: 3px solid #1890ff;
|
||||
}
|
||||
|
||||
display: inline;
|
||||
margin: 0.5rem;
|
||||
padding: 0.5rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
background-color: #e6f7ff;
|
||||
padding: 0.1rem !important;
|
||||
margin: 0;
|
||||
border-bottom: 0.8rem solid #f0f0f0 !important;
|
||||
|
||||
.jobs-list-item-content {
|
||||
&-selected {
|
||||
border-left: 3px solid #1890ff;
|
||||
}
|
||||
|
||||
display: inline;
|
||||
margin: 0.5rem;
|
||||
padding: 0.5rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
background-color: #e6f7ff;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -25,14 +25,16 @@ export function JobsTargetsStatsMolecule({
|
||||
job,
|
||||
selectedJobTargetPc,
|
||||
}) {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
const { actPriceSum, jobRpsDollars } = useCallback(
|
||||
CalculateJobRpsDollars(job, true),
|
||||
[job]
|
||||
[job, CalculateJobRpsDollars]
|
||||
);
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
const { dbPriceSum, jobRpsPc } = useCallback(
|
||||
CalculateJobRpsPc(job, jobRpsDollars, true),
|
||||
[job, jobRpsDollars]
|
||||
[job, jobRpsDollars, CalculateJobRpsPc]
|
||||
);
|
||||
|
||||
if (loading) return <Skeleton active />;
|
||||
@@ -43,30 +45,52 @@ export function JobsTargetsStatsMolecule({
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-around",
|
||||
marginTop: "1rem",
|
||||
marginTop: "2rem",
|
||||
marginBottom: "1rem",
|
||||
}}
|
||||
>
|
||||
<Statistic
|
||||
title="Target RPS %"
|
||||
value={(selectedJobTargetPc * 100).toFixed(1)}
|
||||
suffix="%"
|
||||
/>
|
||||
<Statistic
|
||||
title="Current RPS %"
|
||||
valueStyle={{
|
||||
color: selectedJobTargetPc > (jobRpsPc || 0) ? "tomato" : "seagreen",
|
||||
}}
|
||||
value={((jobRpsPc || 0) * 100).toFixed(1)}
|
||||
suffix="%"
|
||||
/>
|
||||
<Statistic
|
||||
title="Target RPS $"
|
||||
value={actPriceSum.percentage(selectedJobTargetPc * 100).toFormat()}
|
||||
/>
|
||||
<Statistic title="Current RPS $" value={jobRpsDollars.toFormat()} />
|
||||
<Statistic title="DB Price Total" value={dbPriceSum.toFormat()} />
|
||||
<Statistic title="Actual Price Total" value={actPriceSum.toFormat()} />
|
||||
<div style={{ display: "flex" }}>
|
||||
<Statistic
|
||||
title="Target RPS %"
|
||||
value={(selectedJobTargetPc * 100).toFixed(1)}
|
||||
suffix="%"
|
||||
style={{ margin: "0rem .5rem" }}
|
||||
/>
|
||||
<Statistic
|
||||
title="Current RPS %"
|
||||
style={{ margin: "0rem .5rem" }}
|
||||
valueStyle={{
|
||||
color:
|
||||
selectedJobTargetPc > (jobRpsPc || 0) ? "tomato" : "seagreen",
|
||||
}}
|
||||
value={((jobRpsPc || 0) * 100).toFixed(1)}
|
||||
suffix="%"
|
||||
/>
|
||||
</div>
|
||||
<div style={{ display: "flex" }}>
|
||||
<Statistic
|
||||
title="Target RPS $"
|
||||
style={{ margin: "0rem .5rem" }}
|
||||
value={actPriceSum.percentage(selectedJobTargetPc * 100).toFormat()}
|
||||
/>
|
||||
<Statistic
|
||||
title="Current RPS $"
|
||||
style={{ margin: "0rem .5rem" }}
|
||||
value={jobRpsDollars.toFormat()}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ display: "flex" }}>
|
||||
<Statistic
|
||||
title="DB Price Total"
|
||||
style={{ margin: "0rem .5rem" }}
|
||||
value={dbPriceSum.toFormat()}
|
||||
/>
|
||||
<Statistic
|
||||
title="Actual Price Total"
|
||||
style={{ margin: "0rem .5rem" }}
|
||||
value={actPriceSum.toFormat()}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,11 +3,16 @@ import React, { useState } from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { Link } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import ipcTypes from "../../../ipc.types";
|
||||
import { setSelectedJobId } from "../../../redux/application/application.actions";
|
||||
|
||||
import {
|
||||
selectReportData,
|
||||
selectReportLoading,
|
||||
} from "../../../redux/reporting/reporting.selectors";
|
||||
|
||||
const { ipcRenderer } = window;
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
reportingLoading: selectReportLoading,
|
||||
reportData: selectReportData,
|
||||
@@ -123,6 +128,10 @@ export function ReportingJobsListMolecule({
|
||||
<Input.Search
|
||||
placeholder="Search"
|
||||
onSearch={(val) => {
|
||||
ipcRenderer.send(ipcTypes.default.app.toMain.track, {
|
||||
event: "REPORTS_LIST_SEARCH",
|
||||
query: val,
|
||||
});
|
||||
setSearchText(val);
|
||||
}}
|
||||
enterButton
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Button, Input, Table } from "antd";
|
||||
import { Button, Input, message, Table } from "antd";
|
||||
import React, { useState } from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
selectScanLoading,
|
||||
} from "../../../redux/scan/scan.selectors";
|
||||
import { alphaSort } from "../../../util/sorters";
|
||||
import DeleteAllEmsAtom from "../../atoms/delete-all-ems/delete-all-ems.atom";
|
||||
import LastScannedAtom from "../../atoms/last-scanned/last-scanned.atom";
|
||||
import ScanRefreshAtom from "../../atoms/scan-refresh/scan-refresh.atom";
|
||||
|
||||
@@ -64,12 +65,13 @@ export function ScanEstimateListMolecule({ scanLoading, estimates }) {
|
||||
key: "import",
|
||||
render: (text, record) => (
|
||||
<Button
|
||||
onClick={() =>
|
||||
onClick={() => {
|
||||
message.info("Attempting to import job...");
|
||||
ipcRenderer.send(
|
||||
ipcTypes.default.fileScan.toMain.importJob,
|
||||
record.filepath
|
||||
)
|
||||
}
|
||||
);
|
||||
}}
|
||||
>
|
||||
Import
|
||||
</Button>
|
||||
@@ -96,6 +98,7 @@ export function ScanEstimateListMolecule({ scanLoading, estimates }) {
|
||||
<div className="imex-table-header">
|
||||
<ScanRefreshAtom />
|
||||
<LastScannedAtom />
|
||||
<DeleteAllEmsAtom />
|
||||
<Input.Search
|
||||
className="imex-table-header__search"
|
||||
placeholder="Search"
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import { useQuery } from "@apollo/client";
|
||||
import { Result } from "antd";
|
||||
import { Card, Result } from "antd";
|
||||
import React, { useEffect } from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { QUERY_JOB_BY_PK } from "../../../graphql/jobs.queries";
|
||||
import { setSelectedJobTargetPc } from "../../../redux/application/application.actions";
|
||||
import { selectSelectedJobId } from "../../../redux/application/application.selectors";
|
||||
import DeleteJobAtom from "../../atoms/delete-job/delete-job.atom";
|
||||
import ErrorResultAtom from "../../atoms/error-result/error-result.atom";
|
||||
import JobsPartsGraphAtom from "../../atoms/jobs-parts-graph/jobs-parts-graph.atom";
|
||||
import JobsDetailDescriptionMolecule from "../../molecules/jobs-detail-description/jobs-detail-description.molecule";
|
||||
@@ -37,7 +36,19 @@ export function JobsDetailOrganism({ selectedJobId, setSelectedJobTargetPc }) {
|
||||
});
|
||||
}, [data, setSelectedJobTargetPc]);
|
||||
|
||||
if (!selectedJobId) return <Result title="No job selected." />;
|
||||
if (!selectedJobId)
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
height: "100%",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<Result title="No job selected." />
|
||||
</div>
|
||||
);
|
||||
if (error)
|
||||
return (
|
||||
<ErrorResultAtom
|
||||
@@ -48,37 +59,42 @@ export function JobsDetailOrganism({ selectedJobId, setSelectedJobTargetPc }) {
|
||||
|
||||
return (
|
||||
<div className="jobs-detail-container">
|
||||
<JobsDetailDescriptionMolecule
|
||||
loading={loading}
|
||||
job={data ? data.jobs_by_pk : null}
|
||||
/>
|
||||
<JobsLinesTableMolecule
|
||||
loading={loading}
|
||||
job={data ? data.jobs_by_pk : {}}
|
||||
/>
|
||||
<JobsTargetsStatsMolecule
|
||||
loading={loading}
|
||||
job={data ? data.jobs_by_pk : null}
|
||||
/>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "space-evenly",
|
||||
minHeight: "20rem",
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<JobsPartsGraphAtom
|
||||
job={data ? data.jobs_by_pk : null}
|
||||
<Card>
|
||||
<JobsDetailDescriptionMolecule
|
||||
loading={loading}
|
||||
price="db_price"
|
||||
/>
|
||||
<JobsPartsGraphAtom
|
||||
job={data ? data.jobs_by_pk : null}
|
||||
loading={loading}
|
||||
/>
|
||||
</div>
|
||||
<DeleteJobAtom jobId={data ? data.jobs_by_pk.id : null} />
|
||||
</Card>
|
||||
<Card>
|
||||
<JobsLinesTableMolecule
|
||||
loading={loading}
|
||||
job={data ? data.jobs_by_pk : {}}
|
||||
/>
|
||||
</Card>
|
||||
<Card>
|
||||
<JobsTargetsStatsMolecule
|
||||
loading={loading}
|
||||
job={data ? data.jobs_by_pk : null}
|
||||
/>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "space-evenly",
|
||||
minHeight: "20rem",
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<JobsPartsGraphAtom
|
||||
job={data ? data.jobs_by_pk : null}
|
||||
loading={loading}
|
||||
price="db_price"
|
||||
/>
|
||||
<JobsPartsGraphAtom
|
||||
job={data ? data.jobs_by_pk : null}
|
||||
loading={loading}
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
.jobs-detail-container {
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
background-color: rgb(244, 244, 244);
|
||||
& > * {
|
||||
margin: 0.7rem;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,10 +4,13 @@ import { Dropdown, List, Menu, Spin } from "antd";
|
||||
import React, { useState } from "react";
|
||||
import InfiniteScroll from "react-infinite-scroller";
|
||||
import { SEARCH_JOBS_PAGINATED } from "../../../graphql/jobs.queries";
|
||||
import ipcTypes from "../../../ipc.types";
|
||||
import ErrorResultAtom from "../../atoms/error-result/error-result.atom";
|
||||
import JobsListItemMolecule from "../../molecules/jobs-list-item/jobs-list-item.molecule";
|
||||
import JobsSearchFieldsMolecule from "../../molecules/jobs-search-fields/jobs-search-fields.molecule";
|
||||
|
||||
const { ipcRenderer } = window;
|
||||
|
||||
const limit = 20;
|
||||
export default function JobsTableOrganism() {
|
||||
const [state, setState] = useState({ hasMore: true });
|
||||
@@ -32,7 +35,10 @@ export default function JobsTableOrganism() {
|
||||
);
|
||||
|
||||
const handleInfiniteOnLoad = (page) => {
|
||||
if (fetchMore)
|
||||
if (fetchMore) {
|
||||
ipcRenderer.send(ipcTypes.default.app.toMain.track, {
|
||||
event: "FETCH_MORE_JOBS",
|
||||
});
|
||||
fetchMore({
|
||||
variables: {
|
||||
offset: limit * page,
|
||||
@@ -59,6 +65,7 @@ export default function JobsTableOrganism() {
|
||||
return newCache;
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
if (error)
|
||||
|
||||
@@ -5,7 +5,6 @@ import JobsListOrganism from "../../organisms/jobs-list-latest/jobs-list-latest.
|
||||
import JobsListSearchOrganism from "../../organisms/jobs-list-search/jobs-list-search.organism";
|
||||
|
||||
export default function JobsPage() {
|
||||
console.log("Jobs Page Rerender");
|
||||
const selectedBreakpoint = Object.entries(Grid.useBreakpoint())
|
||||
.filter((screen) => !!screen[1])
|
||||
.slice(-1)[0];
|
||||
|
||||
@@ -22,7 +22,7 @@ export function RoutesPage({ bodyshop }) {
|
||||
errorMessage="You do not currently have access to any shop. Please reach out to technical support."
|
||||
/>
|
||||
);
|
||||
console.log("routes render");
|
||||
|
||||
return (
|
||||
<Layout style={{ background: "#fff", height: "100vh" }} hasSider>
|
||||
<Layout.Sider
|
||||
@@ -33,7 +33,7 @@ export function RoutesPage({ bodyshop }) {
|
||||
<SiderMenuOrganism />
|
||||
</Layout.Sider>
|
||||
<Layout style={{ background: "#fff" }}>
|
||||
<Layout.Content style={{ margin: "1rem", height: "100%" }}>
|
||||
<Layout.Content style={{ marginLeft: "1rem", height: "100%" }}>
|
||||
<Route exact path="/settings" component={SettingsPage} />
|
||||
<Route exact path="/reporting" component={ReportingPage} />
|
||||
<Route exact path="/scan" component={ScanPage} />
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Col, Row } from "antd";
|
||||
import React, { useEffect } from "react";
|
||||
import ipcTypes from "../../../ipc.types";
|
||||
import NotificationsToggleAtom from "../../atoms/notifications-toggle/notifications-toggle.atom";
|
||||
import WatcherStartupAtom from "../../atoms/watcher-startup/watcher-startup.atom";
|
||||
import WatcherPollingMolecule from "../../molecules/watcher-polling/watcher-polling.molecule";
|
||||
import FilePathsListOrganism from "../../organisms/filepaths-list/filepaths-list.organism";
|
||||
import ShopSettingsOrganism from "../../organisms/shop-settings/shop-settings.organism";
|
||||
@@ -23,6 +24,7 @@ export default function SettingsPage() {
|
||||
<WatcherManagerOrganism />
|
||||
<WatcherPollingMolecule />
|
||||
<NotificationsToggleAtom />
|
||||
<WatcherStartupAtom />
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
import { Button, Layout } from "antd";
|
||||
import React, { useEffect } from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import ipcTypes from "../ipc.types";
|
||||
|
||||
const { ipcRenderer } = window.require("electron");
|
||||
|
||||
const mapStateToProps = createStructuredSelector({});
|
||||
const mapDispatchToProps = (dispatch) => ({});
|
||||
|
||||
export function App() {
|
||||
useEffect(() => {
|
||||
ipcRenderer.on("not a real channel", (event, obj) => {
|
||||
console.log("not a real channel", obj);
|
||||
});
|
||||
// Cleanup the listener events so that memory leaks are avoided.
|
||||
return function cleanup() {
|
||||
ipcRenderer.removeAllListeners("not a real channel");
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Layout>
|
||||
<Layout.Header>
|
||||
<div> Header</div>
|
||||
</Layout.Header>
|
||||
<Layout.Content>
|
||||
<div>Welcome to your new react app. asdas sd</div>
|
||||
<Button
|
||||
onClick={() => {
|
||||
ipcRenderer.send("test", { test: true });
|
||||
}}
|
||||
>
|
||||
TEST Generic IPC
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
ipcRenderer.send(ipcTypes.default.filewatcher.start);
|
||||
}}
|
||||
>
|
||||
Start Watcher
|
||||
</Button>
|
||||
</Layout.Content>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(App);
|
||||
@@ -9,7 +9,7 @@ import App from "./App/App";
|
||||
import "./index.css";
|
||||
import { persistor, store } from "./redux/store";
|
||||
require("dotenv").config();
|
||||
LogRocket.init("imex/rps");
|
||||
if (process.env.NODE_ENV === "production") LogRocket.init("imex/rps");
|
||||
|
||||
ReactDOM.render(
|
||||
<Provider store={store}>
|
||||
@@ -21,3 +21,4 @@ ReactDOM.render(
|
||||
</Provider>,
|
||||
document.getElementById("root")
|
||||
);
|
||||
console.log("Connecting to endpoint: ", process.env.REACT_APP_GRAPHQL_ENDPOINT);
|
||||
|
||||
@@ -21,6 +21,7 @@ exports.default = {
|
||||
toMain: {
|
||||
scanFilePaths: "fileScan__scanFilePaths",
|
||||
importJob: "fileScan__importJob",
|
||||
deleteAllEms: "filescan_deleteAllEms",
|
||||
},
|
||||
toRenderer: {
|
||||
scanFilePathsResponse: "fileScan__scanFilePathsResponse",
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { message } from "antd";
|
||||
import gql from "graphql-tag";
|
||||
import _ from "lodash";
|
||||
import moment from "moment";
|
||||
@@ -5,7 +6,7 @@ import client from "../graphql/GraphQLClient";
|
||||
import {
|
||||
INSERT_NEW_JOB,
|
||||
QUERY_JOB_BY_CLM_NO,
|
||||
UPDATE_JOB
|
||||
UPDATE_JOB,
|
||||
} from "../graphql/jobs.queries";
|
||||
import { QUERY_GROUPS_BY_MAKE_TYPE } from "../graphql/veh_group.queries";
|
||||
import { store } from "../redux/store";
|
||||
@@ -56,7 +57,7 @@ export async function UpsertEstimate(job) {
|
||||
} else {
|
||||
logger.info("Attemping to insert job record.");
|
||||
|
||||
const result = await client.mutate({
|
||||
await client.mutate({
|
||||
mutation: INSERT_NEW_JOB,
|
||||
variables: {
|
||||
job: { ...job, bodyshopid: shopId },
|
||||
@@ -64,9 +65,8 @@ export async function UpsertEstimate(job) {
|
||||
refetchQueries: ["QUERY_ALL_JOBS_PAGINATED"],
|
||||
});
|
||||
logger.info("Job inserted succesfully.");
|
||||
|
||||
console.log("UpsertEstimate -> result", result);
|
||||
}
|
||||
message.success("Job uploaded successfully!");
|
||||
}
|
||||
|
||||
export const GetSupplementDelta = async (jobId, existingLinesO, newLines) => {
|
||||
|
||||
@@ -38,7 +38,13 @@ export const setSelectedJobTargetPcSuccess = (pct) => ({
|
||||
type: ApplicationActionTypes.SET_SELECTED_JOB_TARGET_PC_SUCCESS,
|
||||
payload: pct,
|
||||
});
|
||||
|
||||
export const setSettings = (settingsObj) => ({
|
||||
type: ApplicationActionTypes.SET_SETTINGS,
|
||||
payload: settingsObj,
|
||||
});
|
||||
|
||||
export const setUpdateAvailable = (available) => ({
|
||||
type: ApplicationActionTypes.SET_UPDATE_AVAILABLE,
|
||||
payload: available,
|
||||
});
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import ipcTypes from "../../ipc.types";
|
||||
import ApplicationActionTypes from "./application.types";
|
||||
const INITIAL_STATE = {
|
||||
watcherStatus: "Not Started",
|
||||
@@ -6,8 +7,11 @@ const INITIAL_STATE = {
|
||||
selectedJobId: null,
|
||||
selectedJobTargetPc: 0,
|
||||
settings: {},
|
||||
updateAvailable: false,
|
||||
};
|
||||
|
||||
const { ipcRenderer } = window;
|
||||
|
||||
const applicationReducer = (state = INITIAL_STATE, action) => {
|
||||
switch (action.type) {
|
||||
case ApplicationActionTypes.SET_WATCHED_PATHS:
|
||||
@@ -16,11 +20,17 @@ const applicationReducer = (state = INITIAL_STATE, action) => {
|
||||
watchedPaths: action.payload,
|
||||
};
|
||||
case ApplicationActionTypes.ADD_WATCHED_PATH:
|
||||
ipcRenderer.send(ipcTypes.default.app.toMain.track, {
|
||||
event: "ADD_WATCHED_PATH",
|
||||
});
|
||||
return {
|
||||
...state,
|
||||
watchedPaths: [...state.watchedPaths, action.payload],
|
||||
};
|
||||
case ApplicationActionTypes.REMOVE_WATCHED_PATH:
|
||||
ipcRenderer.send(ipcTypes.default.app.toMain.track, {
|
||||
event: "REMOVE_WATCHED_PATH",
|
||||
});
|
||||
return {
|
||||
...state,
|
||||
watchedPaths: state.watchedPaths.filter((p) => p !== action.payload),
|
||||
@@ -31,6 +41,10 @@ const applicationReducer = (state = INITIAL_STATE, action) => {
|
||||
watcherStatus: action.payload,
|
||||
};
|
||||
case ApplicationActionTypes.SET_WATCHER_ERROR:
|
||||
ipcRenderer.send(ipcTypes.default.app.toMain.track, {
|
||||
event: "WATCHER_ERROR",
|
||||
error: action.payload,
|
||||
});
|
||||
return {
|
||||
...state,
|
||||
watcherError: action.payload,
|
||||
@@ -41,10 +55,14 @@ const applicationReducer = (state = INITIAL_STATE, action) => {
|
||||
selectedJobTargetPc: action.payload,
|
||||
};
|
||||
case ApplicationActionTypes.SET_SELECTED_JOB_ID:
|
||||
ipcRenderer.send(ipcTypes.default.app.toMain.track, {
|
||||
event: "SET_SELECTED_JOB",
|
||||
});
|
||||
return { ...state, selectedJobId: action.payload };
|
||||
case ApplicationActionTypes.SET_SETTINGS:
|
||||
return { ...state, settings: { ...state.settings, ...action.payload } };
|
||||
|
||||
case ApplicationActionTypes.SET_UPDATE_AVAILABLE:
|
||||
return { ...state, updateAvailable: action.payload };
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
||||
@@ -31,3 +31,8 @@ export const selectSettings = createSelector(
|
||||
[selectApplication],
|
||||
(application) => application.settings
|
||||
);
|
||||
|
||||
export const selectUpdateAvailable = createSelector(
|
||||
[selectApplication],
|
||||
(application) => application.updateAvailable
|
||||
);
|
||||
|
||||
@@ -8,5 +8,6 @@ const ApplicationActionTypes = {
|
||||
SET_SELECTED_JOB_TARGET_PC: "SET_SELECTED_JOB_TARGET_PC",
|
||||
SET_SELECTED_JOB_TARGET_PC_SUCCESS: "SET_SELECTED_JOB_TARGET_PC_SUCCESS",
|
||||
SET_SETTINGS: "SET_SETTINGS",
|
||||
SET_UPDATE_AVAILABLE: "SET_UPDATE_AVAILABLE",
|
||||
};
|
||||
export default ApplicationActionTypes;
|
||||
|
||||
@@ -30,5 +30,5 @@ sagaMiddleWare.run(rootSaga);
|
||||
|
||||
export const persistor = persistStore(store);
|
||||
|
||||
export default { store, persistStore };
|
||||
|
||||
const epxortObj = { store, persistStore };
|
||||
export default epxortObj;
|
||||
|
||||
@@ -92,6 +92,9 @@ export function* onSignOutStart() {
|
||||
|
||||
export function* signOutStart() {
|
||||
try {
|
||||
ipcRenderer.send(ipcTypes.default.app.toMain.track, {
|
||||
event: "SIGN_OUT",
|
||||
});
|
||||
ipcRenderer.send(ipcTypes.default.fileWatcher.toMain.stop);
|
||||
yield auth.signOut();
|
||||
yield put(signOutSuccess());
|
||||
@@ -137,7 +140,9 @@ export function* signInSuccessSaga({ payload }) {
|
||||
ipcTypes.default.app.toMain.setAcceptableInsCoNm,
|
||||
shop.data.bodyshops[0].accepted_ins_co
|
||||
);
|
||||
ipcRenderer.send(ipcTypes.default.fileWatcher.toMain.start);
|
||||
ipcRenderer.send(ipcTypes.default.fileWatcher.toMain.start, {
|
||||
startup: true,
|
||||
});
|
||||
} else {
|
||||
console.log("No bodyshop has been associated.");
|
||||
yield put(setBodyshop(false));
|
||||
@@ -155,6 +160,10 @@ export function* onSendPasswordResetStart() {
|
||||
}
|
||||
export function* sendPasswordResetEmail({ payload }) {
|
||||
try {
|
||||
ipcRenderer.send(ipcTypes.default.app.toMain.track, {
|
||||
event: "RESET_PASSWORD",
|
||||
email: payload,
|
||||
});
|
||||
yield auth.sendPasswordResetEmail(payload);
|
||||
|
||||
yield put(sendPasswordResetSuccess());
|
||||
|
||||
Reference in New Issue
Block a user