diff --git a/electron/decoder/decoder.js b/electron/decoder/decoder.js
index 2fa069e..4fe217a 100644
--- a/electron/decoder/decoder.js
+++ b/electron/decoder/decoder.js
@@ -170,7 +170,7 @@ async function DecodeVehFile(extensionlessFilePath) {
async function DecodeTtlFile(extensionlessFilePath) {
let dbf = await DBFFile.open(`${extensionlessFilePath}.TTL`);
let records = await dbf.readRecords(1);
- return _.pick(records[0], ["CLM_TOTAL"]);
+ return { clm_total: records[0]["G_TTL_AMT"] };
}
async function DecodeLinFile(extensionlessFilePath) {
diff --git a/hasura/debug.log b/hasura/debug.log
new file mode 100644
index 0000000..6f29fd0
--- /dev/null
+++ b/hasura/debug.log
@@ -0,0 +1 @@
+[1014/195617.530:ERROR:directory_reader_win.cc(43)] FindFirstFile: The system cannot find the path specified. (0x3)
diff --git a/hasura/migrations/1602731230140_update_permission_user_public_table_jobs/down.yaml b/hasura/migrations/1602731230140_update_permission_user_public_table_jobs/down.yaml
new file mode 100644
index 0000000..bd7ee76
--- /dev/null
+++ b/hasura/migrations/1602731230140_update_permission_user_public_table_jobs/down.yaml
@@ -0,0 +1,37 @@
+- args:
+ role: user
+ table:
+ name: jobs
+ schema: public
+ type: drop_select_permission
+- args:
+ permission:
+ allow_aggregations: false
+ columns:
+ - clm_total
+ - clm_no
+ - ins_co_nm
+ - ownr_fn
+ - ownr_ln
+ - ro_number
+ - v_makedesc
+ - v_model
+ - v_model_yr
+ - v_type
+ - v_vin
+ - created_at
+ - updated_at
+ - bodyshopid
+ - id
+ computed_fields: []
+ filter:
+ bodyshop:
+ associations:
+ user:
+ authid:
+ _eq: X-Hasura-User-Id
+ role: user
+ table:
+ name: jobs
+ schema: public
+ type: create_select_permission
diff --git a/hasura/migrations/1602731230140_update_permission_user_public_table_jobs/up.yaml b/hasura/migrations/1602731230140_update_permission_user_public_table_jobs/up.yaml
new file mode 100644
index 0000000..f390ffb
--- /dev/null
+++ b/hasura/migrations/1602731230140_update_permission_user_public_table_jobs/up.yaml
@@ -0,0 +1,37 @@
+- args:
+ role: user
+ table:
+ name: jobs
+ schema: public
+ type: drop_select_permission
+- args:
+ permission:
+ allow_aggregations: true
+ columns:
+ - clm_total
+ - clm_no
+ - ins_co_nm
+ - ownr_fn
+ - ownr_ln
+ - ro_number
+ - v_makedesc
+ - v_model
+ - v_model_yr
+ - v_type
+ - v_vin
+ - created_at
+ - updated_at
+ - bodyshopid
+ - id
+ computed_fields: []
+ filter:
+ bodyshop:
+ associations:
+ user:
+ authid:
+ _eq: X-Hasura-User-Id
+ role: user
+ table:
+ name: jobs
+ schema: public
+ type: create_select_permission
diff --git a/hasura/migrations/metadata.yaml b/hasura/migrations/metadata.yaml
index 9a1dd0c..d660583 100644
--- a/hasura/migrations/metadata.yaml
+++ b/hasura/migrations/metadata.yaml
@@ -195,6 +195,7 @@ tables:
user:
authid:
_eq: X-Hasura-User-Id
+ allow_aggregations: true
update_permissions:
- role: user
permission:
diff --git a/package-lock.json b/package-lock.json
index 294235e..b6a9e68 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -6787,6 +6787,11 @@
}
}
},
+ "dinero.js": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/dinero.js/-/dinero.js-1.8.1.tgz",
+ "integrity": "sha512-AQ09MDKonkGUrhBZZFx4tPTVcVJuHJ0VEA73LvcBoBB2eQSi1DbapeXj4wnUUpx1hVnPdyev1xPNnNMGy/Au0g=="
+ },
"dir-glob": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.0.0.tgz",
@@ -13426,6 +13431,17 @@
"prepend-http": "^1.0.0",
"query-string": "^4.1.0",
"sort-keys": "^1.0.0"
+ },
+ "dependencies": {
+ "query-string": {
+ "version": "4.3.4",
+ "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz",
+ "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=",
+ "requires": {
+ "object-assign": "^4.1.0",
+ "strict-uri-encode": "^1.0.0"
+ }
+ }
}
},
"prepend-http": {
@@ -13442,6 +13458,11 @@
"ajv-errors": "^1.0.0",
"ajv-keywords": "^3.1.0"
}
+ },
+ "strict-uri-encode": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz",
+ "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM="
}
}
},
@@ -16315,12 +16336,13 @@
"integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
},
"query-string": {
- "version": "4.3.4",
- "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz",
- "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=",
+ "version": "6.13.5",
+ "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.13.5.tgz",
+ "integrity": "sha512-svk3xg9qHR39P3JlHuD7g3nRnyay5mHbrPctEBDUxUkHRifPHXJDhBUycdCC0NBjXoDf44Gb+IsOZL1Uwn8M/Q==",
"requires": {
- "object-assign": "^4.1.0",
- "strict-uri-encode": "^1.0.0"
+ "decode-uri-component": "^0.2.0",
+ "split-on-first": "^1.0.0",
+ "strict-uri-encode": "^2.0.0"
}
},
"querystring": {
@@ -17130,6 +17152,14 @@
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.7.tgz",
"integrity": "sha512-TAv1KJFh3RhqxNvhzxj6LeT5NWklP6rDr2a0jaTfsZ5wSZWHOGeqQyejUp3xxLfPt2UpyJEcVQB/zyPcmonNFA=="
},
+ "react-infinite-scroller": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/react-infinite-scroller/-/react-infinite-scroller-1.2.4.tgz",
+ "integrity": "sha512-/oOa0QhZjXPqaD6sictN2edFMsd3kkMiE19Vcz5JDgHpzEJVqYcmq+V3mkwO88087kvKGe1URNksHEOt839Ubw==",
+ "requires": {
+ "prop-types": "^15.5.8"
+ }
+ },
"react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
@@ -18914,6 +18944,11 @@
"integrity": "sha1-mHbb0qFp0xFUAtSObqYynIgWpQ0=",
"dev": true
},
+ "split-on-first": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz",
+ "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw=="
+ },
"split-string": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
@@ -19059,9 +19094,9 @@
"integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ=="
},
"strict-uri-encode": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz",
- "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM="
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz",
+ "integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY="
},
"string-convert": {
"version": "0.2.1",
diff --git a/package.json b/package.json
index a823908..c9cce92 100644
--- a/package.json
+++ b/package.json
@@ -11,6 +11,7 @@
"apollo-link-logger": "^2.0.0",
"chokidar": "^3.4.3",
"dbffile": "^1.4.3",
+ "dinero.js": "^1.8.1",
"dotenv": "^8.2.0",
"electron-is-dev": "^1.2.0",
"electron-settings": "^4.0.2",
@@ -18,10 +19,13 @@
"firebase": "^7.23.0",
"graphql": "^15.3.0",
"lodash": "^4.17.20",
+ "moment": "^2.29.1",
"node-notifier": "^8.0.0",
"node-sass": "^4.14.1",
+ "query-string": "^6.13.5",
"react": "^16.13.1",
"react-dom": "^16.13.1",
+ "react-infinite-scroller": "^1.2.4",
"react-redux": "^7.2.1",
"react-router-dom": "^5.2.0",
"react-scripts": "3.4.3",
diff --git a/src/App/App.jsx b/src/App/App.jsx
index 837b21d..c624fb5 100644
--- a/src/App/App.jsx
+++ b/src/App/App.jsx
@@ -10,6 +10,7 @@ import client from "../graphql/GraphQLClient";
import "../ipc/ipc-renderer-handler";
import { checkUserSession } from "../redux/user/user.actions";
import { selectCurrentUser } from "../redux/user/user.selectors";
+import "./App.styles.scss";
const mapStateToProps = createStructuredSelector({
currentUser: selectCurrentUser,
diff --git a/src/App/App.styles.scss b/src/App/App.styles.scss
index e69de29..8b0e57c 100644
--- a/src/App/App.styles.scss
+++ b/src/App/App.styles.scss
@@ -0,0 +1,52 @@
+body {
+ height: 100%;
+ overflow: hidden;
+}
+
+.imex-flex-row {
+ display: flex;
+ justify-content: flex-start;
+ flex-wrap: wrap;
+ align-items: center;
+
+ &__grow {
+ flex: 1;
+ }
+
+ &__margin {
+ margin: 0.2rem 0.2rem;
+ }
+
+ &__margin-large {
+ margin: 0.5rem 0.5rem;
+ }
+
+ &__flex-space-around {
+ justify-content: space-around;
+ }
+}
+
+.ellipses {
+ display: inline-block; /* for em, a, span, etc (inline by default) */
+ text-overflow: ellipsis;
+ width: calc(95%);
+ 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 {
+ 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;
+}
diff --git a/src/components/atoms/currency-formatter/currency-formatter.atom.jsx b/src/components/atoms/currency-formatter/currency-formatter.atom.jsx
new file mode 100644
index 0000000..add5efc
--- /dev/null
+++ b/src/components/atoms/currency-formatter/currency-formatter.atom.jsx
@@ -0,0 +1,6 @@
+import React from "react";
+import Dinero from "dinero.js";
+export default function CurrencyFormatterAtom({ children, ...restProps }) {
+ const m = Dinero({ amount: Math.round((children || 0) * 100) });
+ return
{m.toFormat()}
;
+}
diff --git a/src/components/atoms/error-result/error-result.atom.jsx b/src/components/atoms/error-result/error-result.atom.jsx
new file mode 100644
index 0000000..8b170c2
--- /dev/null
+++ b/src/components/atoms/error-result/error-result.atom.jsx
@@ -0,0 +1,23 @@
+import { Button, Result } from "antd";
+import React from "react";
+
+export default function ErrorResultAtom({
+ title,
+ errorMessage,
+ tryAgainCallback,
+}) {
+ return (
+ tryAgainCallback()}>
+ Try Again
+
+ ) : null
+ }
+ />
+ );
+}
diff --git a/src/components/atoms/time-ago-formatter/time-ago-formatter.atom.jsx b/src/components/atoms/time-ago-formatter/time-ago-formatter.atom.jsx
new file mode 100644
index 0000000..87e874b
--- /dev/null
+++ b/src/components/atoms/time-ago-formatter/time-ago-formatter.atom.jsx
@@ -0,0 +1,12 @@
+import { Tooltip } from "antd";
+import moment from "moment";
+import React from "react";
+
+export default function TimeAgoFormatter(props) {
+ const m = moment(props.children);
+ return props.children ? (
+
+ {m.fromNow()}
+
+ ) : null;
+}
diff --git a/src/components/molecules/jobs-detail-description/jobs-detail-description.molecule.jsx b/src/components/molecules/jobs-detail-description/jobs-detail-description.molecule.jsx
new file mode 100644
index 0000000..bb0b16c
--- /dev/null
+++ b/src/components/molecules/jobs-detail-description/jobs-detail-description.molecule.jsx
@@ -0,0 +1,31 @@
+import { Descriptions, Skeleton } from "antd";
+import React from "react";
+import CurrencyFormatterAtom from "../../atoms/currency-formatter/currency-formatter.atom";
+import ErrorResultAtom from "../../atoms/error-result/error-result.atom";
+
+export default function JobsDetailDescriptionMolecule({ loading, job }) {
+ if (loading) return ;
+
+ if (!job) return ;
+
+ return (
+
+
+ {job.clm_no}
+
+ {job.ins_co_nm}
+
+ {`${job.ownr_fn} ${job.ownr_ln}`}
+ {`${job.v_model_yr} ${job.v_makedesc} ${job.v_model}`}
+
+ {job.clm_total}
+
+
+
+ );
+}
diff --git a/src/components/molecules/jobs-lines-table/jobs-lines-table.molecule.jsx b/src/components/molecules/jobs-lines-table/jobs-lines-table.molecule.jsx
new file mode 100644
index 0000000..6c59a12
--- /dev/null
+++ b/src/components/molecules/jobs-lines-table/jobs-lines-table.molecule.jsx
@@ -0,0 +1,73 @@
+import { Table } from "antd";
+import React from "react";
+import CurrencyFormatterAtom from "../../atoms/currency-formatter/currency-formatter.atom";
+
+export default function JobLinesTableMolecule({ loading, jobLines }) {
+ const columns = [
+ {
+ title: "#",
+ dataIndex: "unq_seq",
+ key: "unq_seq",
+ },
+ {
+ title: "S#",
+ dataIndex: "line_ind",
+ key: "line_ind",
+ },
+ {
+ title: "Line Description",
+ dataIndex: "line_desc",
+ key: "line_desc",
+ },
+ {
+ title: "Part Type",
+ dataIndex: "part_type",
+ key: "part_type",
+ },
+ {
+ title: "Part Number",
+ dataIndex: "oem_partno",
+ key: "oem_partno",
+ },
+ {
+ title: "Database Price",
+ dataIndex: "db_price",
+ key: "db_price",
+
+ render: (text, record) => (
+ {record.db_price}
+ ),
+ },
+ {
+ title: "Actual Price",
+ dataIndex: "act_price",
+ key: "act_price",
+
+ render: (text, record) => (
+ {record.act_price}
+ ),
+ },
+ {
+ title: "Qty.",
+ dataIndex: "part_qty",
+ key: "part_qty",
+ },
+ ];
+
+ return (
+
+ );
+}
diff --git a/src/components/organisms/jobs-detail/jobs-detail.organism.jsx b/src/components/organisms/jobs-detail/jobs-detail.organism.jsx
new file mode 100644
index 0000000..8e8fbc1
--- /dev/null
+++ b/src/components/organisms/jobs-detail/jobs-detail.organism.jsx
@@ -0,0 +1,48 @@
+import { useQuery } from "@apollo/client";
+import { Result } from "antd";
+import React from "react";
+import { connect } from "react-redux";
+import { createStructuredSelector } from "reselect";
+import { QUERY_JOB_BY_PK } from "../../../graphql/jobs.queries";
+import { selectSelectedJobId } from "../../../redux/application/application.selectors";
+import ErrorResultAtom from "../../atoms/error-result/error-result.atom";
+import JobsDetailDescriptionMolecule from "../../molecules/jobs-detail-description/jobs-detail-description.molecule";
+import JobsLinesTableMolecule from "../../molecules/jobs-lines-table/jobs-lines-table.molecule";
+
+const mapStateToProps = createStructuredSelector({
+ //currentUser: selectCurrentUser
+ selectedJobId: selectSelectedJobId,
+});
+const mapDispatchToProps = (dispatch) => ({
+ //setUserLanguage: language => dispatch(setUserLanguage(language))
+});
+
+export function JobsDetailOrganism({ selectedJobId }) {
+ const { loading, error, data } = useQuery(QUERY_JOB_BY_PK, {
+ variables: { jobId: selectedJobId },
+ skip: !selectedJobId,
+ });
+
+ if (!selectedJobId) return ;
+ if (error)
+ return (
+
+ );
+ return (
+
+
+
+ {selectedJobId}
+
+ );
+}
+export default connect(mapStateToProps, mapDispatchToProps)(JobsDetailOrganism);
diff --git a/src/components/organisms/jobs-table/jobs-table.organism.jsx b/src/components/organisms/jobs-table/jobs-table.organism.jsx
new file mode 100644
index 0000000..452667b
--- /dev/null
+++ b/src/components/organisms/jobs-table/jobs-table.organism.jsx
@@ -0,0 +1,135 @@
+import { useQuery } from "@apollo/client";
+import { List, Space, Spin, Typography } from "antd";
+import React, { useState } from "react";
+import InfiniteScroll from "react-infinite-scroller";
+import { connect } from "react-redux";
+import { createStructuredSelector } from "reselect";
+import { QUERY_ALL_JOBS_PAGINATED } from "../../../graphql/jobs.queries";
+import { setSelectedJobId } from "../../../redux/application/application.actions";
+import { selectSelectedJobId } from "../../../redux/application/application.selectors";
+import ErrorResultAtom from "../../atoms/error-result/error-result.atom";
+import TimeAgoFormatter from "../../atoms/time-ago-formatter/time-ago-formatter.atom";
+import "./jobs-table.organism.styles.scss";
+
+const mapStateToProps = createStructuredSelector({
+ selectedJobId: selectSelectedJobId,
+});
+const mapDispatchToProps = (dispatch) => ({
+ setSelectedJobId: (jobId) => dispatch(setSelectedJobId(jobId)),
+});
+
+const limit = 20;
+export function JobsTableOrganism({ selectedJobId, setSelectedJobId }) {
+ const [state, setState] = useState({ hasMore: true });
+
+ const { loading, error, data, fetchMore } = useQuery(
+ QUERY_ALL_JOBS_PAGINATED,
+ {
+ variables: {
+ offset: 0,
+ limit: limit,
+ order: [{ updated_at: "desc" }],
+ },
+ }
+ );
+
+ const handleInfiniteOnLoad = (page) => {
+ fetchMore({
+ variables: {
+ offset: limit * page,
+ },
+ updateQuery: (prev, { fetchMoreResult }) => {
+ if (!fetchMoreResult) {
+ console.log("No more results. Fetch More was empty.");
+ setState({ ...state, hasMore: false });
+ return prev;
+ }
+
+ const newCache = Object.assign({}, prev, {
+ jobs: [...prev.jobs, ...fetchMoreResult.jobs],
+ });
+
+ if (
+ newCache.jobs.length >= data &&
+ data.jobs_aggregate.aggregate.count
+ ) {
+ console.log("No more results.");
+ setState({ ...state, hasMore: false });
+ }
+
+ return newCache;
+ },
+ });
+ };
+
+ const handleSelect = (jobId) => {
+ setSelectedJobId(jobId);
+ };
+
+ if (error)
+ return (
+
+ );
+
+ return (
+
+
+
+ (
+ handleSelect(item.id)}
+ >
+
+
+
+ {`${item.clm_no}${
+ item.ins_co_nm ? ` | ${item.ins_co_nm}` : ""
+ }`}
+
+
+ {item.updated_at}
+
+
+
+ {`${item.ownr_fn} ${item.ownr_ln}`}
+
+ {`${item.v_model_yr} ${item.v_makedesc} ${item.v_model} ${item.v_vin}`}
+
+
+
+
+ )}
+ >
+ {loading && state.hasMore && (
+
+
+
+ )}
+
+
+
+ {`${data ? data.jobs.length : 0} jobs loaded. ${
+ data ? data.jobs_aggregate.aggregate.count : 0
+ } total jobs.`}
+
+ );
+}
+export default connect(mapStateToProps, mapDispatchToProps)(JobsTableOrganism);
diff --git a/src/components/organisms/jobs-table/jobs-table.organism.styles.scss b/src/components/organisms/jobs-table/jobs-table.organism.styles.scss
new file mode 100644
index 0000000..aefdb88
--- /dev/null
+++ b/src/components/organisms/jobs-table/jobs-table.organism.styles.scss
@@ -0,0 +1,26 @@
+.jobs-list-infinite-container {
+ overflow-y: auto;
+ overflow-x: hidden;
+ height: 95vh;
+}
+
+.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;
+ }
+}
diff --git a/src/components/pages/jobs/jobs.page.jsx b/src/components/pages/jobs/jobs.page.jsx
index ccb9c2e..2fde400 100644
--- a/src/components/pages/jobs/jobs.page.jsx
+++ b/src/components/pages/jobs/jobs.page.jsx
@@ -1,33 +1,25 @@
-import { Button } from "antd";
+import { Col, Row } from "antd";
import React from "react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
-
-const { ipcRenderer } = window;
-//const settings = window.require("electron-settings");
+import JobsTableOrganism from "../../organisms/jobs-table/jobs-table.organism";
+import JobsDetailOrganism from "../../organisms/jobs-detail/jobs-detail.organism";
const mapStateToProps = createStructuredSelector({});
const mapDispatchToProps = (dispatch) => ({});
export function JobsPage() {
- // useEffect(() => {
- // ipcRenderer.on("test-success", (event, obj) => {
- // console.log("Test Success", obj);
- // });
-
- // // Cleanup the listener events so that memory leaks are avoided.
- // return function cleanup() {
- // ipcRenderer.removeAllListeners(
- // "test-success",
- // ipcTypes.default.filewatcher.startSuccess
- // );
- // };
- // }, []);
-
return (
-
Welcome to your new react app.
-
+
+
+
+
+
+
+
+
+
);
}
diff --git a/src/graphql/jobs.queries.js b/src/graphql/jobs.queries.js
index dc10dd7..5f3975e 100644
--- a/src/graphql/jobs.queries.js
+++ b/src/graphql/jobs.queries.js
@@ -24,3 +24,61 @@ export const INSERT_NEW_JOB = gql`
// v_type
// ]
// }
+
+export const QUERY_ALL_JOBS_PAGINATED = gql`
+ query QUERY_ALL_JOBS_PAGINATED(
+ $offset: Int
+ $limit: Int
+ $order: [jobs_order_by!]
+ ) {
+ jobs(offset: $offset, limit: $limit, order_by: $order) {
+ ownr_fn
+ ownr_ln
+ v_vin
+ v_model_yr
+ v_model
+ v_makedesc
+ id
+ ins_co_nm
+ clm_no
+ clm_total
+ ro_number
+ updated_at
+ }
+ jobs_aggregate {
+ aggregate {
+ count(distinct: true)
+ }
+ }
+ }
+`;
+
+export const QUERY_JOB_BY_PK = gql`
+ query QUERY_ALL_JOBS_PAGINATED($jobId: uuid!) {
+ jobs_by_pk(id: $jobId) {
+ ownr_fn
+ ownr_ln
+ v_vin
+ v_model_yr
+ v_model
+ v_makedesc
+ id
+ ins_co_nm
+ clm_no
+ clm_total
+ ro_number
+ updated_at
+ joblines(order_by: { unq_seq: asc }) {
+ id
+ act_price
+ db_price
+ line_desc
+ line_ind
+ oem_partno
+ part_qty
+ part_type
+ unq_seq
+ }
+ }
+ }
+`;
diff --git a/src/redux/application/application.actions.js b/src/redux/application/application.actions.js
index e718d87..a5f9bfa 100644
--- a/src/redux/application/application.actions.js
+++ b/src/redux/application/application.actions.js
@@ -19,7 +19,13 @@ export const setWatcherStatus = (status) => ({
type: ApplicationActionTypes.SET_WATCHER_STATUS,
payload: status,
});
+
export const setWatcherError = (error) => ({
type: ApplicationActionTypes.SET_WATCHER_ERROR,
payload: error,
});
+
+export const setSelectedJobId = (jobId) => ({
+ type: ApplicationActionTypes.SET_SELECTED_JOB_ID,
+ payload: jobId,
+});
diff --git a/src/redux/application/application.reducer.js b/src/redux/application/application.reducer.js
index fb187e3..c29a546 100644
--- a/src/redux/application/application.reducer.js
+++ b/src/redux/application/application.reducer.js
@@ -3,6 +3,7 @@ const INITIAL_STATE = {
watcherStatus: "Not Started",
watchedPaths: [],
watcherError: null,
+ selectedJobId: null,
};
const applicationReducer = (state = INITIAL_STATE, action) => {
@@ -32,6 +33,8 @@ const applicationReducer = (state = INITIAL_STATE, action) => {
...state,
watcherError: action.payload,
};
+ case ApplicationActionTypes.SET_SELECTED_JOB_ID:
+ return { ...state, selectedJobId: action.payload };
default:
return state;
}
diff --git a/src/redux/application/application.selectors.js b/src/redux/application/application.selectors.js
index ce24fc7..8fcd74e 100644
--- a/src/redux/application/application.selectors.js
+++ b/src/redux/application/application.selectors.js
@@ -16,3 +16,7 @@ export const selectWatcherError = createSelector(
[selectApplication],
(application) => application.watcherError
);
+export const selectSelectedJobId = createSelector(
+ [selectApplication],
+ (application) => application.selectedJobId
+);
diff --git a/src/redux/application/application.types.js b/src/redux/application/application.types.js
index 847e8f5..72b30fc 100644
--- a/src/redux/application/application.types.js
+++ b/src/redux/application/application.types.js
@@ -4,5 +4,6 @@ const ApplicationActionTypes = {
REMOVE_WATCHED_PATH: "REMOVE_WATCHED_PATH",
SET_WATCHER_STATUS: "SET_WATCHER_STATUS",
SET_WATCHER_ERROR: "SET_WATCHER_ERROR",
+ SET_SELECTED_JOB_ID: "SET_SELECTED_JOB_ID",
};
export default ApplicationActionTypes;
diff --git a/src/redux/root.reducer.js b/src/redux/root.reducer.js
index 067e904..902b0fd 100644
--- a/src/redux/root.reducer.js
+++ b/src/redux/root.reducer.js
@@ -7,7 +7,7 @@ import userReducer from "./user/user.reducer";
const persistConfig = {
key: "root",
storage,
- blacklist: ["application"],
+ blacklist: ["application", "user"],
};
const rootReducer = combineReducers({