From 4d5d370ccf7e7550ba0af32a4721c7a28759f78a Mon Sep 17 00:00:00 2001
From: Patrick Fic <>
Date: Mon, 19 Oct 2020 07:38:26 -0700
Subject: [PATCH] Added graphs, fixed rps total calculation
---
electron/decoder/decoder.js | 37 +++-
electron/electron-store.js | 2 +-
electron/file-watcher/file-watcher.js | 5 +-
electron/ipc-main-handler.js | 6 +
.../down.yaml | 5 +
.../up.yaml | 6 +
.../down.yaml | 26 +++
.../up.yaml | 27 +++
hasura/migrations/metadata.yaml | 1 +
package-lock.json | 186 ++++++++++++++++++
package.json | 1 +
.../jobs-parts-graph.atom.jsx | 79 ++++++++
.../jobs-lines-table.molecule.jsx | 2 +-
.../jobs-targets-stats.molecule.jsx | 28 ++-
.../jobs-detail/jobs-detail.organism.jsx | 20 +-
.../jobs-list/jobs-list.organism.jsx | 2 +-
src/graphql/bodyshop.queries.js | 1 +
src/graphql/jobs.queries.js | 3 +-
src/ipc.types.js | 5 +
src/redux/user/user.sagas.js | 5 +-
20 files changed, 416 insertions(+), 31 deletions(-)
create mode 100644 hasura/migrations/1602889815353_alter_table_public_bodyshops_add_column_accepted_ins_co/down.yaml
create mode 100644 hasura/migrations/1602889815353_alter_table_public_bodyshops_add_column_accepted_ins_co/up.yaml
create mode 100644 hasura/migrations/1602889830378_update_permission_user_public_table_bodyshops/down.yaml
create mode 100644 hasura/migrations/1602889830378_update_permission_user_public_table_bodyshops/up.yaml
create mode 100644 src/components/atoms/jobs-parts-graph/jobs-parts-graph.atom.jsx
diff --git a/electron/decoder/decoder.js b/electron/decoder/decoder.js
index 448ac7b..c4f053a 100644
--- a/electron/decoder/decoder.js
+++ b/electron/decoder/decoder.js
@@ -2,6 +2,7 @@ const { DBFFile } = require("dbffile");
const path = require("path");
const _ = require("lodash");
const log = require("electron-log");
+const { store } = require("../electron-store");
async function DecodeEstimate(filePath) {
const parsedFilePath = path.parse(filePath);
@@ -9,18 +10,34 @@ async function DecodeEstimate(filePath) {
parsedFilePath.dir,
parsedFilePath.name
);
- const ret = {
+ const job = {
...(await DecodeAd1File(extensionlessFilePath)),
...(await DecodeVehFile(extensionlessFilePath)),
...(await DecodeTtlFile(extensionlessFilePath)),
...(await DecodeLinFile(extensionlessFilePath)),
};
- if (ret.V_MILEAGE > 20000)
- return _.transform(ret, function (result, val, key) {
+ const ad2 = await DecodeAd2File(extensionlessFilePath);
+
+ if (job.OWNR_FN === "") job.OWNR_FN = ad2.CLMT_FN;
+ if (job.OWNR_LN === "") job.OWNR_LN = ad2.CLMT_LN;
+
+ const accepted_ins_co = store.get("accepted_ins_co");
+
+ let returnValue;
+ if (job.V_MILEAGE <= 20000) {
+ returnValue = { ERROR: "Vehicle mileage is less than 20,000kms." };
+ } else if (!accepted_ins_co.includes(job.INS_CO_NM)) {
+ returnValue = {
+ ERROR: `Insurance Company Name is not valid for RPS. (${job.INS_CO_NM})`,
+ };
+ } else {
+ returnValue = _.transform(job, function (result, val, key) {
result[key.toLowerCase()] = val;
});
- return null;
+ }
+
+ return returnValue;
}
async function DecodeAd1File(extensionlessFilePath) {
@@ -144,6 +161,13 @@ async function DecodeAd1File(extensionlessFilePath) {
// "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"]);
+}
+
async function DecodeVehFile(extensionlessFilePath) {
let dbf = await DBFFile.open(`${extensionlessFilePath}V.VEH`);
let records = await dbf.readRecords(1);
@@ -299,6 +323,11 @@ async function DecodeLinFile(extensionlessFilePath) {
(jobline) =>
jobline.part_type &&
!jobline.db_ref.startsWith("900") &&
+ !jobline.db_ref.toLowerCase().startsWith("urethane") &&
+ !jobline.db_ref.toLowerCase().startsWith("wheel") &&
+ !jobline.db_ref.toLowerCase().startsWith("hazardous") &&
+ !jobline.db_ref.toLowerCase().startsWith("detail") &&
+ !jobline.db_ref.toLowerCase().startsWith("clean") &&
jobline.part_type.toUpperCase() !== "PAG" &&
jobline.part_type.toUpperCase() !== "PAS" &&
jobline.part_type.toUpperCase() !== "PASL" &&
diff --git a/electron/electron-store.js b/electron/electron-store.js
index 4e62a8a..9d59c9c 100644
--- a/electron/electron-store.js
+++ b/electron/electron-store.js
@@ -1,5 +1,5 @@
const Store = require("electron-store");
-const store = new Store({ defaults: { filePaths: [] } });
+const store = new Store({ defaults: { filePaths: [], accepted_ins_co: [] } });
exports.store = store;
diff --git a/electron/file-watcher/file-watcher.js b/electron/file-watcher/file-watcher.js
index edde955..edb58d6 100644
--- a/electron/file-watcher/file-watcher.js
+++ b/electron/file-watcher/file-watcher.js
@@ -106,7 +106,7 @@ async function HandleNewFile(path) {
b.webContents.send(ipcTypes.default.estimate.toRenderer.estimateDecodeStart);
const newJob = await DecodeEstimate(path);
- if (newJob && newJob) {
+ if (newJob && !newJob.ERROR) {
b.webContents.send(
ipcTypes.default.estimate.toRenderer.estimateDecodeSuccess,
newJob
@@ -119,8 +119,7 @@ async function HandleNewFile(path) {
} else {
NewNotification({
title: "Job Ignored",
- body:
- "The job was not uploaded because it does not meet RPS requirements.",
+ body: newJob.ERROR,
}).show();
}
}
diff --git a/electron/ipc-main-handler.js b/electron/ipc-main-handler.js
index e1783fd..6c69626 100644
--- a/electron/ipc-main-handler.js
+++ b/electron/ipc-main-handler.js
@@ -1,4 +1,6 @@
const { ipcMain } = require("electron");
+const { default: ipcTypes } = require("../src/ipc.types");
+const { store } = require("./electron-store");
const { mainWindow } = require("./main");
//Import Ipc Handlers
require("./file-watcher/file-watcher-ipc");
@@ -11,3 +13,7 @@ ipcMain.on("test", async (event, object) => {
console.log(mainWindow);
event.reply("test-toRenderer", { status: 0, message: null });
});
+
+ipcMain.on(ipcTypes.app.toMain.setAcceptableInsCoNm, (event, insCos) => {
+ store.set("accepted_ins_co", insCos);
+});
diff --git a/hasura/migrations/1602889815353_alter_table_public_bodyshops_add_column_accepted_ins_co/down.yaml b/hasura/migrations/1602889815353_alter_table_public_bodyshops_add_column_accepted_ins_co/down.yaml
new file mode 100644
index 0000000..05743ac
--- /dev/null
+++ b/hasura/migrations/1602889815353_alter_table_public_bodyshops_add_column_accepted_ins_co/down.yaml
@@ -0,0 +1,5 @@
+- args:
+ cascade: false
+ read_only: false
+ sql: ALTER TABLE "public"."bodyshops" DROP COLUMN "accepted_ins_co";
+ type: run_sql
diff --git a/hasura/migrations/1602889815353_alter_table_public_bodyshops_add_column_accepted_ins_co/up.yaml b/hasura/migrations/1602889815353_alter_table_public_bodyshops_add_column_accepted_ins_co/up.yaml
new file mode 100644
index 0000000..4453703
--- /dev/null
+++ b/hasura/migrations/1602889815353_alter_table_public_bodyshops_add_column_accepted_ins_co/up.yaml
@@ -0,0 +1,6 @@
+- args:
+ cascade: false
+ read_only: false
+ sql: ALTER TABLE "public"."bodyshops" ADD COLUMN "accepted_ins_co" jsonb NULL
+ DEFAULT jsonb_build_array();
+ type: run_sql
diff --git a/hasura/migrations/1602889830378_update_permission_user_public_table_bodyshops/down.yaml b/hasura/migrations/1602889830378_update_permission_user_public_table_bodyshops/down.yaml
new file mode 100644
index 0000000..a777f71
--- /dev/null
+++ b/hasura/migrations/1602889830378_update_permission_user_public_table_bodyshops/down.yaml
@@ -0,0 +1,26 @@
+- args:
+ role: user
+ table:
+ name: bodyshops
+ schema: public
+ type: drop_select_permission
+- args:
+ permission:
+ allow_aggregations: false
+ columns:
+ - created_at
+ - id
+ - shopname
+ - targets
+ - updated_at
+ computed_fields: []
+ filter:
+ associations:
+ user:
+ authid:
+ _eq: X-Hasura-User-Id
+ role: user
+ table:
+ name: bodyshops
+ schema: public
+ type: create_select_permission
diff --git a/hasura/migrations/1602889830378_update_permission_user_public_table_bodyshops/up.yaml b/hasura/migrations/1602889830378_update_permission_user_public_table_bodyshops/up.yaml
new file mode 100644
index 0000000..c82a190
--- /dev/null
+++ b/hasura/migrations/1602889830378_update_permission_user_public_table_bodyshops/up.yaml
@@ -0,0 +1,27 @@
+- args:
+ role: user
+ table:
+ name: bodyshops
+ schema: public
+ type: drop_select_permission
+- args:
+ permission:
+ allow_aggregations: false
+ columns:
+ - accepted_ins_co
+ - created_at
+ - id
+ - shopname
+ - targets
+ - updated_at
+ computed_fields: []
+ filter:
+ associations:
+ user:
+ authid:
+ _eq: X-Hasura-User-Id
+ role: user
+ table:
+ name: bodyshops
+ schema: public
+ type: create_select_permission
diff --git a/hasura/migrations/metadata.yaml b/hasura/migrations/metadata.yaml
index eb3f498..35df80b 100644
--- a/hasura/migrations/metadata.yaml
+++ b/hasura/migrations/metadata.yaml
@@ -42,6 +42,7 @@ tables:
- role: user
permission:
columns:
+ - accepted_ins_co
- created_at
- id
- shopname
diff --git a/package-lock.json b/package-lock.json
index 2b5b10e..a264126 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -6313,6 +6313,73 @@
"type": "^1.0.1"
}
},
+ "d3-array": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz",
+ "integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw=="
+ },
+ "d3-collection": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/d3-collection/-/d3-collection-1.0.7.tgz",
+ "integrity": "sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A=="
+ },
+ "d3-color": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.4.1.tgz",
+ "integrity": "sha512-p2sTHSLCJI2QKunbGb7ocOh7DgTAn8IrLx21QRc/BSnodXM4sv6aLQlnfpvehFMLZEfBc6g9pH9SWQccFYfJ9Q=="
+ },
+ "d3-format": {
+ "version": "1.4.5",
+ "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.4.5.tgz",
+ "integrity": "sha512-J0piedu6Z8iB6TbIGfZgDzfXxUFN3qQRMofy2oPdXzQibYGqPB/9iMcxr/TGalU+2RsyDO+U4f33id8tbnSRMQ=="
+ },
+ "d3-interpolate": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.4.0.tgz",
+ "integrity": "sha512-V9znK0zc3jOPV4VD2zZn0sDhZU3WAE2bmlxdIwwQPPzPjvyLkd8B3JUVdS1IDUFDkWZ72c9qnv1GK2ZagTZ8EA==",
+ "requires": {
+ "d3-color": "1"
+ }
+ },
+ "d3-path": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz",
+ "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg=="
+ },
+ "d3-scale": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-2.2.2.tgz",
+ "integrity": "sha512-LbeEvGgIb8UMcAa0EATLNX0lelKWGYDQiPdHj+gLblGVhGLyNbaCn3EvrJf0A3Y/uOOU5aD6MTh5ZFCdEwGiCw==",
+ "requires": {
+ "d3-array": "^1.2.0",
+ "d3-collection": "1",
+ "d3-format": "1",
+ "d3-interpolate": "1",
+ "d3-time": "1",
+ "d3-time-format": "2"
+ }
+ },
+ "d3-shape": {
+ "version": "1.3.7",
+ "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz",
+ "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==",
+ "requires": {
+ "d3-path": "1"
+ }
+ },
+ "d3-time": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-1.1.0.tgz",
+ "integrity": "sha512-Xh0isrZ5rPYYdqhAVk8VLnMEidhz5aP7htAADH6MfzgmmicPkTo8LhkLxci61/lCB7n7UmE3bN0leRt+qvkLxA=="
+ },
+ "d3-time-format": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-2.3.0.tgz",
+ "integrity": "sha512-guv6b2H37s2Uq/GefleCDtbe0XZAuy7Wa49VGkPVPMfLL9qObgBST3lEHJBMUp8S7NdLQAGIvr2KXk8Hc98iKQ==",
+ "requires": {
+ "d3-time": "1"
+ }
+ },
"damerau-levenshtein": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.6.tgz",
@@ -6394,6 +6461,11 @@
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
"integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA="
},
+ "decimal.js-light": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz",
+ "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg=="
+ },
"decode-uri-component": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
@@ -6726,6 +6798,14 @@
"utila": "~0.4"
}
},
+ "dom-helpers": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz",
+ "integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==",
+ "requires": {
+ "@babel/runtime": "^7.1.2"
+ }
+ },
"dom-serializer": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz",
@@ -12204,6 +12284,11 @@
"resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
"integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY="
},
+ "lodash.debounce": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
+ "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168="
+ },
"lodash.escape": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-4.0.1.tgz",
@@ -12248,6 +12333,11 @@
"lodash._reinterpolate": "^3.0.0"
}
},
+ "lodash.throttle": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
+ "integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ="
+ },
"lodash.uniq": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
@@ -12372,6 +12462,11 @@
"escape-string-regexp": "^4.0.0"
}
},
+ "math-expression-evaluator": {
+ "version": "1.2.22",
+ "resolved": "https://registry.npmjs.org/math-expression-evaluator/-/math-expression-evaluator-1.2.22.tgz",
+ "integrity": "sha512-L0j0tFVZBQQLeEjmWOvDLoRciIY8gQGWahvkztXUal8jH8R5Rlqo9GCvgqvXcy9LQhEWdQCVvzqAbxgYNt4blQ=="
+ },
"md5.js": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
@@ -16115,6 +16210,17 @@
"react-is": "^16.9.0"
}
},
+ "react-resize-detector": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/react-resize-detector/-/react-resize-detector-2.3.0.tgz",
+ "integrity": "sha512-oCAddEWWeFWYH5FAcHdBYcZjAw9fMzRUK9sWSx6WvSSOPVRxcHd5zTIGy/mOus+AhN/u6T4TMiWxvq79PywnJQ==",
+ "requires": {
+ "lodash.debounce": "^4.0.8",
+ "lodash.throttle": "^4.1.1",
+ "prop-types": "^15.6.0",
+ "resize-observer-polyfill": "^1.5.0"
+ }
+ },
"react-router": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-5.2.0.tgz",
@@ -16249,6 +16355,17 @@
}
}
},
+ "react-smooth": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-1.0.5.tgz",
+ "integrity": "sha512-eW057HT0lFgCKh8ilr0y2JaH2YbNcuEdFpxyg7Gf/qDKk9hqGMyXryZJ8iMGJEuKH0+wxS0ccSsBBB3W8yCn8w==",
+ "requires": {
+ "lodash": "~4.17.4",
+ "prop-types": "^15.6.0",
+ "raf": "^3.4.0",
+ "react-transition-group": "^2.5.0"
+ }
+ },
"react-test-renderer": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-16.13.1.tgz",
@@ -16261,6 +16378,17 @@
"scheduler": "^0.19.1"
}
},
+ "react-transition-group": {
+ "version": "2.9.0",
+ "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.9.0.tgz",
+ "integrity": "sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==",
+ "requires": {
+ "dom-helpers": "^3.4.0",
+ "loose-envify": "^1.4.0",
+ "prop-types": "^15.6.2",
+ "react-lifecycles-compat": "^3.0.4"
+ }
+ },
"read-config-file": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/read-config-file/-/read-config-file-6.0.0.tgz",
@@ -16333,6 +16461,39 @@
"util.promisify": "^1.0.0"
}
},
+ "recharts": {
+ "version": "1.8.5",
+ "resolved": "https://registry.npmjs.org/recharts/-/recharts-1.8.5.tgz",
+ "integrity": "sha512-tM9mprJbXVEBxjM7zHsIy6Cc41oO/pVYqyAsOHLxlJrbNBuLs0PHB3iys2M+RqCF0//k8nJtZF6X6swSkWY3tg==",
+ "requires": {
+ "classnames": "^2.2.5",
+ "core-js": "^2.6.10",
+ "d3-interpolate": "^1.3.0",
+ "d3-scale": "^2.1.0",
+ "d3-shape": "^1.2.0",
+ "lodash": "^4.17.5",
+ "prop-types": "^15.6.0",
+ "react-resize-detector": "^2.3.0",
+ "react-smooth": "^1.0.5",
+ "recharts-scale": "^0.4.2",
+ "reduce-css-calc": "^1.3.0"
+ },
+ "dependencies": {
+ "core-js": {
+ "version": "2.6.11",
+ "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz",
+ "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg=="
+ }
+ }
+ },
+ "recharts-scale": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.3.tgz",
+ "integrity": "sha512-t8p5sccG9Blm7c1JQK/ak9O8o95WGhNXD7TXg/BW5bYbVlr6eCeRBNpgyigD4p6pSSMehC5nSvBUPj6F68rbFA==",
+ "requires": {
+ "decimal.js-light": "^2.4.1"
+ }
+ },
"recursive-readdir": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz",
@@ -16350,6 +16511,31 @@
"strip-indent": "^1.0.1"
}
},
+ "reduce-css-calc": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz",
+ "integrity": "sha1-dHyRTgSWFKTJz7umKYca0dKSdxY=",
+ "requires": {
+ "balanced-match": "^0.4.2",
+ "math-expression-evaluator": "^1.2.14",
+ "reduce-function-call": "^1.0.1"
+ },
+ "dependencies": {
+ "balanced-match": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz",
+ "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg="
+ }
+ }
+ },
+ "reduce-function-call": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/reduce-function-call/-/reduce-function-call-1.0.3.tgz",
+ "integrity": "sha512-Hl/tuV2VDgWgCSEeWMLwxLZqX7OK59eU1guxXsRKTAyeYimivsKdtcV4fu3r710tpG5GmDKDhQ0HSZLExnNmyQ==",
+ "requires": {
+ "balanced-match": "^1.0.0"
+ }
+ },
"redux": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/redux/-/redux-4.0.5.tgz",
diff --git a/package.json b/package.json
index d5d51a3..51924e9 100644
--- a/package.json
+++ b/package.json
@@ -33,6 +33,7 @@
"react-redux": "^7.2.1",
"react-router-dom": "^5.2.0",
"react-scripts": "3.4.3",
+ "recharts": "^1.8.5",
"redux": "^4.0.5",
"redux-persist": "^6.0.0",
"redux-saga": "^1.1.3",
diff --git a/src/components/atoms/jobs-parts-graph/jobs-parts-graph.atom.jsx b/src/components/atoms/jobs-parts-graph/jobs-parts-graph.atom.jsx
new file mode 100644
index 0000000..0ee2684
--- /dev/null
+++ b/src/components/atoms/jobs-parts-graph/jobs-parts-graph.atom.jsx
@@ -0,0 +1,79 @@
+import { Skeleton, Typography } from "antd";
+import React, { useMemo } from "react";
+import { Cell, Pie, PieChart, ResponsiveContainer } from "recharts";
+import ErrorResultAtom from "../error-result/error-result.atom";
+import Dinero from "dinero.js";
+export default function JobPartsGraphAtom({
+ job,
+ loading,
+ price = "act_price",
+}) {
+ const data = useMemo(() => {
+ if (!job) return [];
+
+ const sums = job.joblines.reduce((acc, val) => {
+ if (!acc[val.part_type]) {
+ acc[val.part_type] = Dinero();
+ }
+
+ acc[val.part_type] = acc[val.part_type].add(
+ Dinero({ amount: Math.round((val[price] || 0) * 100) })
+ );
+
+ return acc;
+ }, {});
+
+ return Object.keys(sums).map((key) => {
+ return {
+ name: key,
+ value: sums[key].getAmount() / 100,
+ label: sums[key].toFormat(),
+ color: getColor(key),
+ };
+ });
+ }, [job, price]);
+
+ if (loading) return