From 0456543574ca7ac543d5714091fafb3522bb1e9d Mon Sep 17 00:00:00 2001 From: Patrick Fic Date: Wed, 14 Oct 2020 22:37:49 -0700 Subject: [PATCH] Added formatting for jobs lists and jobs detail components --- electron/decoder/decoder.js | 2 +- hasura/debug.log | 1 + .../down.yaml | 37 +++++ .../up.yaml | 37 +++++ hasura/migrations/metadata.yaml | 1 + package-lock.json | 51 +++++-- package.json | 4 + src/App/App.jsx | 1 + src/App/App.styles.scss | 52 +++++++ .../currency-formatter.atom.jsx | 6 + .../atoms/error-result/error-result.atom.jsx | 23 +++ .../time-ago-formatter.atom.jsx | 12 ++ .../jobs-detail-description.molecule.jsx | 31 ++++ .../jobs-lines-table.molecule.jsx | 73 ++++++++++ .../jobs-detail/jobs-detail.organism.jsx | 48 +++++++ .../jobs-table/jobs-table.organism.jsx | 135 ++++++++++++++++++ .../jobs-table.organism.styles.scss | 26 ++++ src/components/pages/jobs/jobs.page.jsx | 32 ++--- src/graphql/jobs.queries.js | 58 ++++++++ src/redux/application/application.actions.js | 6 + src/redux/application/application.reducer.js | 3 + .../application/application.selectors.js | 4 + src/redux/application/application.types.js | 1 + src/redux/root.reducer.js | 2 +- 24 files changed, 616 insertions(+), 30 deletions(-) create mode 100644 hasura/debug.log create mode 100644 hasura/migrations/1602731230140_update_permission_user_public_table_jobs/down.yaml create mode 100644 hasura/migrations/1602731230140_update_permission_user_public_table_jobs/up.yaml create mode 100644 src/components/atoms/currency-formatter/currency-formatter.atom.jsx create mode 100644 src/components/atoms/error-result/error-result.atom.jsx create mode 100644 src/components/atoms/time-ago-formatter/time-ago-formatter.atom.jsx create mode 100644 src/components/molecules/jobs-detail-description/jobs-detail-description.molecule.jsx create mode 100644 src/components/molecules/jobs-lines-table/jobs-lines-table.molecule.jsx create mode 100644 src/components/organisms/jobs-detail/jobs-detail.organism.jsx create mode 100644 src/components/organisms/jobs-table/jobs-table.organism.jsx create mode 100644 src/components/organisms/jobs-table/jobs-table.organism.styles.scss 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({