Merge branch 'master' into feature/IO-1366-Audit-Logging
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
This commit is contained in:
@@ -42,31 +42,22 @@ jobs:
|
|||||||
app-build:
|
app-build:
|
||||||
docker:
|
docker:
|
||||||
- image: cimg/node:16.15.0
|
- image: cimg/node:16.15.0
|
||||||
|
resource_class: large
|
||||||
working_directory: ~/repo/client
|
working_directory: ~/repo/client
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- checkout:
|
- checkout:
|
||||||
path: ~/repo
|
path: ~/repo
|
||||||
|
|
||||||
- restore_cache:
|
|
||||||
name: Restore Yarn Package Cache
|
|
||||||
keys:
|
|
||||||
- yarn-packages-{{ checksum "yarn.lock" }}
|
|
||||||
- run:
|
- run:
|
||||||
name: Install Dependencies
|
name: Install Dependencies
|
||||||
command: yarn install --frozen-lockfile --cache-folder ~/.cache/yarn
|
command: npm i
|
||||||
- save_cache:
|
|
||||||
name: Save Yarn Package Cache
|
|
||||||
key: yarn-packages-{{ checksum "yarn.lock" }}
|
|
||||||
paths:
|
|
||||||
- ~/.cache/yarn
|
|
||||||
|
|
||||||
- run: yarn run build
|
- run: npm run build
|
||||||
|
|
||||||
- aws-s3/sync:
|
- aws-s3/sync:
|
||||||
from: build
|
from: build
|
||||||
to: "s3://imex-online-production/"
|
to: "s3://imex-online-production/"
|
||||||
|
arguments: "--exclude '*.map'"
|
||||||
- jira/notify
|
- jira/notify
|
||||||
|
|
||||||
test-hasura-migrate:
|
test-hasura-migrate:
|
||||||
@@ -92,31 +83,22 @@ jobs:
|
|||||||
test-app-build:
|
test-app-build:
|
||||||
docker:
|
docker:
|
||||||
- image: cimg/node:16.15.0
|
- image: cimg/node:16.15.0
|
||||||
|
resource_class: large
|
||||||
working_directory: ~/repo/client
|
working_directory: ~/repo/client
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- checkout:
|
- checkout:
|
||||||
path: ~/repo
|
path: ~/repo
|
||||||
|
|
||||||
- restore_cache:
|
|
||||||
name: Restore Yarn Package Cache
|
|
||||||
keys:
|
|
||||||
- yarn-packages-{{ checksum "yarn.lock" }}
|
|
||||||
- run:
|
- run:
|
||||||
name: Install Dependencies
|
name: Install Dependencies
|
||||||
command: yarn install --frozen-lockfile --cache-folder ~/.cache/yarn
|
command: npm i
|
||||||
- save_cache:
|
|
||||||
name: Save Yarn Package Cache
|
|
||||||
key: yarn-packages-{{ checksum "yarn.lock" }}
|
|
||||||
paths:
|
|
||||||
- ~/.cache/yarn
|
|
||||||
|
|
||||||
- run: yarn run build:test
|
- run: npm run build:test
|
||||||
|
|
||||||
- aws-s3/sync:
|
- aws-s3/sync:
|
||||||
from: build
|
from: build
|
||||||
to: "s3://imex-online-test/"
|
to: "s3://imex-online-test/"
|
||||||
|
arguments: "--exclude '*.map'"
|
||||||
- jira/notify
|
- jira/notify
|
||||||
|
|
||||||
admin-app-build:
|
admin-app-build:
|
||||||
@@ -177,4 +159,4 @@ workflows:
|
|||||||
#- admin-app-build:
|
#- admin-app-build:
|
||||||
#filters:
|
#filters:
|
||||||
#branches:
|
#branches:
|
||||||
#only: master
|
#only: master
|
||||||
|
|||||||
167
_reference/reportFiltersAndSorters.md
Normal file
167
_reference/reportFiltersAndSorters.md
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
# Filters and Sorters
|
||||||
|
|
||||||
|
This documentation details the schema required for `.filters` files on the report server. It is used to dynamically
|
||||||
|
modify the graphQL query and provide the user more power over their reports.
|
||||||
|
|
||||||
|
# Special Notes
|
||||||
|
- When passing the data to the template server, the property filters and sorters is added to the data object and will reflect the filters and sorters the user has selected
|
||||||
|
|
||||||
|
## High level Schema Overview
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const schema = {
|
||||||
|
"filters": [
|
||||||
|
{
|
||||||
|
"name": "jobs.joblines.mod_lb_hrs", // Name and path of the field in the graphQL query
|
||||||
|
"translation": "jobs.joblines.mod_lb_hrs_1", // Translation key for the label used in the GUI
|
||||||
|
"label": "mod_lb_hrs_1", // Label used in the case the GUI does not contain a translation
|
||||||
|
"type": "number" // Type of field, can be number or string currently
|
||||||
|
},
|
||||||
|
// ... more filters
|
||||||
|
],
|
||||||
|
"sorters": [
|
||||||
|
{
|
||||||
|
"name": "jobs.joblines.mod_lb_hrs", // Name and path of the field in the graphQL query
|
||||||
|
"translation": "jobs.joblines.mod_lb_hrs_1", // Translation key for the label used in the GUI
|
||||||
|
"label": "mod_lb_hrs_1", // Label used in the case the GUI does not contain a translation
|
||||||
|
"type": "number" // Type of field, can be number or string currently
|
||||||
|
},
|
||||||
|
// ... more sorters
|
||||||
|
],
|
||||||
|
"dates": {
|
||||||
|
// This is not yet implemented and will be added in a future release
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Filters
|
||||||
|
|
||||||
|
Filters effect the where clause of the graphQL query. They are used to filter the data returned from the server.
|
||||||
|
A note on special notation used in the `name` field.
|
||||||
|
|
||||||
|
## Reflection
|
||||||
|
Filters can make use of reflection to pre-fill select boxes, the following is an example of that in the filters file.
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"name": "jobs.status",
|
||||||
|
"translation": "jobs.fields.status",
|
||||||
|
"label": "Status",
|
||||||
|
"type": "string",
|
||||||
|
"reflector": {
|
||||||
|
"type": "internal",
|
||||||
|
"name": "special.job_statuses"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
```
|
||||||
|
|
||||||
|
in this example, a reflector with the type 'internal' (all types at the moment require this, and it is used for future functionality), with a name of `special.job_statuses`
|
||||||
|
|
||||||
|
The following cases are available
|
||||||
|
|
||||||
|
- `special.job_statuses` - This will reflect the statuses of the jobs table `bodyshop.md_ro_statuses.statuses'`
|
||||||
|
- `special.cost_centers` - This will reflect the cost centers `bodyshop.md_responsibility_centers.costs`
|
||||||
|
- `special.categories` - This will reflect the categories `bodyshop.md_categories`
|
||||||
|
- `special.insurance_companies` - This will reflect the insurance companies `bodyshop.md_ins_cos`'
|
||||||
|
- `special.employee_teams` - This will reflect the employee teams `bodyshop.employee_teams`
|
||||||
|
- `special.employees` - This will reflect the employees `bodyshop.employees`
|
||||||
|
- `special.first_names` - This will reflect the first names `bodyshop.employees`
|
||||||
|
- `special.last_names` - This will reflect the last names `bodyshop.employees`
|
||||||
|
-
|
||||||
|
### Path without brackets, multi level
|
||||||
|
|
||||||
|
`"name": "jobs.joblines.mod_lb_hrs",`
|
||||||
|
This will produce a where clause at the `joblines` level of the graphQL query,
|
||||||
|
|
||||||
|
```graphql
|
||||||
|
query gendoc_hours_sold_detail_open($starttz: timestamptz!, $endtz: timestamptz!) {
|
||||||
|
jobs(
|
||||||
|
where: {date_invoiced: {_is_null: true}, date_open: {_gte: $starttz, _lte: $endtz}, ro_number: {_is_null: false}, voided: {_eq: false}}
|
||||||
|
) {
|
||||||
|
joblines(
|
||||||
|
order_by: {line_no: asc}
|
||||||
|
where: {removed: {_eq: false}, mod_lb_hrs: {_lt: 3}}
|
||||||
|
) {
|
||||||
|
line_no
|
||||||
|
mod_lbr_ty
|
||||||
|
mod_lb_hrs
|
||||||
|
convertedtolbr
|
||||||
|
convertedtolbr_data
|
||||||
|
}
|
||||||
|
ownr_co_nm
|
||||||
|
ownr_fn
|
||||||
|
ownr_ln
|
||||||
|
plate_no
|
||||||
|
ro_number
|
||||||
|
status
|
||||||
|
v_make_desc
|
||||||
|
v_model_desc
|
||||||
|
v_model_yr
|
||||||
|
v_vin
|
||||||
|
v_color
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Path with brackets,top level
|
||||||
|
`"name": "[jobs].joblines.mod_lb_hrs",`
|
||||||
|
This will produce a where clause at the `jobs` level of the graphQL query.
|
||||||
|
|
||||||
|
```graphql
|
||||||
|
query gendoc_hours_sold_detail_open($starttz: timestamptz!, $endtz: timestamptz!) {
|
||||||
|
jobs(
|
||||||
|
where: {date_invoiced: {_is_null: true}, date_open: {_gte: $starttz, _lte: $endtz}, ro_number: {_is_null: false}, voided: {_eq: false}, joblines: {mod_lb_hrs: {_gt: 4}}}
|
||||||
|
) {
|
||||||
|
joblines(
|
||||||
|
order_by: {line_no: asc}
|
||||||
|
where: {removed: {_eq: false}}
|
||||||
|
) {
|
||||||
|
line_no
|
||||||
|
mod_lbr_ty
|
||||||
|
mod_lb_hrs
|
||||||
|
convertedtolbr
|
||||||
|
convertedtolbr_data
|
||||||
|
}
|
||||||
|
ownr_co_nm
|
||||||
|
ownr_fn
|
||||||
|
ownr_ln
|
||||||
|
plate_no
|
||||||
|
ro_number
|
||||||
|
status
|
||||||
|
v_make_desc
|
||||||
|
v_model_desc
|
||||||
|
v_model_yr
|
||||||
|
v_vin
|
||||||
|
v_color
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Known Caveats
|
||||||
|
- Will only support two level of nesting in the graphQL query `jobs.joblines.mod_lb_hrs` vs `[jobs].joblines.mod_lb_hrs` is fine, but `jobs.[joblines.].some_table.mod_lb_hrs` is not.
|
||||||
|
- The `dates` object is not yet implemented and will be added in a future release.
|
||||||
|
- The type object must be 'string' or 'number' and is case-sensitive.
|
||||||
|
- The `translation` key is used to look up the label in the GUI, if it is not found, the `label` key is used.
|
||||||
|
- Do not add the ability to filter things that are already filtered as part of the original query, this would be redundant and could cause issues.
|
||||||
|
- Do not add the ability to filter on things like FK constraints, must like the above example.
|
||||||
|
|
||||||
|
## Sorters
|
||||||
|
- Sorters follow the same schema as filters, however, they do not do square bracket wrapping to indicate level hoisting, a filter added on `job.md_status` would be added at the top level, and a filter added on `jobs.joblines.mod_lb_hrs` would be added at the `joblines` level.
|
||||||
|
- Most of the reports currently do sorting on a template level, this will need to change to actually see the results using the sorters.
|
||||||
|
|
||||||
|
### Default Sorters
|
||||||
|
- A sorter can be given a default object containing a `order` and `direction` key value. This will be used to sort the report if the user does not select any of the sorters themselves.
|
||||||
|
- The `order` key is the order in which the sorters are applied, and the `direction` key is the direction of the sort, either `asc` or `desc`.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "jobs.joblines.mod_lb_hrs",
|
||||||
|
"translation": "jobs.joblines.mod_lb_hrs_1",
|
||||||
|
"label": "mod_lb_hrs_1",
|
||||||
|
"type": "number",
|
||||||
|
"default": {
|
||||||
|
"order": 1,
|
||||||
|
"direction": "asc"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
GENERATE_SOURCEMAP=false
|
GENERATE_SOURCEMAP=true
|
||||||
REACT_APP_GRAPHQL_ENDPOINT=https://db.imex.online/v1/graphql
|
REACT_APP_GRAPHQL_ENDPOINT=https://db.imex.online/v1/graphql
|
||||||
REACT_APP_GRAPHQL_ENDPOINT_WS=wss://db.imex.online/v1/graphql
|
REACT_APP_GRAPHQL_ENDPOINT_WS=wss://db.imex.online/v1/graphql
|
||||||
REACT_APP_GA_CODE=231103507
|
REACT_APP_GA_CODE=231103507
|
||||||
|
|||||||
3
client/.gitignore
vendored
Normal file
3
client/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
|
||||||
|
# Sentry Config File
|
||||||
|
.sentryclirc
|
||||||
@@ -1,25 +1,25 @@
|
|||||||
// craco.config.js
|
// craco.config.js
|
||||||
const TerserPlugin = require("terser-webpack-plugin");
|
const TerserPlugin = require("terser-webpack-plugin");
|
||||||
const CracoLessPlugin = require("craco-less");
|
const CracoLessPlugin = require("craco-less");
|
||||||
const SentryWebpackPlugin = require("@sentry/webpack-plugin");
|
//const SentryWebpackPlugin = require("@sentry/webpack-plugin");
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
plugins: [
|
plugins: [
|
||||||
{
|
// {
|
||||||
plugin: SentryWebpackPlugin,
|
// plugin: SentryWebpackPlugin,
|
||||||
options: {
|
// options: {
|
||||||
// sentry-cli configuration
|
// // sentry-cli configuration
|
||||||
authToken:
|
// authToken:
|
||||||
"6b45b028a02342db97a9a2f92c0959058665443d379d4a3a876430009e744260",
|
// "6b45b028a02342db97a9a2f92c0959058665443d379d4a3a876430009e744260",
|
||||||
org: "snapt-software",
|
// org: "snapt-software",
|
||||||
project: "imexonline",
|
// project: "imexonline",
|
||||||
release: process.env.REACT_APP_GIT_SHA,
|
// release: process.env.REACT_APP_GIT_SHA,
|
||||||
|
|
||||||
// webpack-specific configuration
|
// // webpack-specific configuration
|
||||||
include: ".",
|
// include: ".",
|
||||||
ignore: ["node_modules", "webpack.config.js"],
|
// ignore: ["node_modules", "webpack.config.js"],
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
{
|
{
|
||||||
plugin: CracoLessPlugin,
|
plugin: CracoLessPlugin,
|
||||||
options: {
|
options: {
|
||||||
|
|||||||
649
client/package-lock.json
generated
649
client/package-lock.json
generated
@@ -13,12 +13,14 @@
|
|||||||
"@craco/craco": "^7.0.0",
|
"@craco/craco": "^7.0.0",
|
||||||
"@fingerprintjs/fingerprintjs": "^3.4.2",
|
"@fingerprintjs/fingerprintjs": "^3.4.2",
|
||||||
"@jsreport/browser-client": "^3.1.0",
|
"@jsreport/browser-client": "^3.1.0",
|
||||||
"@sentry/react": "^7.40.0",
|
"@sentry/cli": "^2.27.0",
|
||||||
|
"@sentry/react": "^7.99.0",
|
||||||
"@sentry/tracing": "^7.40.0",
|
"@sentry/tracing": "^7.40.0",
|
||||||
"@splitsoftware/splitio-react": "^1.8.1",
|
"@splitsoftware/splitio-react": "^1.8.1",
|
||||||
"@tanem/react-nprogress": "^5.0.8",
|
"@tanem/react-nprogress": "^5.0.8",
|
||||||
"antd": "^4.24.8",
|
"antd": "^4.24.8",
|
||||||
"apollo-link-logger": "^2.0.1",
|
"apollo-link-logger": "^2.0.1",
|
||||||
|
"apollo-link-sentry": "^3.3.0",
|
||||||
"axios": "^1.3.4",
|
"axios": "^1.3.4",
|
||||||
"craco-less": "^2.0.0",
|
"craco-less": "^2.0.0",
|
||||||
"dinero.js": "^1.9.1",
|
"dinero.js": "^1.9.1",
|
||||||
@@ -4214,40 +4216,195 @@
|
|||||||
"integrity": "sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg==",
|
"integrity": "sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@sentry/browser": {
|
"node_modules/@sentry-internal/feedback": {
|
||||||
"version": "7.40.0",
|
"version": "7.99.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-7.40.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-7.99.0.tgz",
|
||||||
"integrity": "sha512-07rZ+cTcpmYB1r84/oZtmSPJJvLCxW8yIh/5s4MdKRyZpqIDKhOz6cCS/4j+l1V+MeLcNLZBjFtNdKA2eocTpg==",
|
"integrity": "sha512-exIO1o+bE0MW4z30FxC0cYzJ4ZHSMlDPMHCBDPzU+MWGQc/fb8s58QUrx5Dnm6HTh9G3H+YlroCxIo9u0GSwGQ==",
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sentry/core": "7.40.0",
|
"@sentry/core": "7.99.0",
|
||||||
"@sentry/replay": "7.40.0",
|
"@sentry/types": "7.99.0",
|
||||||
"@sentry/types": "7.40.0",
|
"@sentry/utils": "7.99.0"
|
||||||
"@sentry/utils": "7.40.0",
|
},
|
||||||
"tslib": "^1.9.3"
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@sentry-internal/feedback/node_modules/@sentry/core": {
|
||||||
|
"version": "7.99.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.99.0.tgz",
|
||||||
|
"integrity": "sha512-vOAtzcAXEUtS/oW7wi3wMkZ3hsb5Ch96gKyrrj/mXdOp2zrcwdNV6N9/pawq2E9P/7Pw8AXw4CeDZztZrjQLuA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@sentry/types": "7.99.0",
|
||||||
|
"@sentry/utils": "7.99.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sentry/browser/node_modules/tslib": {
|
"node_modules/@sentry-internal/feedback/node_modules/@sentry/types": {
|
||||||
"version": "1.14.1",
|
"version": "7.99.0",
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.99.0.tgz",
|
||||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
|
"integrity": "sha512-94qwOw4w40sAs5mCmzcGyj8ZUu/KhnWnuMZARRq96k+SjRW/tHFAOlIdnFSrt3BLPvSOK7R3bVAskZQ0N4FTmA==",
|
||||||
"license": "0BSD"
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@sentry-internal/feedback/node_modules/@sentry/utils": {
|
||||||
|
"version": "7.99.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.99.0.tgz",
|
||||||
|
"integrity": "sha512-cYZy5WNTkWs5GgggGnjfGqC44CWir0pAv4GVVSx0fsup4D4pMKBJPrtub15f9uC+QkUf3vVkqwpBqeFxtmJQTQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@sentry/types": "7.99.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@sentry-internal/replay-canvas": {
|
||||||
|
"version": "7.99.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-7.99.0.tgz",
|
||||||
|
"integrity": "sha512-PoIkfusToDq0snfl2M6HJx/1KJYtXxYhQplrn11kYadO04SdG0XGXf4h7wBTMEQ7LDEAtQyvsOu4nEQtTO3YjQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@sentry/core": "7.99.0",
|
||||||
|
"@sentry/replay": "7.99.0",
|
||||||
|
"@sentry/types": "7.99.0",
|
||||||
|
"@sentry/utils": "7.99.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@sentry-internal/replay-canvas/node_modules/@sentry/core": {
|
||||||
|
"version": "7.99.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.99.0.tgz",
|
||||||
|
"integrity": "sha512-vOAtzcAXEUtS/oW7wi3wMkZ3hsb5Ch96gKyrrj/mXdOp2zrcwdNV6N9/pawq2E9P/7Pw8AXw4CeDZztZrjQLuA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@sentry/types": "7.99.0",
|
||||||
|
"@sentry/utils": "7.99.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@sentry-internal/replay-canvas/node_modules/@sentry/types": {
|
||||||
|
"version": "7.99.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.99.0.tgz",
|
||||||
|
"integrity": "sha512-94qwOw4w40sAs5mCmzcGyj8ZUu/KhnWnuMZARRq96k+SjRW/tHFAOlIdnFSrt3BLPvSOK7R3bVAskZQ0N4FTmA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@sentry-internal/replay-canvas/node_modules/@sentry/utils": {
|
||||||
|
"version": "7.99.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.99.0.tgz",
|
||||||
|
"integrity": "sha512-cYZy5WNTkWs5GgggGnjfGqC44CWir0pAv4GVVSx0fsup4D4pMKBJPrtub15f9uC+QkUf3vVkqwpBqeFxtmJQTQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@sentry/types": "7.99.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@sentry-internal/tracing": {
|
||||||
|
"version": "7.99.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.99.0.tgz",
|
||||||
|
"integrity": "sha512-z3JQhHjoM1KdM20qrHwRClKJrNLr2CcKtCluq7xevLtXHJWNAQQbafnWD+Aoj85EWXBzKt9yJMv2ltcXJ+at+w==",
|
||||||
|
"dependencies": {
|
||||||
|
"@sentry/core": "7.99.0",
|
||||||
|
"@sentry/types": "7.99.0",
|
||||||
|
"@sentry/utils": "7.99.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@sentry-internal/tracing/node_modules/@sentry/core": {
|
||||||
|
"version": "7.99.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.99.0.tgz",
|
||||||
|
"integrity": "sha512-vOAtzcAXEUtS/oW7wi3wMkZ3hsb5Ch96gKyrrj/mXdOp2zrcwdNV6N9/pawq2E9P/7Pw8AXw4CeDZztZrjQLuA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@sentry/types": "7.99.0",
|
||||||
|
"@sentry/utils": "7.99.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@sentry-internal/tracing/node_modules/@sentry/types": {
|
||||||
|
"version": "7.99.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.99.0.tgz",
|
||||||
|
"integrity": "sha512-94qwOw4w40sAs5mCmzcGyj8ZUu/KhnWnuMZARRq96k+SjRW/tHFAOlIdnFSrt3BLPvSOK7R3bVAskZQ0N4FTmA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@sentry-internal/tracing/node_modules/@sentry/utils": {
|
||||||
|
"version": "7.99.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.99.0.tgz",
|
||||||
|
"integrity": "sha512-cYZy5WNTkWs5GgggGnjfGqC44CWir0pAv4GVVSx0fsup4D4pMKBJPrtub15f9uC+QkUf3vVkqwpBqeFxtmJQTQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@sentry/types": "7.99.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@sentry/browser": {
|
||||||
|
"version": "7.99.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-7.99.0.tgz",
|
||||||
|
"integrity": "sha512-bgfoUv3wkwwLgN5YUOe0ibB3y268ZCnamZh6nLFqnY/UBKC1+FXWFdvzVON/XKUm62LF8wlpCybOf08ebNj2yg==",
|
||||||
|
"dependencies": {
|
||||||
|
"@sentry-internal/feedback": "7.99.0",
|
||||||
|
"@sentry-internal/replay-canvas": "7.99.0",
|
||||||
|
"@sentry-internal/tracing": "7.99.0",
|
||||||
|
"@sentry/core": "7.99.0",
|
||||||
|
"@sentry/replay": "7.99.0",
|
||||||
|
"@sentry/types": "7.99.0",
|
||||||
|
"@sentry/utils": "7.99.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@sentry/browser/node_modules/@sentry/core": {
|
||||||
|
"version": "7.99.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.99.0.tgz",
|
||||||
|
"integrity": "sha512-vOAtzcAXEUtS/oW7wi3wMkZ3hsb5Ch96gKyrrj/mXdOp2zrcwdNV6N9/pawq2E9P/7Pw8AXw4CeDZztZrjQLuA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@sentry/types": "7.99.0",
|
||||||
|
"@sentry/utils": "7.99.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@sentry/browser/node_modules/@sentry/types": {
|
||||||
|
"version": "7.99.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.99.0.tgz",
|
||||||
|
"integrity": "sha512-94qwOw4w40sAs5mCmzcGyj8ZUu/KhnWnuMZARRq96k+SjRW/tHFAOlIdnFSrt3BLPvSOK7R3bVAskZQ0N4FTmA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@sentry/browser/node_modules/@sentry/utils": {
|
||||||
|
"version": "7.99.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.99.0.tgz",
|
||||||
|
"integrity": "sha512-cYZy5WNTkWs5GgggGnjfGqC44CWir0pAv4GVVSx0fsup4D4pMKBJPrtub15f9uC+QkUf3vVkqwpBqeFxtmJQTQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@sentry/types": "7.99.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sentry/cli": {
|
"node_modules/@sentry/cli": {
|
||||||
"version": "1.74.6",
|
"version": "2.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/cli/-/cli-1.74.6.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry/cli/-/cli-2.27.0.tgz",
|
||||||
"integrity": "sha512-pJ7JJgozyjKZSTjOGi86chIngZMLUlYt2HOog+OJn+WGvqEkVymu8m462j1DiXAnex9NspB4zLLNuZ/R6rTQHg==",
|
"integrity": "sha512-pc0opd71W8lGhYvmB1keQtJkarxzCS9f9ErKYv6TfXOOX6drvwkyA6vD/6xEnpzyvqGAuGRU4T4sEeLD3irwUQ==",
|
||||||
"dev": true,
|
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "BSD-3-Clause",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"https-proxy-agent": "^5.0.0",
|
"https-proxy-agent": "^5.0.0",
|
||||||
"mkdirp": "^0.5.5",
|
|
||||||
"node-fetch": "^2.6.7",
|
"node-fetch": "^2.6.7",
|
||||||
"npmlog": "^4.1.2",
|
|
||||||
"progress": "^2.0.3",
|
"progress": "^2.0.3",
|
||||||
"proxy-from-env": "^1.1.0",
|
"proxy-from-env": "^1.1.0",
|
||||||
"which": "^2.0.2"
|
"which": "^2.0.2"
|
||||||
@@ -4256,7 +4413,124 @@
|
|||||||
"sentry-cli": "bin/sentry-cli"
|
"sentry-cli": "bin/sentry-cli"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 8"
|
"node": ">= 10"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@sentry/cli-darwin": "2.27.0",
|
||||||
|
"@sentry/cli-linux-arm": "2.27.0",
|
||||||
|
"@sentry/cli-linux-arm64": "2.27.0",
|
||||||
|
"@sentry/cli-linux-i686": "2.27.0",
|
||||||
|
"@sentry/cli-linux-x64": "2.27.0",
|
||||||
|
"@sentry/cli-win32-i686": "2.27.0",
|
||||||
|
"@sentry/cli-win32-x64": "2.27.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@sentry/cli-darwin": {
|
||||||
|
"version": "2.27.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@sentry/cli-darwin/-/cli-darwin-2.27.0.tgz",
|
||||||
|
"integrity": "sha512-/DOZlN5rK19g7YP2OaVNauQhUrRfJ88RDr6qURFiqdxYHDc3isPFGHZJmeZBTwOnDDepyZb4XLaOyfwvAOxHig==",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@sentry/cli-linux-arm": {
|
||||||
|
"version": "2.27.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@sentry/cli-linux-arm/-/cli-linux-arm-2.27.0.tgz",
|
||||||
|
"integrity": "sha512-JmMQ9zgFhkZUEN5WIYuJisu4Jif/ThRHDjbsbXBRbUkkgRn88hgUfg299djMvlZZxjpl3K9AEua+1TIUeQd0Sg==",
|
||||||
|
"cpu": [
|
||||||
|
"arm"
|
||||||
|
],
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux",
|
||||||
|
"freebsd"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@sentry/cli-linux-arm64": {
|
||||||
|
"version": "2.27.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@sentry/cli-linux-arm64/-/cli-linux-arm64-2.27.0.tgz",
|
||||||
|
"integrity": "sha512-f+zuB9XGfB8pNamNgSDhqsavuLuzi6saZxbr3uQf30bA5AESI5hspOd1zPcidOORCVZxiPzQe3+T7avBI1XLuw==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux",
|
||||||
|
"freebsd"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@sentry/cli-linux-i686": {
|
||||||
|
"version": "2.27.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@sentry/cli-linux-i686/-/cli-linux-i686-2.27.0.tgz",
|
||||||
|
"integrity": "sha512-/4eyz7jnYp20mZqNtpvCEBkxFW0nEjEZRo2BiASQ5/7K8CmoJRe1vhpDA0WOfzi1zTFIfpdE1/RZm2CjHS6DHQ==",
|
||||||
|
"cpu": [
|
||||||
|
"x86",
|
||||||
|
"ia32"
|
||||||
|
],
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux",
|
||||||
|
"freebsd"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@sentry/cli-linux-x64": {
|
||||||
|
"version": "2.27.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@sentry/cli-linux-x64/-/cli-linux-x64-2.27.0.tgz",
|
||||||
|
"integrity": "sha512-ptu7wXecnYssihzHlxEOaqbFHWmNEfbepBKGXTdWK2kC+D51+7yHsR9xRdThwVID1bisFgjAveKmBQjmKuXjHQ==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux",
|
||||||
|
"freebsd"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@sentry/cli-win32-i686": {
|
||||||
|
"version": "2.27.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@sentry/cli-win32-i686/-/cli-win32-i686-2.27.0.tgz",
|
||||||
|
"integrity": "sha512-Db4/xmdE5qV4Aq7Yc8vRw22Y46JJdGMdsMsl5jIf0GVSQPgO23O/2uTiDGpPOdeq91K9EtvpH1zQfDLIfLMaXw==",
|
||||||
|
"cpu": [
|
||||||
|
"x86",
|
||||||
|
"ia32"
|
||||||
|
],
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@sentry/cli-win32-x64": {
|
||||||
|
"version": "2.27.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@sentry/cli-win32-x64/-/cli-win32-x64-2.27.0.tgz",
|
||||||
|
"integrity": "sha512-q7y/BH4iGfs0TD5PXh2Q8oqnTbOIufoT1NWJcKqvZcOiqCLK3PNUiq7xUeX1PMTrFYAh3Bm6EekOnMavqvbGmg==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sentry/core": {
|
"node_modules/@sentry/core": {
|
||||||
@@ -4280,16 +4554,15 @@
|
|||||||
"license": "0BSD"
|
"license": "0BSD"
|
||||||
},
|
},
|
||||||
"node_modules/@sentry/react": {
|
"node_modules/@sentry/react": {
|
||||||
"version": "7.40.0",
|
"version": "7.99.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/react/-/react-7.40.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry/react/-/react-7.99.0.tgz",
|
||||||
"integrity": "sha512-7yYagpOCdsXnVTtLL8Y7wAf2xXgsk2ncuju3O/G4kEckkLewZWmQeoknOSGFlAgVdGNhTaXc2WGzgOiBMOkhug==",
|
"integrity": "sha512-RtHwgzMHJhzJfSQpVG0SDPQYMTGDX3Q37/YWI59S4ALMbSW4/F6n/eQAvGVYZKbh2UCSqgFuRWaXOYkSZT17wA==",
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sentry/browser": "7.40.0",
|
"@sentry/browser": "7.99.0",
|
||||||
"@sentry/types": "7.40.0",
|
"@sentry/core": "7.99.0",
|
||||||
"@sentry/utils": "7.40.0",
|
"@sentry/types": "7.99.0",
|
||||||
"hoist-non-react-statics": "^3.3.2",
|
"@sentry/utils": "7.99.0",
|
||||||
"tslib": "^1.9.3"
|
"hoist-non-react-statics": "^3.3.2"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
@@ -4298,26 +4571,82 @@
|
|||||||
"react": "15.x || 16.x || 17.x || 18.x"
|
"react": "15.x || 16.x || 17.x || 18.x"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sentry/react/node_modules/tslib": {
|
"node_modules/@sentry/react/node_modules/@sentry/core": {
|
||||||
"version": "1.14.1",
|
"version": "7.99.0",
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.99.0.tgz",
|
||||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
|
"integrity": "sha512-vOAtzcAXEUtS/oW7wi3wMkZ3hsb5Ch96gKyrrj/mXdOp2zrcwdNV6N9/pawq2E9P/7Pw8AXw4CeDZztZrjQLuA==",
|
||||||
"license": "0BSD"
|
"dependencies": {
|
||||||
|
"@sentry/types": "7.99.0",
|
||||||
|
"@sentry/utils": "7.99.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@sentry/react/node_modules/@sentry/types": {
|
||||||
|
"version": "7.99.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.99.0.tgz",
|
||||||
|
"integrity": "sha512-94qwOw4w40sAs5mCmzcGyj8ZUu/KhnWnuMZARRq96k+SjRW/tHFAOlIdnFSrt3BLPvSOK7R3bVAskZQ0N4FTmA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@sentry/react/node_modules/@sentry/utils": {
|
||||||
|
"version": "7.99.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.99.0.tgz",
|
||||||
|
"integrity": "sha512-cYZy5WNTkWs5GgggGnjfGqC44CWir0pAv4GVVSx0fsup4D4pMKBJPrtub15f9uC+QkUf3vVkqwpBqeFxtmJQTQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@sentry/types": "7.99.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sentry/replay": {
|
"node_modules/@sentry/replay": {
|
||||||
"version": "7.40.0",
|
"version": "7.99.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/replay/-/replay-7.40.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry/replay/-/replay-7.99.0.tgz",
|
||||||
"integrity": "sha512-Y9Kvo9jKouUdrHQhHVv5SmWZClF5o7BFI6oVpLlv4zXORPQlyoZONM/9sxiMvvH73alDSpxzCoxyhlypAOH4ww==",
|
"integrity": "sha512-gyN/I2WpQrLAZDT+rScB/0jnFL2knEVBo8U8/OVt8gNP20Pq8T/rDZKO/TG0cBfvULDUbJj2P4CJryn2p/O2rA==",
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sentry/core": "7.40.0",
|
"@sentry-internal/tracing": "7.99.0",
|
||||||
"@sentry/types": "7.40.0",
|
"@sentry/core": "7.99.0",
|
||||||
"@sentry/utils": "7.40.0"
|
"@sentry/types": "7.99.0",
|
||||||
|
"@sentry/utils": "7.99.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@sentry/replay/node_modules/@sentry/core": {
|
||||||
|
"version": "7.99.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.99.0.tgz",
|
||||||
|
"integrity": "sha512-vOAtzcAXEUtS/oW7wi3wMkZ3hsb5Ch96gKyrrj/mXdOp2zrcwdNV6N9/pawq2E9P/7Pw8AXw4CeDZztZrjQLuA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@sentry/types": "7.99.0",
|
||||||
|
"@sentry/utils": "7.99.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@sentry/replay/node_modules/@sentry/types": {
|
||||||
|
"version": "7.99.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.99.0.tgz",
|
||||||
|
"integrity": "sha512-94qwOw4w40sAs5mCmzcGyj8ZUu/KhnWnuMZARRq96k+SjRW/tHFAOlIdnFSrt3BLPvSOK7R3bVAskZQ0N4FTmA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@sentry/replay/node_modules/@sentry/utils": {
|
||||||
|
"version": "7.99.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.99.0.tgz",
|
||||||
|
"integrity": "sha512-cYZy5WNTkWs5GgggGnjfGqC44CWir0pAv4GVVSx0fsup4D4pMKBJPrtub15f9uC+QkUf3vVkqwpBqeFxtmJQTQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@sentry/types": "7.99.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@sentry/tracing": {
|
"node_modules/@sentry/tracing": {
|
||||||
"version": "7.40.0",
|
"version": "7.40.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-7.40.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-7.40.0.tgz",
|
||||||
@@ -4381,6 +4710,27 @@
|
|||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@sentry/webpack-plugin/node_modules/@sentry/cli": {
|
||||||
|
"version": "1.77.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@sentry/cli/-/cli-1.77.3.tgz",
|
||||||
|
"integrity": "sha512-c3eDqcDRmy4TFz2bFU5Y6QatlpoBPPa8cxBooaS4aMQpnIdLYPF1xhyyiW0LQlDUNc3rRjNF7oN5qKoaRoMTQQ==",
|
||||||
|
"dev": true,
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"dependencies": {
|
||||||
|
"https-proxy-agent": "^5.0.0",
|
||||||
|
"mkdirp": "^0.5.5",
|
||||||
|
"node-fetch": "^2.6.7",
|
||||||
|
"progress": "^2.0.3",
|
||||||
|
"proxy-from-env": "^1.1.0",
|
||||||
|
"which": "^2.0.2"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"sentry-cli": "bin/sentry-cli"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@sinclair/typebox": {
|
"node_modules/@sinclair/typebox": {
|
||||||
"version": "0.24.51",
|
"version": "0.24.51",
|
||||||
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz",
|
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz",
|
||||||
@@ -6112,12 +6462,21 @@
|
|||||||
"@apollo/client": "^3.0.0"
|
"@apollo/client": "^3.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/aproba": {
|
"node_modules/apollo-link-sentry": {
|
||||||
"version": "1.2.0",
|
"version": "3.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/apollo-link-sentry/-/apollo-link-sentry-3.3.0.tgz",
|
||||||
"integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
|
"integrity": "sha512-wLffWmo5sRw3rHN1Ck6azM0oxObvtaBBf3AC8cLX4SxhyjmkRIagGDji6CFkyAhxupPz0b9/H1u4Ocx+63lNug==",
|
||||||
"dev": true,
|
"dependencies": {
|
||||||
"license": "ISC"
|
"deepmerge": "^4.2.2",
|
||||||
|
"dot-prop": "^6.0.0",
|
||||||
|
"tslib": "^2.0.3",
|
||||||
|
"zen-observable-ts": "^1.2.5"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@apollo/client": "^3.2.3",
|
||||||
|
"@sentry/browser": "^7.41.0",
|
||||||
|
"graphql": "15 - 16"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"node_modules/arch": {
|
"node_modules/arch": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
@@ -6140,17 +6499,6 @@
|
|||||||
],
|
],
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/are-we-there-yet": {
|
|
||||||
"version": "1.1.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz",
|
|
||||||
"integrity": "sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "ISC",
|
|
||||||
"dependencies": {
|
|
||||||
"delegates": "^1.0.0",
|
|
||||||
"readable-stream": "^2.0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/arg": {
|
"node_modules/arg": {
|
||||||
"version": "5.0.2",
|
"version": "5.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
|
||||||
@@ -7571,16 +7919,6 @@
|
|||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/code-point-at": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
|
|
||||||
"integrity": "sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.10.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/collect-v8-coverage": {
|
"node_modules/collect-v8-coverage": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz",
|
||||||
@@ -7743,13 +8081,6 @@
|
|||||||
"node": ">=0.8"
|
"node": ">=0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/console-control-strings": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
|
|
||||||
"integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "ISC"
|
|
||||||
},
|
|
||||||
"node_modules/content-disposition": {
|
"node_modules/content-disposition": {
|
||||||
"version": "0.5.4",
|
"version": "0.5.4",
|
||||||
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
|
||||||
@@ -8911,13 +9242,6 @@
|
|||||||
"node": ">=0.4.0"
|
"node": ">=0.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/delegates": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
|
|
||||||
"integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/denque": {
|
"node_modules/denque": {
|
||||||
"version": "1.5.1",
|
"version": "1.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz",
|
||||||
@@ -9218,6 +9542,28 @@
|
|||||||
"tslib": "^2.0.3"
|
"tslib": "^2.0.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/dot-prop": {
|
||||||
|
"version": "6.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz",
|
||||||
|
"integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==",
|
||||||
|
"dependencies": {
|
||||||
|
"is-obj": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/dot-prop/node_modules/is-obj": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/dotenv": {
|
"node_modules/dotenv": {
|
||||||
"version": "16.0.1",
|
"version": "16.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.1.tgz",
|
||||||
@@ -10464,6 +10810,8 @@
|
|||||||
},
|
},
|
||||||
"node_modules/eventemitter2": {
|
"node_modules/eventemitter2": {
|
||||||
"version": "6.4.7",
|
"version": "6.4.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.7.tgz",
|
||||||
|
"integrity": "sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
@@ -11283,74 +11631,6 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/gauge": {
|
|
||||||
"version": "2.7.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
|
|
||||||
"integrity": "sha512-14x4kjc6lkD3ltw589k0NrPD6cCNTD6CWoVUNpB85+DrtONoZn+Rug6xZU5RvSC4+TZPxA5AnBibQYAvZn41Hg==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "ISC",
|
|
||||||
"dependencies": {
|
|
||||||
"aproba": "^1.0.3",
|
|
||||||
"console-control-strings": "^1.0.0",
|
|
||||||
"has-unicode": "^2.0.0",
|
|
||||||
"object-assign": "^4.1.0",
|
|
||||||
"signal-exit": "^3.0.0",
|
|
||||||
"string-width": "^1.0.1",
|
|
||||||
"strip-ansi": "^3.0.1",
|
|
||||||
"wide-align": "^1.1.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/gauge/node_modules/ansi-regex": {
|
|
||||||
"version": "2.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
|
|
||||||
"integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.10.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/gauge/node_modules/is-fullwidth-code-point": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
|
|
||||||
"integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"number-is-nan": "^1.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.10.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/gauge/node_modules/string-width": {
|
|
||||||
"version": "1.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
|
|
||||||
"integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"code-point-at": "^1.0.0",
|
|
||||||
"is-fullwidth-code-point": "^1.0.0",
|
|
||||||
"strip-ansi": "^3.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.10.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/gauge/node_modules/strip-ansi": {
|
|
||||||
"version": "3.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
|
|
||||||
"integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"ansi-regex": "^2.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.10.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/gensync": {
|
"node_modules/gensync": {
|
||||||
"version": "1.0.0-beta.2",
|
"version": "1.0.0-beta.2",
|
||||||
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
|
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
|
||||||
@@ -11753,13 +12033,6 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/has-unicode": {
|
|
||||||
"version": "2.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
|
|
||||||
"integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "ISC"
|
|
||||||
},
|
|
||||||
"node_modules/he": {
|
"node_modules/he": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
|
||||||
@@ -15744,19 +16017,6 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/npmlog": {
|
|
||||||
"version": "4.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
|
|
||||||
"integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "ISC",
|
|
||||||
"dependencies": {
|
|
||||||
"are-we-there-yet": "~1.1.2",
|
|
||||||
"console-control-strings": "~1.1.0",
|
|
||||||
"gauge": "~2.7.3",
|
|
||||||
"set-blocking": "~2.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/nth-check": {
|
"node_modules/nth-check": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
|
||||||
@@ -15769,16 +16029,6 @@
|
|||||||
"url": "https://github.com/fb55/nth-check?sponsor=1"
|
"url": "https://github.com/fb55/nth-check?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/number-is-nan": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
|
|
||||||
"integrity": "sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.10.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/nwsapi": {
|
"node_modules/nwsapi": {
|
||||||
"version": "2.2.1",
|
"version": "2.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.1.tgz",
|
||||||
@@ -17794,7 +18044,6 @@
|
|||||||
"version": "2.0.3",
|
"version": "2.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
|
||||||
"integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
|
"integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.4.0"
|
"node": ">=0.4.0"
|
||||||
@@ -20526,13 +20775,6 @@
|
|||||||
"node": ">= 0.8.0"
|
"node": ">= 0.8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/set-blocking": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
|
|
||||||
"integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "ISC"
|
|
||||||
},
|
|
||||||
"node_modules/setprototypeof": {
|
"node_modules/setprototypeof": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
|
||||||
@@ -22110,19 +22352,6 @@
|
|||||||
"is-typedarray": "^1.0.0"
|
"is-typedarray": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/typescript": {
|
|
||||||
"version": "4.9.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
|
|
||||||
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
|
|
||||||
"peer": true,
|
|
||||||
"bin": {
|
|
||||||
"tsc": "bin/tsc",
|
|
||||||
"tsserver": "bin/tsserver"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=4.2.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/typescript-compare": {
|
"node_modules/typescript-compare": {
|
||||||
"version": "0.0.2",
|
"version": "0.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/typescript-compare/-/typescript-compare-0.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/typescript-compare/-/typescript-compare-0.0.2.tgz",
|
||||||
@@ -23080,16 +23309,6 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/wide-align": {
|
|
||||||
"version": "1.1.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz",
|
|
||||||
"integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "ISC",
|
|
||||||
"dependencies": {
|
|
||||||
"string-width": "^1.0.2 || 2 || 3 || 4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/wildcard": {
|
"node_modules/wildcard": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz",
|
||||||
|
|||||||
@@ -9,12 +9,14 @@
|
|||||||
"@craco/craco": "^7.0.0",
|
"@craco/craco": "^7.0.0",
|
||||||
"@fingerprintjs/fingerprintjs": "^3.4.2",
|
"@fingerprintjs/fingerprintjs": "^3.4.2",
|
||||||
"@jsreport/browser-client": "^3.1.0",
|
"@jsreport/browser-client": "^3.1.0",
|
||||||
"@sentry/react": "^7.40.0",
|
"@sentry/cli": "^2.27.0",
|
||||||
|
"@sentry/react": "^7.99.0",
|
||||||
"@sentry/tracing": "^7.40.0",
|
"@sentry/tracing": "^7.40.0",
|
||||||
"@splitsoftware/splitio-react": "^1.8.1",
|
"@splitsoftware/splitio-react": "^1.8.1",
|
||||||
"@tanem/react-nprogress": "^5.0.8",
|
"@tanem/react-nprogress": "^5.0.8",
|
||||||
"antd": "^4.24.8",
|
"antd": "^4.24.8",
|
||||||
"apollo-link-logger": "^2.0.1",
|
"apollo-link-logger": "^2.0.1",
|
||||||
|
"apollo-link-sentry": "^3.3.0",
|
||||||
"axios": "^1.3.4",
|
"axios": "^1.3.4",
|
||||||
"craco-less": "^2.0.0",
|
"craco-less": "^2.0.0",
|
||||||
"dinero.js": "^1.9.1",
|
"dinero.js": "^1.9.1",
|
||||||
@@ -88,13 +90,14 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"analyze": "source-map-explorer 'build/static/js/*.js'",
|
"analyze": "source-map-explorer 'build/static/js/*.js'",
|
||||||
"start": "craco start",
|
"start": "craco start",
|
||||||
"build": "REACT_APP_GIT_SHA=`git rev-parse --short HEAD` craco build",
|
"build": "REACT_APP_GIT_SHA=`git rev-parse --short HEAD` craco build && npm run sentry:sourcemaps",
|
||||||
"build:test": "env-cmd -f .env.test npm run build",
|
"build:test": "env-cmd -f .env.test npm run build",
|
||||||
"build-deploy:test": "npm run build:test && s3cmd sync build/* s3://imex-online-test && echo '🚀 TESTING Deployed!'",
|
"build-deploy:test": "npm run build:test && s3cmd sync build/* s3://imex-online-test && echo '🚀 TESTING Deployed!'",
|
||||||
"buildcra": "REACT_APP_GIT_SHA=`git rev-parse --short HEAD` craco build",
|
"buildcra": "REACT_APP_GIT_SHA=`git rev-parse --short HEAD` craco build",
|
||||||
"test": "cypress open",
|
"test": "cypress open",
|
||||||
"eject": "react-scripts eject",
|
"eject": "react-scripts eject",
|
||||||
"madge": "madge --image ./madge-graph.svg --extensions js,jsx,ts,tsx --circular ."
|
"madge": "madge --image ./madge-graph.svg --extensions js,jsx,ts,tsx --circular .",
|
||||||
|
"sentry:sourcemaps": "sentry-cli sourcemaps inject --org imex --project imexonline ./build && sentry-cli sourcemaps upload --org imex --project imexonline ./build"
|
||||||
},
|
},
|
||||||
"eslintConfig": {
|
"eslintConfig": {
|
||||||
"extends": [
|
"extends": [
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { useTranslation } from "react-i18next";
|
|||||||
import GlobalLoadingBar from "../components/global-loading-bar/global-loading-bar.component";
|
import GlobalLoadingBar from "../components/global-loading-bar/global-loading-bar.component";
|
||||||
import client from "../utils/GraphQLClient";
|
import client from "../utils/GraphQLClient";
|
||||||
import App from "./App";
|
import App from "./App";
|
||||||
|
import * as Sentry from "@sentry/react";
|
||||||
|
|
||||||
moment.locale("en-US");
|
moment.locale("en-US");
|
||||||
|
|
||||||
@@ -18,7 +19,7 @@ export const factory = SplitSdk({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default function AppContainer() {
|
function AppContainer() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -42,3 +43,5 @@ export default function AppContainer() {
|
|||||||
</ApolloProvider>
|
</ApolloProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default Sentry.withProfiler(AppContainer);
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import {
|
|||||||
} from "../redux/user/user.selectors";
|
} from "../redux/user/user.selectors";
|
||||||
import PrivateRoute from "../utils/private-route";
|
import PrivateRoute from "../utils/private-route";
|
||||||
import "./App.styles.scss";
|
import "./App.styles.scss";
|
||||||
|
import handleBeta from "../utils/handleBeta";
|
||||||
|
|
||||||
const ResetPassword = lazy(() =>
|
const ResetPassword = lazy(() =>
|
||||||
import("../pages/reset-password/reset-password.component")
|
import("../pages/reset-password/reset-password.component")
|
||||||
@@ -57,7 +58,6 @@ export function App({
|
|||||||
if (!navigator.onLine) {
|
if (!navigator.onLine) {
|
||||||
setOnline(false);
|
setOnline(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
checkUserSession();
|
checkUserSession();
|
||||||
}, [checkUserSession, setOnline]);
|
}, [checkUserSession, setOnline]);
|
||||||
|
|
||||||
@@ -73,6 +73,7 @@ export function App({
|
|||||||
window.addEventListener("online", function (e) {
|
window.addEventListener("online", function (e) {
|
||||||
setOnline(true);
|
setOnline(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (currentUser.authorized && bodyshop) {
|
if (currentUser.authorized && bodyshop) {
|
||||||
client.setAttribute("imexshopid", bodyshop.imexshopid);
|
client.setAttribute("imexshopid", bodyshop.imexshopid);
|
||||||
@@ -107,6 +108,8 @@ export function App({
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
handleBeta();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Switch>
|
<Switch>
|
||||||
<Suspense fallback={<LoadingSpinner message="ImEX Online" />}>
|
<Suspense fallback={<LoadingSpinner message="ImEX Online" />}>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { createStructuredSelector } from "reselect";
|
|||||||
import {
|
import {
|
||||||
DELETE_BILL_LINE,
|
DELETE_BILL_LINE,
|
||||||
INSERT_NEW_BILL_LINES,
|
INSERT_NEW_BILL_LINES,
|
||||||
UPDATE_BILL_LINE
|
UPDATE_BILL_LINE,
|
||||||
} from "../../graphql/bill-lines.queries";
|
} from "../../graphql/bill-lines.queries";
|
||||||
import { QUERY_BILL_BY_PK, UPDATE_BILL } from "../../graphql/bills.queries";
|
import { QUERY_BILL_BY_PK, UPDATE_BILL } from "../../graphql/bills.queries";
|
||||||
import { insertAuditTrail } from "../../redux/application/application.actions";
|
import { insertAuditTrail } from "../../redux/application/application.actions";
|
||||||
@@ -20,6 +20,7 @@ import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
|||||||
import AlertComponent from "../alert/alert.component";
|
import AlertComponent from "../alert/alert.component";
|
||||||
import BillFormContainer from "../bill-form/bill-form.container";
|
import BillFormContainer from "../bill-form/bill-form.container";
|
||||||
import BillMarkExportedButton from "../bill-mark-exported-button/bill-mark-exported-button.component";
|
import BillMarkExportedButton from "../bill-mark-exported-button/bill-mark-exported-button.component";
|
||||||
|
import BillPrintButton from "../bill-print-button/bill-print-button.component";
|
||||||
import BillReeportButtonComponent from "../bill-reexport-button/bill-reexport-button.component";
|
import BillReeportButtonComponent from "../bill-reexport-button/bill-reexport-button.component";
|
||||||
import JobDocumentsGallery from "../jobs-documents-gallery/jobs-documents-gallery.container";
|
import JobDocumentsGallery from "../jobs-documents-gallery/jobs-documents-gallery.container";
|
||||||
import JobsDocumentsLocalGallery from "../jobs-documents-local-gallery/jobs-documents-local-gallery.container";
|
import JobsDocumentsLocalGallery from "../jobs-documents-local-gallery/jobs-documents-local-gallery.container";
|
||||||
@@ -176,7 +177,7 @@ export function BillDetailEditcontainer({
|
|||||||
extra={
|
extra={
|
||||||
<Space>
|
<Space>
|
||||||
<BillDetailEditReturn data={data} />
|
<BillDetailEditReturn data={data} />
|
||||||
|
<BillPrintButton billid={search.billid} />
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
visible={visible}
|
visible={visible}
|
||||||
onConfirm={() => form.submit()}
|
onConfirm={() => form.submit()}
|
||||||
|
|||||||
@@ -94,6 +94,7 @@ function BillEnterModalContainer({
|
|||||||
location,
|
location,
|
||||||
outstanding_returns,
|
outstanding_returns,
|
||||||
inventory,
|
inventory,
|
||||||
|
federal_tax_exempt,
|
||||||
...remainingValues
|
...remainingValues
|
||||||
} = values;
|
} = values;
|
||||||
|
|
||||||
|
|||||||
@@ -79,6 +79,20 @@ export function BillFormComponent({
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleFederalTaxExemptSwitchToggle = (checked) => {
|
||||||
|
// Early gate
|
||||||
|
if (!checked) return;
|
||||||
|
const values = form.getFieldsValue("billlines");
|
||||||
|
// Gate bill lines
|
||||||
|
if (!values?.billlines?.length) return;
|
||||||
|
|
||||||
|
const billlines = values.billlines.map((b) => {
|
||||||
|
b.applicable_taxes.federal = false;
|
||||||
|
return b;
|
||||||
|
});
|
||||||
|
form.setFieldsValue({ billlines });
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (job) form.validateFields(["is_credit_memo"]);
|
if (job) form.validateFields(["is_credit_memo"]);
|
||||||
}, [job, form]);
|
}, [job, form]);
|
||||||
@@ -387,7 +401,16 @@ export function BillFormComponent({
|
|||||||
>
|
>
|
||||||
<CurrencyInput min={0} />
|
<CurrencyInput min={0} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item shouldUpdate span={15}>
|
{bodyshop.pbs_serialnumber || bodyshop.cdk_dealerid ? (
|
||||||
|
<Form.Item
|
||||||
|
span={2}
|
||||||
|
label={t("bills.labels.federal_tax_exempt")}
|
||||||
|
name="federal_tax_exempt"
|
||||||
|
>
|
||||||
|
<Switch onChange={handleFederalTaxExemptSwitchToggle} />
|
||||||
|
</Form.Item>
|
||||||
|
) : null}
|
||||||
|
<Form.Item shouldUpdate span={13}>
|
||||||
{() => {
|
{() => {
|
||||||
const values = form.getFieldsValue([
|
const values = form.getFieldsValue([
|
||||||
"billlines",
|
"billlines",
|
||||||
@@ -405,7 +428,7 @@ export function BillFormComponent({
|
|||||||
totals = CalculateBillTotal(values);
|
totals = CalculateBillTotal(values);
|
||||||
if (!!totals)
|
if (!!totals)
|
||||||
return (
|
return (
|
||||||
<div>
|
<div align="right">
|
||||||
<Space wrap>
|
<Space wrap>
|
||||||
<Statistic
|
<Statistic
|
||||||
title={t("bills.labels.subtotal")}
|
title={t("bills.labels.subtotal")}
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
import { DeleteFilled, DollarCircleFilled } from "@ant-design/icons";
|
import { DeleteFilled, DollarCircleFilled } from "@ant-design/icons";
|
||||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||||
import {
|
import {
|
||||||
Button, Form,
|
Button,
|
||||||
|
Form,
|
||||||
Input,
|
Input,
|
||||||
InputNumber,
|
InputNumber,
|
||||||
Select,
|
Select,
|
||||||
Space,
|
Space,
|
||||||
Switch,
|
Switch,
|
||||||
Table,
|
Table,
|
||||||
Tooltip
|
Tooltip,
|
||||||
} from "antd";
|
} from "antd";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
@@ -466,7 +467,8 @@ export function BillEnterModalLinesComponent({
|
|||||||
return {
|
return {
|
||||||
key: `${field.index}fedtax`,
|
key: `${field.index}fedtax`,
|
||||||
valuePropName: "checked",
|
valuePropName: "checked",
|
||||||
initialValue: true,
|
initialValue:
|
||||||
|
form.getFieldValue("federal_tax_exempt") === true ? false : true,
|
||||||
name: [field.name, "applicable_taxes", "federal"],
|
name: [field.name, "applicable_taxes", "federal"],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import { Button, Space } from "antd";
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { GenerateDocument } from "../../utils/RenderTemplate";
|
||||||
|
import { TemplateList } from "../../utils/TemplateConstants";
|
||||||
|
|
||||||
|
export default function BillPrintButton({ billid }) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const Templates = TemplateList("job_special");
|
||||||
|
|
||||||
|
const submitHandler = async () => {
|
||||||
|
setLoading(true);
|
||||||
|
try {
|
||||||
|
await GenerateDocument(
|
||||||
|
{
|
||||||
|
name: Templates.parts_invoice_label_single.key,
|
||||||
|
variables: {
|
||||||
|
id: billid,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{},
|
||||||
|
"p"
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
console.warn("Warning: Error generating a document.");
|
||||||
|
}
|
||||||
|
setLoading(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Space wrap>
|
||||||
|
<Button loading={loading} onClick={submitHandler}>
|
||||||
|
{t("bills.labels.printlabels")}
|
||||||
|
</Button>
|
||||||
|
</Space>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ import PhoneNumberFormatter from "../../utils/PhoneFormatter";
|
|||||||
import ChatArchiveButton from "../chat-archive-button/chat-archive-button.component";
|
import ChatArchiveButton from "../chat-archive-button/chat-archive-button.component";
|
||||||
import ChatConversationTitleTags from "../chat-conversation-title-tags/chat-conversation-title-tags.component";
|
import ChatConversationTitleTags from "../chat-conversation-title-tags/chat-conversation-title-tags.component";
|
||||||
import ChatLabelComponent from "../chat-label/chat-label.component";
|
import ChatLabelComponent from "../chat-label/chat-label.component";
|
||||||
|
import ChatPrintButton from "../chat-print-button/chat-print-button.component";
|
||||||
import ChatTagRoContainer from "../chat-tag-ro/chat-tag-ro.container";
|
import ChatTagRoContainer from "../chat-tag-ro/chat-tag-ro.container";
|
||||||
|
|
||||||
export default function ChatConversationTitle({ conversation }) {
|
export default function ChatConversationTitle({ conversation }) {
|
||||||
@@ -13,6 +14,7 @@ export default function ChatConversationTitle({ conversation }) {
|
|||||||
{conversation && conversation.phone_num}
|
{conversation && conversation.phone_num}
|
||||||
</PhoneNumberFormatter>
|
</PhoneNumberFormatter>
|
||||||
<ChatLabelComponent conversation={conversation} />
|
<ChatLabelComponent conversation={conversation} />
|
||||||
|
<ChatPrintButton conversation={conversation} />
|
||||||
<ChatConversationTitleTags
|
<ChatConversationTitleTags
|
||||||
jobConversations={
|
jobConversations={
|
||||||
(conversation && conversation.job_conversations) || []
|
(conversation && conversation.job_conversations) || []
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
import { MailOutlined, PrinterOutlined } from "@ant-design/icons";
|
||||||
|
import { Space, Spin } from "antd";
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import { setEmailOptions } from "../../redux/email/email.actions";
|
||||||
|
import { GenerateDocument } from "../../utils/RenderTemplate";
|
||||||
|
import { TemplateList } from "../../utils/TemplateConstants";
|
||||||
|
|
||||||
|
const mapStateToProps = createStructuredSelector({});
|
||||||
|
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
setEmailOptions: (e) => dispatch(setEmailOptions(e)),
|
||||||
|
});
|
||||||
|
|
||||||
|
export function ChatPrintButton({ conversation }) {
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Space wrap>
|
||||||
|
<PrinterOutlined
|
||||||
|
onClick={() => {
|
||||||
|
setLoading(true);
|
||||||
|
GenerateDocument(
|
||||||
|
{
|
||||||
|
name: TemplateList("messaging").conversation_list.key,
|
||||||
|
variables: { id: conversation.id },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
subject: TemplateList("messaging").conversation_list.subject,
|
||||||
|
},
|
||||||
|
"p",
|
||||||
|
conversation.id
|
||||||
|
);
|
||||||
|
setLoading(false);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<MailOutlined
|
||||||
|
onClick={() => {
|
||||||
|
setLoading(true);
|
||||||
|
GenerateDocument(
|
||||||
|
{
|
||||||
|
name: TemplateList("messaging").conversation_list.key,
|
||||||
|
variables: { id: conversation.id },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
subject: TemplateList("messaging").conversation_list.subject,
|
||||||
|
},
|
||||||
|
"e",
|
||||||
|
conversation.id
|
||||||
|
);
|
||||||
|
setLoading(false);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{loading && <Spin />}
|
||||||
|
</Space>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(ChatPrintButton);
|
||||||
@@ -35,6 +35,15 @@ export default function ContractsCarsComponent({
|
|||||||
state.sortedInfo.columnKey === "status" && state.sortedInfo.order,
|
state.sortedInfo.columnKey === "status" && state.sortedInfo.order,
|
||||||
render: (text, record) => <div>{t(record.status)}</div>,
|
render: (text, record) => <div>{t(record.status)}</div>,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: t("courtesycars.fields.readiness"),
|
||||||
|
dataIndex: "readiness",
|
||||||
|
key: "readiness",
|
||||||
|
sorter: (a, b) => alphaSort(a.readiness, b.readiness),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "readiness" && state.sortedInfo.order,
|
||||||
|
render: (text, record) => t(record.readiness),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: t("courtesycars.fields.year"),
|
title: t("courtesycars.fields.year"),
|
||||||
dataIndex: "year",
|
dataIndex: "year",
|
||||||
|
|||||||
@@ -68,6 +68,30 @@ export default function ContractFormComponent({
|
|||||||
<FormDateTimePicker />
|
<FormDateTimePicker />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
)}
|
)}
|
||||||
|
{create && (
|
||||||
|
<Form.Item
|
||||||
|
shouldUpdate={(p, c) => p.scheduledreturn !== c.scheduledreturn}
|
||||||
|
>
|
||||||
|
{() => {
|
||||||
|
const insuranceOver =
|
||||||
|
selectedCar &&
|
||||||
|
selectedCar.insuranceexpires &&
|
||||||
|
moment(selectedCar.insuranceexpires)
|
||||||
|
.endOf("day")
|
||||||
|
.isBefore(moment(form.getFieldValue("scheduledreturn")));
|
||||||
|
if (insuranceOver)
|
||||||
|
return (
|
||||||
|
<Space direction="vertical" style={{ color: "tomato" }}>
|
||||||
|
<span>
|
||||||
|
<WarningFilled style={{ marginRight: ".3rem" }} />
|
||||||
|
{t("contracts.labels.insuranceexpired")}
|
||||||
|
</span>
|
||||||
|
</Space>
|
||||||
|
);
|
||||||
|
return <></>;
|
||||||
|
}}
|
||||||
|
</Form.Item>
|
||||||
|
)}
|
||||||
</LayoutFormRow>
|
</LayoutFormRow>
|
||||||
<LayoutFormRow grow>
|
<LayoutFormRow grow>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
@@ -90,16 +114,17 @@ export default function ContractFormComponent({
|
|||||||
>
|
>
|
||||||
{() => {
|
{() => {
|
||||||
const mileageOver =
|
const mileageOver =
|
||||||
selectedCar &&
|
selectedCar && selectedCar.nextservicekm
|
||||||
selectedCar.nextservicekm <= form.getFieldValue("kmstart");
|
? selectedCar.nextservicekm <= form.getFieldValue("kmstart")
|
||||||
|
: false;
|
||||||
const dueForService =
|
const dueForService =
|
||||||
selectedCar &&
|
selectedCar &&
|
||||||
selectedCar.nextservicedate &&
|
selectedCar.nextservicedate &&
|
||||||
moment(selectedCar.nextservicedate).isBefore(
|
moment(selectedCar.nextservicedate)
|
||||||
moment(form.getFieldValue("scheduledreturn"))
|
.endOf("day")
|
||||||
);
|
.isSameOrBefore(
|
||||||
|
moment(form.getFieldValue("scheduledreturn"))
|
||||||
|
);
|
||||||
if (mileageOver || dueForService)
|
if (mileageOver || dueForService)
|
||||||
return (
|
return (
|
||||||
<Space direction="vertical" style={{ color: "tomato" }}>
|
<Space direction="vertical" style={{ color: "tomato" }}>
|
||||||
@@ -117,7 +142,6 @@ export default function ContractFormComponent({
|
|||||||
</span>
|
</span>
|
||||||
</Space>
|
</Space>
|
||||||
);
|
);
|
||||||
|
|
||||||
return <></>;
|
return <></>;
|
||||||
}}
|
}}
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { useTranslation } from "react-i18next";
|
|||||||
import { CHECK_CC_FLEET_NUMBER } from "../../graphql/courtesy-car.queries";
|
import { CHECK_CC_FLEET_NUMBER } from "../../graphql/courtesy-car.queries";
|
||||||
import { DateFormatter } from "../../utils/DateFormatter";
|
import { DateFormatter } from "../../utils/DateFormatter";
|
||||||
import CourtesyCarFuelSlider from "../courtesy-car-fuel-select/courtesy-car-fuel-select.component";
|
import CourtesyCarFuelSlider from "../courtesy-car-fuel-select/courtesy-car-fuel-select.component";
|
||||||
|
import CourtesyCarReadiness from "../courtesy-car-readiness-select/courtesy-car-readiness-select.component";
|
||||||
import CourtesyCarStatus from "../courtesy-car-status-select/courtesy-car-status-select.component";
|
import CourtesyCarStatus from "../courtesy-car-status-select/courtesy-car-status-select.component";
|
||||||
import FormDatePicker from "../form-date-picker/form-date-picker.component";
|
import FormDatePicker from "../form-date-picker/form-date-picker.component";
|
||||||
//import FormFieldsChanged from "../form-fields-changed-alert/form-fields-changed-alert.component";
|
//import FormFieldsChanged from "../form-fields-changed-alert/form-fields-changed-alert.component";
|
||||||
@@ -213,6 +214,9 @@ export default function CourtesyCarCreateFormComponent({ form, saveLoading }) {
|
|||||||
>
|
>
|
||||||
<CourtesyCarStatus />
|
<CourtesyCarStatus />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
<Form.Item label={t("courtesycars.fields.readiness")} name="readiness">
|
||||||
|
<CourtesyCarReadiness />
|
||||||
|
</Form.Item>
|
||||||
<div>
|
<div>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("courtesycars.fields.nextservicekm")}
|
label={t("courtesycars.fields.nextservicekm")}
|
||||||
@@ -227,8 +231,9 @@ export default function CourtesyCarCreateFormComponent({ form, saveLoading }) {
|
|||||||
>
|
>
|
||||||
{() => {
|
{() => {
|
||||||
const nextservicekm = form.getFieldValue("nextservicekm");
|
const nextservicekm = form.getFieldValue("nextservicekm");
|
||||||
const mileageOver =
|
const mileageOver = nextservicekm
|
||||||
nextservicekm && nextservicekm <= form.getFieldValue("mileage");
|
? nextservicekm <= form.getFieldValue("mileage")
|
||||||
|
: false;
|
||||||
if (mileageOver)
|
if (mileageOver)
|
||||||
return (
|
return (
|
||||||
<Space direction="vertical" style={{ color: "tomato" }}>
|
<Space direction="vertical" style={{ color: "tomato" }}>
|
||||||
|
|||||||
@@ -34,6 +34,32 @@ const CourtesyCarFuelComponent = (props, ref) => {
|
|||||||
step={null}
|
step={null}
|
||||||
style={{ marginLeft: "2rem", marginRight: "2rem" }}
|
style={{ marginLeft: "2rem", marginRight: "2rem" }}
|
||||||
{...props}
|
{...props}
|
||||||
|
tooltip={{
|
||||||
|
formatter: (value) => {
|
||||||
|
switch (value) {
|
||||||
|
case 0:
|
||||||
|
return t("courtesycars.labels.fuel.empty");
|
||||||
|
case 13:
|
||||||
|
return t("courtesycars.labels.fuel.18");
|
||||||
|
case 25:
|
||||||
|
return t("courtesycars.labels.fuel.14");
|
||||||
|
case 38:
|
||||||
|
return t("courtesycars.labels.fuel.38");
|
||||||
|
case 50:
|
||||||
|
return t("courtesycars.labels.fuel.12");
|
||||||
|
case 63:
|
||||||
|
return t("courtesycars.labels.fuel.58");
|
||||||
|
case 75:
|
||||||
|
return t("courtesycars.labels.fuel.34");
|
||||||
|
case 88:
|
||||||
|
return t("courtesycars.labels.fuel.78");
|
||||||
|
case 100:
|
||||||
|
return t("courtesycars.labels.fuel.full");
|
||||||
|
default:
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import { Select } from "antd";
|
||||||
|
import React, { forwardRef, useEffect, useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
const { Option } = Select;
|
||||||
|
|
||||||
|
const CourtesyCarReadinessComponent = ({ value, onChange }, ref) => {
|
||||||
|
const [option, setOption] = useState(value);
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (value !== option && onChange) {
|
||||||
|
onChange(option);
|
||||||
|
}
|
||||||
|
}, [value, option, onChange]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Select
|
||||||
|
allowClear
|
||||||
|
ref={ref}
|
||||||
|
value={option}
|
||||||
|
style={{
|
||||||
|
width: 100,
|
||||||
|
}}
|
||||||
|
onChange={setOption}
|
||||||
|
>
|
||||||
|
<Option value="courtesycars.readiness.ready">
|
||||||
|
{t("courtesycars.readiness.ready")}
|
||||||
|
</Option>
|
||||||
|
<Option value="courtesycars.readiness.notready">
|
||||||
|
{t("courtesycars.readiness.notready")}
|
||||||
|
</Option>
|
||||||
|
</Select>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default forwardRef(CourtesyCarReadinessComponent);
|
||||||
@@ -72,18 +72,34 @@ export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
|
|||||||
sortOrder:
|
sortOrder:
|
||||||
state.sortedInfo.columnKey === "status" && state.sortedInfo.order,
|
state.sortedInfo.columnKey === "status" && state.sortedInfo.order,
|
||||||
render: (text, record) => {
|
render: (text, record) => {
|
||||||
const { nextservicedate, nextservicekm, mileage } = record;
|
const { nextservicedate, nextservicekm, mileage, insuranceexpires } =
|
||||||
|
record;
|
||||||
|
|
||||||
const mileageOver = nextservicekm <= mileage;
|
const mileageOver = nextservicekm ? nextservicekm <= mileage : false;
|
||||||
|
|
||||||
const dueForService =
|
const dueForService =
|
||||||
nextservicedate && moment(nextservicedate).isBefore(moment());
|
nextservicedate &&
|
||||||
|
moment(nextservicedate).endOf("day").isSameOrBefore(moment());
|
||||||
|
|
||||||
|
const insuranceOver =
|
||||||
|
insuranceexpires &&
|
||||||
|
moment(insuranceexpires).endOf("day").isBefore(moment());
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Space>
|
<Space>
|
||||||
{t(record.status)}
|
{t(record.status)}
|
||||||
{(mileageOver || dueForService) && (
|
{(mileageOver || dueForService || insuranceOver) && (
|
||||||
<Tooltip title={t("contracts.labels.cardueforservice")}>
|
<Tooltip
|
||||||
|
title={
|
||||||
|
(mileageOver || dueForService) && insuranceOver
|
||||||
|
? t("contracts.labels.insuranceexpired") +
|
||||||
|
" / " +
|
||||||
|
t("contracts.labels.cardueforservice")
|
||||||
|
: insuranceOver
|
||||||
|
? t("contracts.labels.insuranceexpired")
|
||||||
|
: t("contracts.labels.cardueforservice")
|
||||||
|
}
|
||||||
|
>
|
||||||
<WarningFilled style={{ color: "tomato" }} />
|
<WarningFilled style={{ color: "tomato" }} />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
@@ -91,6 +107,26 @@ export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: t("courtesycars.fields.readiness"),
|
||||||
|
dataIndex: "readiness",
|
||||||
|
key: "readiness",
|
||||||
|
sorter: (a, b) => alphaSort(a.readiness, b.readiness),
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
text: t("courtesycars.readiness.ready"),
|
||||||
|
value: "courtesycars.readiness.ready",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: t("courtesycars.readiness.notready"),
|
||||||
|
value: "courtesycars.readiness.notready",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
onFilter: (value, record) => value.includes(record.readiness),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "readiness" && state.sortedInfo.order,
|
||||||
|
render: (text, record) => t(record.readiness),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: t("courtesycars.fields.year"),
|
title: t("courtesycars.fields.year"),
|
||||||
dataIndex: "year",
|
dataIndex: "year",
|
||||||
@@ -131,6 +167,36 @@ export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
|
|||||||
sortOrder:
|
sortOrder:
|
||||||
state.sortedInfo.columnKey === "plate" && state.sortedInfo.order,
|
state.sortedInfo.columnKey === "plate" && state.sortedInfo.order,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: t("courtesycars.fields.fuel"),
|
||||||
|
dataIndex: "fuel",
|
||||||
|
key: "fuel",
|
||||||
|
sorter: (a, b) => alphaSort(a.fuel, b.fuel),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "fuel" && state.sortedInfo.order,
|
||||||
|
render: (text, record) => {
|
||||||
|
switch (record.fuel) {
|
||||||
|
case 100:
|
||||||
|
return t("courtesycars.labels.fuel.full");
|
||||||
|
case 88:
|
||||||
|
return t("courtesycars.labels.fuel.78");
|
||||||
|
case 63:
|
||||||
|
return t("courtesycars.labels.fuel.58");
|
||||||
|
case 50:
|
||||||
|
return t("courtesycars.labels.fuel.12");
|
||||||
|
case 38:
|
||||||
|
return t("courtesycars.labels.fuel.34");
|
||||||
|
case 25:
|
||||||
|
return t("courtesycars.labels.fuel.14");
|
||||||
|
case 13:
|
||||||
|
return t("courtesycars.labels.fuel.18");
|
||||||
|
case 0:
|
||||||
|
return t("courtesycars.labels.fuel.empty");
|
||||||
|
default:
|
||||||
|
return record.fuel;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: t("courtesycars.labels.outwith"),
|
title: t("courtesycars.labels.outwith"),
|
||||||
dataIndex: "outwith",
|
dataIndex: "outwith",
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import React, { useEffect } from "react";
|
|||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useLocation } from "react-router-dom";
|
import { useLocation } from "react-router-dom";
|
||||||
import { QUERY_CSI_RESPONSE_BY_PK } from "../../graphql/csi.queries";
|
import { QUERY_CSI_RESPONSE_BY_PK } from "../../graphql/csi.queries";
|
||||||
|
import { DateFormatter } from "../../utils/DateFormatter";
|
||||||
import AlertComponent from "../alert/alert.component";
|
import AlertComponent from "../alert/alert.component";
|
||||||
import ConfigFormComponents from "../config-form-components/config-form-components.component";
|
import ConfigFormComponents from "../config-form-components/config-form-components.component";
|
||||||
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
|
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
|
||||||
@@ -44,6 +45,13 @@ export default function CsiResponseFormContainer() {
|
|||||||
readOnly
|
readOnly
|
||||||
componentList={data.csi_by_pk.csiquestion.config}
|
componentList={data.csi_by_pk.csiquestion.config}
|
||||||
/>
|
/>
|
||||||
|
{data.csi_by_pk.validuntil ? (
|
||||||
|
<>
|
||||||
|
{t("csi.fields.validuntil")}
|
||||||
|
{": "}
|
||||||
|
<DateFormatter>{data.csi_by_pk.validuntil}</DateFormatter>
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
</Form>
|
</Form>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -5,9 +5,11 @@ import React, { useState } from "react";
|
|||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Link, useHistory, useLocation } from "react-router-dom";
|
import { Link, useHistory, useLocation } from "react-router-dom";
|
||||||
import { DateFormatter } from "../../utils/DateFormatter";
|
import { DateFormatter } from "../../utils/DateFormatter";
|
||||||
import { alphaSort } from "../../utils/sorters";
|
import { pageLimit } from "../../utils/config";
|
||||||
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
import { alphaSort, dateSort } from "../../utils/sorters";
|
||||||
import {pageLimit} from "../../utils/config";
|
import OwnerNameDisplay, {
|
||||||
|
OwnerNameDisplayFunction,
|
||||||
|
} from "../owner-name-display/owner-name-display.component";
|
||||||
|
|
||||||
export default function CsiResponseListPaginated({
|
export default function CsiResponseListPaginated({
|
||||||
refetch,
|
refetch,
|
||||||
@@ -16,23 +18,23 @@ export default function CsiResponseListPaginated({
|
|||||||
total,
|
total,
|
||||||
}) {
|
}) {
|
||||||
const search = queryString.parse(useLocation().search);
|
const search = queryString.parse(useLocation().search);
|
||||||
const { responseid, page, sortcolumn, sortorder } = search;
|
const { responseid } = search;
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
const { t } = useTranslation();
|
||||||
const [state, setState] = useState({
|
const [state, setState] = useState({
|
||||||
sortedInfo: {},
|
sortedInfo: {},
|
||||||
filteredInfo: { text: "" },
|
filteredInfo: { text: "" },
|
||||||
|
page: "",
|
||||||
});
|
});
|
||||||
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
title: t("jobs.fields.ro_number"),
|
title: t("jobs.fields.ro_number"),
|
||||||
dataIndex: "ro_number",
|
dataIndex: "ro_number",
|
||||||
key: "ro_number",
|
key: "ro_number",
|
||||||
width: "8%",
|
|
||||||
sorter: (a, b) => alphaSort(a.job.ro_number, b.job.ro_number),
|
sorter: (a, b) => alphaSort(a.job.ro_number, b.job.ro_number),
|
||||||
sortOrder: sortcolumn === "ro_number" && sortorder,
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "ro_number" && state.sortedInfo.order,
|
||||||
render: (text, record) => (
|
render: (text, record) => (
|
||||||
<Link to={"/manage/jobs/" + record.job.id}>
|
<Link to={"/manage/jobs/" + record.job.id}>
|
||||||
{record.job.ro_number || t("general.labels.na")}
|
{record.job.ro_number || t("general.labels.na")}
|
||||||
@@ -41,15 +43,18 @@ export default function CsiResponseListPaginated({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("jobs.fields.owner"),
|
title: t("jobs.fields.owner"),
|
||||||
dataIndex: "owner",
|
dataIndex: "owner_name",
|
||||||
key: "owner",
|
key: "owner_name",
|
||||||
ellipsis: true,
|
sorter: (a, b) =>
|
||||||
sorter: (a, b) => alphaSort(a.job.ownr_ln, b.job.ownr_ln),
|
alphaSort(
|
||||||
width: "25%",
|
OwnerNameDisplayFunction(a.job),
|
||||||
sortOrder: sortcolumn === "owner" && sortorder,
|
OwnerNameDisplayFunction(b.job)
|
||||||
|
),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "owner_name" && state.sortedInfo.order,
|
||||||
render: (text, record) => {
|
render: (text, record) => {
|
||||||
return record.job.owner ? (
|
return record.job.ownerid ? (
|
||||||
<Link to={"/manage/owners/" + record.job.owner.id}>
|
<Link to={"/manage/owners/" + record.job.ownerid}>
|
||||||
<OwnerNameDisplay ownerObject={record.job} />
|
<OwnerNameDisplay ownerObject={record.job} />
|
||||||
</Link>
|
</Link>
|
||||||
) : (
|
) : (
|
||||||
@@ -64,9 +69,9 @@ export default function CsiResponseListPaginated({
|
|||||||
dataIndex: "completedon",
|
dataIndex: "completedon",
|
||||||
key: "completedon",
|
key: "completedon",
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
sorter: (a, b) => a.completedon - b.completedon,
|
sorter: (a, b) => dateSort(a.completedon, b.completedon),
|
||||||
width: "25%",
|
sortOrder:
|
||||||
sortOrder: sortcolumn === "completedon" && sortorder,
|
state.sortedInfo.columnKey === "completedon" && state.sortedInfo.order,
|
||||||
render: (text, record) => {
|
render: (text, record) => {
|
||||||
return record.completedon ? (
|
return record.completedon ? (
|
||||||
<DateFormatter>{record.completedon}</DateFormatter>
|
<DateFormatter>{record.completedon}</DateFormatter>
|
||||||
@@ -76,11 +81,12 @@ export default function CsiResponseListPaginated({
|
|||||||
];
|
];
|
||||||
|
|
||||||
const handleTableChange = (pagination, filters, sorter) => {
|
const handleTableChange = (pagination, filters, sorter) => {
|
||||||
setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
|
setState({
|
||||||
search.page = pagination.current;
|
...state,
|
||||||
search.sortcolumn = sorter.columnKey;
|
filteredInfo: filters,
|
||||||
search.sortorder = sorter.order;
|
sortedInfo: sorter,
|
||||||
history.push({ search: queryString.stringify(search) });
|
page: pagination.current,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleOnRowClick = (record) => {
|
const handleOnRowClick = (record) => {
|
||||||
@@ -108,7 +114,7 @@ export default function CsiResponseListPaginated({
|
|||||||
pagination={{
|
pagination={{
|
||||||
position: "top",
|
position: "top",
|
||||||
pageSize: pageLimit,
|
pageSize: pageLimit,
|
||||||
current: parseInt(page || 1),
|
current: parseInt(state.page || 1),
|
||||||
total: total,
|
total: total,
|
||||||
}}
|
}}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
@@ -122,13 +128,6 @@ export default function CsiResponseListPaginated({
|
|||||||
selectedRowKeys: [responseid],
|
selectedRowKeys: [responseid],
|
||||||
type: "radio",
|
type: "radio",
|
||||||
}}
|
}}
|
||||||
onRow={(record, rowIndex) => {
|
|
||||||
return {
|
|
||||||
onClick: (event) => {
|
|
||||||
handleOnRowClick(record);
|
|
||||||
}, // click row
|
|
||||||
};
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -3,21 +3,32 @@ import {
|
|||||||
ExclamationCircleFilled,
|
ExclamationCircleFilled,
|
||||||
PauseCircleOutlined,
|
PauseCircleOutlined,
|
||||||
} from "@ant-design/icons";
|
} from "@ant-design/icons";
|
||||||
import { Card, Space, Table, Tooltip } from "antd";
|
import { Card, Space, Switch, Table, Tooltip, Typography } from "antd";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
|
import { TimeFormatter } from "../../../utils/DateFormatter";
|
||||||
|
import { onlyUnique } from "../../../utils/arrayHelper";
|
||||||
|
import { alphaSort, dateSort } from "../../../utils/sorters";
|
||||||
|
import useLocalStorage from "../../../utils/useLocalStorage";
|
||||||
import ChatOpenButton from "../../chat-open-button/chat-open-button.component";
|
import ChatOpenButton from "../../chat-open-button/chat-open-button.component";
|
||||||
import OwnerNameDisplay from "../../owner-name-display/owner-name-display.component";
|
import OwnerNameDisplay, {
|
||||||
|
OwnerNameDisplayFunction,
|
||||||
|
} from "../../owner-name-display/owner-name-display.component";
|
||||||
import DashboardRefreshRequired from "../refresh-required.component";
|
import DashboardRefreshRequired from "../refresh-required.component";
|
||||||
import {pageLimit} from "../../../utils/config";
|
|
||||||
|
|
||||||
export default function DashboardScheduledInToday({ data, ...cardProps }) {
|
export default function DashboardScheduledInToday({ data, ...cardProps }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [state, setState] = useState({
|
const [state, setState] = useState({
|
||||||
sortedInfo: {},
|
sortedInfo: {},
|
||||||
|
filteredInfo: {},
|
||||||
});
|
});
|
||||||
|
const [isTvModeScheduledIn, setIsTvModeScheduledIn] = useLocalStorage(
|
||||||
|
"isTvModeScheduledIn",
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
if (!data) return null;
|
if (!data) return null;
|
||||||
if (!data.scheduled_in_today)
|
if (!data.scheduled_in_today)
|
||||||
return <DashboardRefreshRequired {...cardProps} />;
|
return <DashboardRefreshRequired {...cardProps} />;
|
||||||
@@ -31,6 +42,12 @@ export default function DashboardScheduledInToday({ data, ...cardProps }) {
|
|||||||
alt_transport: item.job.alt_transport,
|
alt_transport: item.job.alt_transport,
|
||||||
clm_no: item.job.clm_no,
|
clm_no: item.job.clm_no,
|
||||||
jobid: item.job.jobid,
|
jobid: item.job.jobid,
|
||||||
|
joblines_body: item.job.joblines
|
||||||
|
.filter((l) => l.mod_lbr_ty !== "LAR")
|
||||||
|
.reduce((acc, val) => acc + val.mod_lb_hrs, 0),
|
||||||
|
joblines_ref: item.job.joblines
|
||||||
|
.filter((l) => l.mod_lbr_ty === "LAR")
|
||||||
|
.reduce((acc, val) => acc + val.mod_lb_hrs, 0),
|
||||||
ins_co_nm: item.job.ins_co_nm,
|
ins_co_nm: item.job.ins_co_nm,
|
||||||
iouparent: item.job.iouparent,
|
iouparent: item.job.iouparent,
|
||||||
ownerid: item.job.ownerid,
|
ownerid: item.job.ownerid,
|
||||||
@@ -49,7 +66,7 @@ export default function DashboardScheduledInToday({ data, ...cardProps }) {
|
|||||||
v_vin: item.job.v_vin,
|
v_vin: item.job.v_vin,
|
||||||
vehicleid: item.job.vehicleid,
|
vehicleid: item.job.vehicleid,
|
||||||
note: item.note,
|
note: item.note,
|
||||||
start: moment(item.start).format("hh:mm a"),
|
start: item.start,
|
||||||
title: item.title,
|
title: item.title,
|
||||||
};
|
};
|
||||||
appt.push(i);
|
appt.push(i);
|
||||||
@@ -59,11 +76,192 @@ export default function DashboardScheduledInToday({ data, ...cardProps }) {
|
|||||||
return new moment(a.start) - new moment(b.start);
|
return new moment(a.start) - new moment(b.start);
|
||||||
});
|
});
|
||||||
|
|
||||||
const columns = [
|
const tvFontSize = 16;
|
||||||
|
const tvFontWeight = "bold";
|
||||||
|
|
||||||
|
const tvColumns = [
|
||||||
|
{
|
||||||
|
title: t("appointments.fields.time"),
|
||||||
|
dataIndex: "start",
|
||||||
|
key: "start",
|
||||||
|
ellipsis: true,
|
||||||
|
sorter: (a, b) => dateSort(a.start, b.start),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "start" && state.sortedInfo.order,
|
||||||
|
render: (text, record) => (
|
||||||
|
<span style={{ fontSize: tvFontSize, fontWeight: tvFontWeight }}>
|
||||||
|
<TimeFormatter>{record.start}</TimeFormatter>
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: t("jobs.fields.ro_number"),
|
title: t("jobs.fields.ro_number"),
|
||||||
dataIndex: "ro_number",
|
dataIndex: "ro_number",
|
||||||
key: "ro_number",
|
key: "ro_number",
|
||||||
|
sorter: (a, b) => alphaSort(a.ro_number, b.ro_number),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "ro_number" && state.sortedInfo.order,
|
||||||
|
render: (text, record) => (
|
||||||
|
<Link
|
||||||
|
to={"/manage/jobs/" + record.jobid}
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
>
|
||||||
|
<Space>
|
||||||
|
<span style={{ fontSize: tvFontSize, fontWeight: tvFontWeight }}>
|
||||||
|
{record.ro_number || t("general.labels.na")}
|
||||||
|
{record.production_vars && record.production_vars.alert ? (
|
||||||
|
<ExclamationCircleFilled className="production-alert" />
|
||||||
|
) : null}
|
||||||
|
{record.suspended && (
|
||||||
|
<PauseCircleOutlined style={{ color: "orangered" }} />
|
||||||
|
)}
|
||||||
|
{record.iouparent && (
|
||||||
|
<Tooltip title={t("jobs.labels.iou")}>
|
||||||
|
<BranchesOutlined style={{ color: "orangered" }} />
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
</Space>
|
||||||
|
</Link>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("jobs.fields.owner"),
|
||||||
|
dataIndex: "owner",
|
||||||
|
key: "owner",
|
||||||
|
ellipsis: true,
|
||||||
|
sorter: (a, b) =>
|
||||||
|
alphaSort(OwnerNameDisplayFunction(a), OwnerNameDisplayFunction(b)),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "owner" && state.sortedInfo.order,
|
||||||
|
render: (text, record) => {
|
||||||
|
return record.ownerid ? (
|
||||||
|
<Link
|
||||||
|
to={"/manage/owners/" + record.ownerid}
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
>
|
||||||
|
<span style={{ fontSize: tvFontSize, fontWeight: tvFontWeight }}>
|
||||||
|
<OwnerNameDisplay ownerObject={record} />
|
||||||
|
</span>
|
||||||
|
</Link>
|
||||||
|
) : (
|
||||||
|
<span style={{ fontSize: tvFontSize, fontWeight: tvFontWeight }}>
|
||||||
|
<OwnerNameDisplay ownerObject={record} />
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("jobs.fields.vehicle"),
|
||||||
|
dataIndex: "vehicle",
|
||||||
|
key: "vehicle",
|
||||||
|
ellipsis: true,
|
||||||
|
sorter: (a, b) =>
|
||||||
|
alphaSort(
|
||||||
|
`${a.v_model_yr || ""} ${a.v_make_desc || ""} ${
|
||||||
|
a.v_model_desc || ""
|
||||||
|
}`,
|
||||||
|
`${b.v_model_yr || ""} ${b.v_make_desc || ""} ${b.v_model_desc || ""}`
|
||||||
|
),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "vehicle" && state.sortedInfo.order,
|
||||||
|
render: (text, record) => {
|
||||||
|
return record.vehicleid ? (
|
||||||
|
<Link
|
||||||
|
to={"/manage/vehicles/" + record.vehicleid}
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
>
|
||||||
|
<span style={{ fontSize: tvFontSize, fontWeight: tvFontWeight }}>
|
||||||
|
{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
|
||||||
|
record.v_model_desc || ""
|
||||||
|
}`}
|
||||||
|
</span>
|
||||||
|
</Link>
|
||||||
|
) : (
|
||||||
|
<span style={{ fontSize: tvFontSize, fontWeight: tvFontWeight }}>{`${
|
||||||
|
record.v_model_yr || ""
|
||||||
|
} ${record.v_make_desc || ""} ${record.v_model_desc || ""}`}</span>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("appointments.fields.alt_transport"),
|
||||||
|
dataIndex: "alt_transport",
|
||||||
|
key: "alt_transport",
|
||||||
|
ellipsis: true,
|
||||||
|
sorter: (a, b) => alphaSort(a.alt_transport, b.alt_transport),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "alt_transport" &&
|
||||||
|
state.sortedInfo.order,
|
||||||
|
filters:
|
||||||
|
(appt &&
|
||||||
|
appt
|
||||||
|
.map((j) => j.alt_transport)
|
||||||
|
.filter(onlyUnique)
|
||||||
|
.map((s) => {
|
||||||
|
return {
|
||||||
|
text: s || "No Alt. Transport",
|
||||||
|
value: [s],
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.sort((a, b) => alphaSort(a.text, b.text))) ||
|
||||||
|
[],
|
||||||
|
onFilter: (value, record) => value.includes(record.alt_transport),
|
||||||
|
render: (text, record) => (
|
||||||
|
<span style={{ fontSize: tvFontSize, fontWeight: tvFontWeight }}>
|
||||||
|
{record.alt_transport}
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("jobs.fields.lab"),
|
||||||
|
dataIndex: "joblines_body",
|
||||||
|
key: "joblines_body",
|
||||||
|
sorter: (a, b) => a.joblines_body - b.joblines_body,
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "joblines_body" &&
|
||||||
|
state.sortedInfo.order,
|
||||||
|
align: "right",
|
||||||
|
render: (text, record) => (
|
||||||
|
<span style={{ fontSize: tvFontSize, fontWeight: tvFontWeight }}>
|
||||||
|
{record.joblines_body.toFixed(1)}
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("jobs.fields.lar"),
|
||||||
|
dataIndex: "joblines_ref",
|
||||||
|
key: "joblines_ref",
|
||||||
|
sorter: (a, b) => a.joblines_ref - b.joblines_ref,
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "joblines_ref" && state.sortedInfo.order,
|
||||||
|
align: "right",
|
||||||
|
render: (text, record) => (
|
||||||
|
<span style={{ fontSize: tvFontSize, fontWeight: tvFontWeight }}>
|
||||||
|
{record.joblines_ref.toFixed(1)}
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: t("appointments.fields.time"),
|
||||||
|
dataIndex: "start",
|
||||||
|
key: "start",
|
||||||
|
ellipsis: true,
|
||||||
|
sorter: (a, b) => dateSort(a.start, b.start),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "start" && state.sortedInfo.order,
|
||||||
|
render: (text, record) => <TimeFormatter>{record.start}</TimeFormatter>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("jobs.fields.ro_number"),
|
||||||
|
dataIndex: "ro_number",
|
||||||
|
key: "ro_number",
|
||||||
|
sorter: (a, b) => alphaSort(a.ro_number, b.ro_number),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "ro_number" && state.sortedInfo.order,
|
||||||
render: (text, record) => (
|
render: (text, record) => (
|
||||||
<Link
|
<Link
|
||||||
to={"/manage/jobs/" + record.jobid}
|
to={"/manage/jobs/" + record.jobid}
|
||||||
@@ -91,7 +289,10 @@ export default function DashboardScheduledInToday({ data, ...cardProps }) {
|
|||||||
dataIndex: "owner",
|
dataIndex: "owner",
|
||||||
key: "owner",
|
key: "owner",
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
responsive: ["md"],
|
sorter: (a, b) =>
|
||||||
|
alphaSort(OwnerNameDisplayFunction(a), OwnerNameDisplayFunction(b)),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "owner" && state.sortedInfo.order,
|
||||||
render: (text, record) => {
|
render: (text, record) => {
|
||||||
return record.ownerid ? (
|
return record.ownerid ? (
|
||||||
<Link
|
<Link
|
||||||
@@ -108,23 +309,16 @@ export default function DashboardScheduledInToday({ data, ...cardProps }) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("jobs.fields.ownr_ph1"),
|
title: t("dashboard.labels.phone"),
|
||||||
dataIndex: "ownr_ph1",
|
dataIndex: "ownr_ph",
|
||||||
key: "ownr_ph1",
|
key: "ownr_ph",
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
responsive: ["md"],
|
responsive: ["md"],
|
||||||
render: (text, record) => (
|
render: (text, record) => (
|
||||||
<ChatOpenButton phone={record.ownr_ph1} jobid={record.jobid} />
|
<Space size="small" wrap>
|
||||||
),
|
<ChatOpenButton phone={record.ownr_ph1} jobid={record.jobid} />
|
||||||
},
|
<ChatOpenButton phone={record.ownr_ph2} jobid={record.jobid} />
|
||||||
{
|
</Space>
|
||||||
title: t("jobs.fields.ownr_ph2"),
|
|
||||||
dataIndex: "ownr_ph2",
|
|
||||||
key: "ownr_ph2",
|
|
||||||
ellipsis: true,
|
|
||||||
responsive: ["md"],
|
|
||||||
render: (text, record) => (
|
|
||||||
<ChatOpenButton phone={record.ownr_ph2} jobid={record.jobid} />
|
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -134,7 +328,7 @@ export default function DashboardScheduledInToday({ data, ...cardProps }) {
|
|||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
responsive: ["md"],
|
responsive: ["md"],
|
||||||
render: (text, record) => (
|
render: (text, record) => (
|
||||||
<ChatOpenButton phone={record.ownr_ea} jobid={record.jobid} />
|
<a href={`mailto:${record.ownr_ea}`}>{record.ownr_ea}</a>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -142,6 +336,15 @@ export default function DashboardScheduledInToday({ data, ...cardProps }) {
|
|||||||
dataIndex: "vehicle",
|
dataIndex: "vehicle",
|
||||||
key: "vehicle",
|
key: "vehicle",
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
|
sorter: (a, b) =>
|
||||||
|
alphaSort(
|
||||||
|
`${a.v_model_yr || ""} ${a.v_make_desc || ""} ${
|
||||||
|
a.v_model_desc || ""
|
||||||
|
}`,
|
||||||
|
`${b.v_model_yr || ""} ${b.v_make_desc || ""} ${b.v_model_desc || ""}`
|
||||||
|
),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "vehicle" && state.sortedInfo.order,
|
||||||
render: (text, record) => {
|
render: (text, record) => {
|
||||||
return record.vehicleid ? (
|
return record.vehicleid ? (
|
||||||
<Link
|
<Link
|
||||||
@@ -165,43 +368,80 @@ export default function DashboardScheduledInToday({ data, ...cardProps }) {
|
|||||||
key: "ins_co_nm",
|
key: "ins_co_nm",
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
responsive: ["md"],
|
responsive: ["md"],
|
||||||
},
|
sorter: (a, b) => alphaSort(a.ins_co_nm, b.ins_co_nm),
|
||||||
{
|
sortOrder:
|
||||||
title: t("appointments.fields.time"),
|
state.sortedInfo.columnKey === "ins_co_nm" && state.sortedInfo.order,
|
||||||
dataIndex: "start",
|
filters:
|
||||||
key: "start",
|
(appt &&
|
||||||
ellipsis: true,
|
appt
|
||||||
responsive: ["md"],
|
.map((j) => j.ins_co_nm)
|
||||||
|
.filter(onlyUnique)
|
||||||
|
.map((s) => {
|
||||||
|
return {
|
||||||
|
text: s || "No Ins. Co.*",
|
||||||
|
value: [s],
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.sort((a, b) => alphaSort(a.text, b.text))) ||
|
||||||
|
[],
|
||||||
|
onFilter: (value, record) => value.includes(record.ins_co_nm),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("appointments.fields.alt_transport"),
|
title: t("appointments.fields.alt_transport"),
|
||||||
dataIndex: "alt_transport",
|
dataIndex: "alt_transport",
|
||||||
key: "alt_transport",
|
key: "alt_transport",
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
responsive: ["md"],
|
sorter: (a, b) => alphaSort(a.alt_transport, b.alt_transport),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "alt_transport" &&
|
||||||
|
state.sortedInfo.order,
|
||||||
|
filters:
|
||||||
|
(appt &&
|
||||||
|
appt
|
||||||
|
.map((j) => j.alt_transport)
|
||||||
|
.filter(onlyUnique)
|
||||||
|
.map((s) => {
|
||||||
|
return {
|
||||||
|
text: s || "No Alt. Transport",
|
||||||
|
value: [s],
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.sort((a, b) => alphaSort(a.text, b.text))) ||
|
||||||
|
[],
|
||||||
|
onFilter: (value, record) => value.includes(record.alt_transport),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const handleTableChange = (sorter) => {
|
const handleTableChange = (pagination, filters, sorter) => {
|
||||||
setState({ ...state, sortedInfo: sorter });
|
setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
title={t("dashboard.titles.scheduledintoday", {
|
title={t("dashboard.titles.scheduledindate", {
|
||||||
date: moment().startOf("day").format("MM/DD/YYYY"),
|
date: moment().startOf("day").format("MM/DD/YYYY"),
|
||||||
})}
|
})}
|
||||||
|
extra={
|
||||||
|
<Space>
|
||||||
|
<Typography.Text>{t("general.labels.tvmode")}</Typography.Text>
|
||||||
|
<Switch
|
||||||
|
onClick={() => setIsTvModeScheduledIn(!isTvModeScheduledIn)}
|
||||||
|
defaultChecked={isTvModeScheduledIn}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
}
|
||||||
{...cardProps}
|
{...cardProps}
|
||||||
>
|
>
|
||||||
<div style={{ height: "100%" }}>
|
<div style={{ height: "100%" }}>
|
||||||
<Table
|
<Table
|
||||||
onChange={handleTableChange}
|
onChange={handleTableChange}
|
||||||
pagination={{ position: "top", defaultPageSize: pageLimit }}
|
pagination={false}
|
||||||
columns={columns}
|
columns={isTvModeScheduledIn ? tvColumns : columns}
|
||||||
scroll={{ x: true, y: "calc(100% - 2em)" }}
|
scroll={{ x: true, y: "calc(100% - 2em)" }}
|
||||||
rowKey="id"
|
rowKey="id"
|
||||||
style={{ height: "85%" }}
|
style={{ height: "85%" }}
|
||||||
dataSource={appt}
|
dataSource={appt}
|
||||||
|
size={isTvModeScheduledIn ? "small" : "middle"}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
@@ -220,6 +460,10 @@ export const DashboardScheduledInTodayGql = `
|
|||||||
alt_transport
|
alt_transport
|
||||||
clm_no
|
clm_no
|
||||||
jobid: id
|
jobid: id
|
||||||
|
joblines(where: {removed: {_eq: false}}) {
|
||||||
|
mod_lb_hrs
|
||||||
|
mod_lbr_ty
|
||||||
|
}
|
||||||
ins_co_nm
|
ins_co_nm
|
||||||
iouparent
|
iouparent
|
||||||
ownerid
|
ownerid
|
||||||
|
|||||||
@@ -3,37 +3,272 @@ import {
|
|||||||
ExclamationCircleFilled,
|
ExclamationCircleFilled,
|
||||||
PauseCircleOutlined,
|
PauseCircleOutlined,
|
||||||
} from "@ant-design/icons";
|
} from "@ant-design/icons";
|
||||||
import { Card, Space, Table, Tooltip } from "antd";
|
import { Card, Space, Switch, Table, Tooltip, Typography } from "antd";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
|
import { TimeFormatter } from "../../../utils/DateFormatter";
|
||||||
|
import { onlyUnique } from "../../../utils/arrayHelper";
|
||||||
|
import { alphaSort, dateSort } from "../../../utils/sorters";
|
||||||
|
import useLocalStorage from "../../../utils/useLocalStorage";
|
||||||
import ChatOpenButton from "../../chat-open-button/chat-open-button.component";
|
import ChatOpenButton from "../../chat-open-button/chat-open-button.component";
|
||||||
import OwnerNameDisplay from "../../owner-name-display/owner-name-display.component";
|
import OwnerNameDisplay, {
|
||||||
|
OwnerNameDisplayFunction,
|
||||||
|
} from "../../owner-name-display/owner-name-display.component";
|
||||||
import DashboardRefreshRequired from "../refresh-required.component";
|
import DashboardRefreshRequired from "../refresh-required.component";
|
||||||
import {pageLimit} from "../../../utils/config";
|
|
||||||
|
|
||||||
export default function DashboardScheduledOutToday({ data, ...cardProps }) {
|
export default function DashboardScheduledOutToday({ data, ...cardProps }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [state, setState] = useState({
|
const [state, setState] = useState({
|
||||||
sortedInfo: {},
|
sortedInfo: {},
|
||||||
|
filteredInfo: {},
|
||||||
});
|
});
|
||||||
|
const [isTvModeScheduledOut, setIsTvModeScheduledOut] = useLocalStorage(
|
||||||
|
"isTvModeScheduledOut",
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
if (!data) return null;
|
if (!data) return null;
|
||||||
if (!data.scheduled_out_today)
|
if (!data.scheduled_out_today)
|
||||||
return <DashboardRefreshRequired {...cardProps} />;
|
return <DashboardRefreshRequired {...cardProps} />;
|
||||||
|
|
||||||
data.scheduled_out_today.forEach((item) => {
|
data.scheduled_out_today.forEach((item) => {
|
||||||
item.scheduled_completion= moment(item.scheduled_completion).format("hh:mm a")
|
item.joblines_body = item.joblines
|
||||||
|
? item.joblines
|
||||||
|
.filter((l) => l.mod_lbr_ty !== "LAR")
|
||||||
|
.reduce((acc, val) => acc + val.mod_lb_hrs, 0)
|
||||||
|
: 0;
|
||||||
|
item.joblines_ref = item.joblines
|
||||||
|
? item.joblines
|
||||||
|
.filter((l) => l.mod_lbr_ty === "LAR")
|
||||||
|
.reduce((acc, val) => acc + val.mod_lb_hrs, 0)
|
||||||
|
: 0;
|
||||||
});
|
});
|
||||||
data.scheduled_out_today.sort(function (a, b) {
|
data.scheduled_out_today.sort(function (a, b) {
|
||||||
return new Date(a.scheduled_completion) - new Date(b.scheduled_completion);
|
return new Date(a.scheduled_completion) - new Date(b.scheduled_completion);
|
||||||
});
|
});
|
||||||
|
|
||||||
const columns = [
|
const tvFontSize = 18;
|
||||||
|
const tvFontWeight = "bold";
|
||||||
|
|
||||||
|
const tvColumns = [
|
||||||
|
{
|
||||||
|
title: t("jobs.fields.scheduled_completion"),
|
||||||
|
dataIndex: "scheduled_completion",
|
||||||
|
key: "scheduled_completion",
|
||||||
|
ellipsis: true,
|
||||||
|
sorter: (a, b) =>
|
||||||
|
dateSort(a.scheduled_completion, b.scheduled_completion),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "scheduled_completion" &&
|
||||||
|
state.sortedInfo.order,
|
||||||
|
render: (text, record) => (
|
||||||
|
<span style={{ fontSize: tvFontSize, fontWeight: tvFontWeight }}>
|
||||||
|
<TimeFormatter>{record.scheduled_completion}</TimeFormatter>
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: t("jobs.fields.ro_number"),
|
title: t("jobs.fields.ro_number"),
|
||||||
dataIndex: "ro_number",
|
dataIndex: "ro_number",
|
||||||
key: "ro_number",
|
key: "ro_number",
|
||||||
|
sorter: (a, b) => alphaSort(a.ro_number, b.ro_number),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "ro_number" && state.sortedInfo.order,
|
||||||
|
render: (text, record) => (
|
||||||
|
<Link
|
||||||
|
to={"/manage/jobs/" + record.jobid}
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
>
|
||||||
|
<Space>
|
||||||
|
<span style={{ fontSize: tvFontSize, fontWeight: tvFontWeight }}>
|
||||||
|
{record.ro_number || t("general.labels.na")}
|
||||||
|
{record.production_vars && record.production_vars.alert ? (
|
||||||
|
<ExclamationCircleFilled className="production-alert" />
|
||||||
|
) : null}
|
||||||
|
{record.suspended && (
|
||||||
|
<PauseCircleOutlined style={{ color: "orangered" }} />
|
||||||
|
)}
|
||||||
|
{record.iouparent && (
|
||||||
|
<Tooltip title={t("jobs.labels.iou")}>
|
||||||
|
<BranchesOutlined style={{ color: "orangered" }} />
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
</Space>
|
||||||
|
</Link>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("jobs.fields.owner"),
|
||||||
|
dataIndex: "owner",
|
||||||
|
key: "owner",
|
||||||
|
ellipsis: true,
|
||||||
|
sorter: (a, b) =>
|
||||||
|
alphaSort(OwnerNameDisplayFunction(a), OwnerNameDisplayFunction(b)),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "owner" && state.sortedInfo.order,
|
||||||
|
render: (text, record) => {
|
||||||
|
return record.ownerid ? (
|
||||||
|
<Link
|
||||||
|
to={"/manage/owners/" + record.ownerid}
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
>
|
||||||
|
<span style={{ fontSize: tvFontSize, fontWeight: tvFontWeight }}>
|
||||||
|
<OwnerNameDisplay ownerObject={record} />
|
||||||
|
</span>
|
||||||
|
</Link>
|
||||||
|
) : (
|
||||||
|
<span style={{ fontSize: tvFontSize, fontWeight: tvFontWeight }}>
|
||||||
|
<OwnerNameDisplay ownerObject={record} />
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("jobs.fields.vehicle"),
|
||||||
|
dataIndex: "vehicle",
|
||||||
|
key: "vehicle",
|
||||||
|
ellipsis: true,
|
||||||
|
sorter: (a, b) =>
|
||||||
|
alphaSort(
|
||||||
|
`${a.v_model_yr || ""} ${a.v_make_desc || ""} ${
|
||||||
|
a.v_model_desc || ""
|
||||||
|
}`,
|
||||||
|
`${b.v_model_yr || ""} ${b.v_make_desc || ""} ${b.v_model_desc || ""}`
|
||||||
|
),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "vehicle" && state.sortedInfo.order,
|
||||||
|
render: (text, record) => {
|
||||||
|
return record.vehicleid ? (
|
||||||
|
<Link
|
||||||
|
to={"/manage/vehicles/" + record.vehicleid}
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
>
|
||||||
|
<span style={{ fontSize: tvFontSize, fontWeight: tvFontWeight }}>
|
||||||
|
{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
|
||||||
|
record.v_model_desc || ""
|
||||||
|
}`}
|
||||||
|
</span>
|
||||||
|
</Link>
|
||||||
|
) : (
|
||||||
|
<span style={{ fontSize: tvFontSize, fontWeight: tvFontWeight }}>{`${
|
||||||
|
record.v_model_yr || ""
|
||||||
|
} ${record.v_make_desc || ""} ${record.v_model_desc || ""}`}</span>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("appointments.fields.alt_transport"),
|
||||||
|
dataIndex: "alt_transport",
|
||||||
|
key: "alt_transport",
|
||||||
|
ellipsis: true,
|
||||||
|
sorter: (a, b) => alphaSort(a.alt_transport, b.alt_transport),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "alt_transport" &&
|
||||||
|
state.sortedInfo.order,
|
||||||
|
filters:
|
||||||
|
(data.scheduled_out_today &&
|
||||||
|
data.scheduled_out_today
|
||||||
|
.map((j) => j.alt_transport)
|
||||||
|
.filter(onlyUnique)
|
||||||
|
.map((s) => {
|
||||||
|
return {
|
||||||
|
text: s || "No Alt. Transport*",
|
||||||
|
value: [s],
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.sort((a, b) => alphaSort(a.text, b.text))) ||
|
||||||
|
[],
|
||||||
|
onFilter: (value, record) => value.includes(record.alt_transport),
|
||||||
|
render: (text, record) => (
|
||||||
|
<span style={{ fontSize: tvFontSize, fontWeight: tvFontWeight }}>
|
||||||
|
{record.alt_transport}
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("jobs.fields.status"),
|
||||||
|
dataIndex: "status",
|
||||||
|
key: "status",
|
||||||
|
ellipsis: true,
|
||||||
|
sorter: (a, b) => alphaSort(a.alt_transport, b.alt_transport),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "status" && state.sortedInfo.order,
|
||||||
|
filters:
|
||||||
|
(data.scheduled_out_today &&
|
||||||
|
data.scheduled_out_today
|
||||||
|
.map((j) => j.status)
|
||||||
|
.filter(onlyUnique)
|
||||||
|
.map((s) => {
|
||||||
|
return {
|
||||||
|
text: s || "No Status*",
|
||||||
|
value: [s],
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.sort((a, b) => alphaSort(a.text, b.text))) ||
|
||||||
|
[],
|
||||||
|
onFilter: (value, record) => value.includes(record.status),
|
||||||
|
render: (text, record) => (
|
||||||
|
<span style={{ fontSize: tvFontSize, fontWeight: tvFontWeight }}>
|
||||||
|
{record.status}
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("jobs.fields.lab"),
|
||||||
|
dataIndex: "joblines_body",
|
||||||
|
key: "joblines_body",
|
||||||
|
sorter: (a, b) => a.joblines_body - b.joblines_body,
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "joblines_body" &&
|
||||||
|
state.sortedInfo.order,
|
||||||
|
align: "right",
|
||||||
|
render: (text, record) => (
|
||||||
|
<span style={{ fontSize: tvFontSize, fontWeight: tvFontWeight }}>
|
||||||
|
{record.joblines_body.toFixed(1)}
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("jobs.fields.lar"),
|
||||||
|
dataIndex: "joblines_ref",
|
||||||
|
key: "joblines_ref",
|
||||||
|
sorter: (a, b) => a.joblines_ref - b.joblines_ref,
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "joblines_ref" && state.sortedInfo.order,
|
||||||
|
align: "right",
|
||||||
|
render: (text, record) => (
|
||||||
|
<span style={{ fontSize: tvFontSize, fontWeight: tvFontWeight }}>
|
||||||
|
{record.joblines_ref.toFixed(1)}
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: t("jobs.fields.scheduled_completion"),
|
||||||
|
dataIndex: "scheduled_completion",
|
||||||
|
key: "scheduled_completion",
|
||||||
|
ellipsis: true,
|
||||||
|
sorter: (a, b) =>
|
||||||
|
dateSort(a.scheduled_completion, b.scheduled_completion),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "scheduled_completion" &&
|
||||||
|
state.sortedInfo.order,
|
||||||
|
render: (text, record) => (
|
||||||
|
<TimeFormatter>{record.scheduled_completion}</TimeFormatter>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("jobs.fields.ro_number"),
|
||||||
|
dataIndex: "ro_number",
|
||||||
|
key: "ro_number",
|
||||||
|
sorter: (a, b) => alphaSort(a.ro_number, b.ro_number),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "ro_number" && state.sortedInfo.order,
|
||||||
render: (text, record) => (
|
render: (text, record) => (
|
||||||
<Link
|
<Link
|
||||||
to={"/manage/jobs/" + record.jobid}
|
to={"/manage/jobs/" + record.jobid}
|
||||||
@@ -61,7 +296,10 @@ export default function DashboardScheduledOutToday({ data, ...cardProps }) {
|
|||||||
dataIndex: "owner",
|
dataIndex: "owner",
|
||||||
key: "owner",
|
key: "owner",
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
responsive: ["md"],
|
sorter: (a, b) =>
|
||||||
|
alphaSort(OwnerNameDisplayFunction(a), OwnerNameDisplayFunction(b)),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "owner" && state.sortedInfo.order,
|
||||||
render: (text, record) => {
|
render: (text, record) => {
|
||||||
return record.ownerid ? (
|
return record.ownerid ? (
|
||||||
<Link
|
<Link
|
||||||
@@ -78,23 +316,16 @@ export default function DashboardScheduledOutToday({ data, ...cardProps }) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("jobs.fields.ownr_ph1"),
|
title: t("dashboard.labels.phone"),
|
||||||
dataIndex: "ownr_ph1",
|
dataIndex: "ownr_ph",
|
||||||
key: "ownr_ph1",
|
key: "ownr_ph",
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
responsive: ["md"],
|
responsive: ["md"],
|
||||||
render: (text, record) => (
|
render: (text, record) => (
|
||||||
<ChatOpenButton phone={record.ownr_ph1} jobid={record.jobid} />
|
<Space size="small" wrap>
|
||||||
),
|
<ChatOpenButton phone={record.ownr_ph1} jobid={record.jobid} />
|
||||||
},
|
<ChatOpenButton phone={record.ownr_ph2} jobid={record.jobid} />
|
||||||
{
|
</Space>
|
||||||
title: t("jobs.fields.ownr_ph2"),
|
|
||||||
dataIndex: "ownr_ph2",
|
|
||||||
key: "ownr_ph2",
|
|
||||||
ellipsis: true,
|
|
||||||
responsive: ["md"],
|
|
||||||
render: (text, record) => (
|
|
||||||
<ChatOpenButton phone={record.ownr_ph2} jobid={record.jobid} />
|
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -104,7 +335,7 @@ export default function DashboardScheduledOutToday({ data, ...cardProps }) {
|
|||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
responsive: ["md"],
|
responsive: ["md"],
|
||||||
render: (text, record) => (
|
render: (text, record) => (
|
||||||
<ChatOpenButton phone={record.ownr_ea} jobid={record.jobid} />
|
<a href={`mailto:${record.ownr_ea}`}>{record.ownr_ea}</a>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -112,6 +343,15 @@ export default function DashboardScheduledOutToday({ data, ...cardProps }) {
|
|||||||
dataIndex: "vehicle",
|
dataIndex: "vehicle",
|
||||||
key: "vehicle",
|
key: "vehicle",
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
|
sorter: (a, b) =>
|
||||||
|
alphaSort(
|
||||||
|
`${a.v_model_yr || ""} ${a.v_make_desc || ""} ${
|
||||||
|
a.v_model_desc || ""
|
||||||
|
}`,
|
||||||
|
`${b.v_model_yr || ""} ${b.v_make_desc || ""} ${b.v_model_desc || ""}`
|
||||||
|
),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "vehicle" && state.sortedInfo.order,
|
||||||
render: (text, record) => {
|
render: (text, record) => {
|
||||||
return record.vehicleid ? (
|
return record.vehicleid ? (
|
||||||
<Link
|
<Link
|
||||||
@@ -135,43 +375,80 @@ export default function DashboardScheduledOutToday({ data, ...cardProps }) {
|
|||||||
key: "ins_co_nm",
|
key: "ins_co_nm",
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
responsive: ["md"],
|
responsive: ["md"],
|
||||||
},
|
sorter: (a, b) => alphaSort(a.ins_co_nm, b.ins_co_nm),
|
||||||
{
|
sortOrder:
|
||||||
title: t("jobs.fields.scheduled_completion"),
|
state.sortedInfo.columnKey === "ins_co_nm" && state.sortedInfo.order,
|
||||||
dataIndex: "scheduled_completion",
|
filters:
|
||||||
key: "scheduled_completion",
|
(data.scheduled_out_today &&
|
||||||
ellipsis: true,
|
data.scheduled_out_today
|
||||||
responsive: ["md"],
|
.map((j) => j.ins_co_nm)
|
||||||
|
.filter(onlyUnique)
|
||||||
|
.map((s) => {
|
||||||
|
return {
|
||||||
|
text: s || "No Ins. Co.*",
|
||||||
|
value: [s],
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.sort((a, b) => alphaSort(a.text, b.text))) ||
|
||||||
|
[],
|
||||||
|
onFilter: (value, record) => value.includes(record.ins_co_nm),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("appointments.fields.alt_transport"),
|
title: t("appointments.fields.alt_transport"),
|
||||||
dataIndex: "alt_transport",
|
dataIndex: "alt_transport",
|
||||||
key: "alt_transport",
|
key: "alt_transport",
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
responsive: ["md"],
|
sorter: (a, b) => alphaSort(a.alt_transport, b.alt_transport),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "alt_transport" &&
|
||||||
|
state.sortedInfo.order,
|
||||||
|
filters:
|
||||||
|
(data.scheduled_out_today &&
|
||||||
|
data.scheduled_out_today
|
||||||
|
.map((j) => j.alt_transport)
|
||||||
|
.filter(onlyUnique)
|
||||||
|
.map((s) => {
|
||||||
|
return {
|
||||||
|
text: s || "No Alt. Transport*",
|
||||||
|
value: [s],
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.sort((a, b) => alphaSort(a.text, b.text))) ||
|
||||||
|
[],
|
||||||
|
onFilter: (value, record) => value.includes(record.alt_transport),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const handleTableChange = (sorter) => {
|
const handleTableChange = (pagination, filters, sorter) => {
|
||||||
setState({ ...state, sortedInfo: sorter });
|
setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
title={t("dashboard.titles.scheduledouttoday", {
|
title={t("dashboard.titles.scheduledoutdate", {
|
||||||
date: moment().startOf("day").format("MM/DD/YYYY"),
|
date: moment().startOf("day").format("MM/DD/YYYY"),
|
||||||
})}
|
})}
|
||||||
|
extra={
|
||||||
|
<Space>
|
||||||
|
<Typography.Text>{t("general.labels.tvmode")}</Typography.Text>
|
||||||
|
<Switch
|
||||||
|
onClick={() => setIsTvModeScheduledOut(!isTvModeScheduledOut)}
|
||||||
|
defaultChecked={isTvModeScheduledOut}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
}
|
||||||
{...cardProps}
|
{...cardProps}
|
||||||
>
|
>
|
||||||
<div style={{ height: "100%" }}>
|
<div style={{ height: "100%" }}>
|
||||||
<Table
|
<Table
|
||||||
onChange={handleTableChange}
|
onChange={handleTableChange}
|
||||||
pagination={{ position: "top", defaultPageSize: pageLimit }}
|
pagination={false}
|
||||||
columns={columns}
|
columns={isTvModeScheduledOut ? tvColumns : columns}
|
||||||
scroll={{ x: true, y: "calc(100% - 2em)" }}
|
scroll={{ x: true, y: "calc(100% - 2em)" }}
|
||||||
rowKey="id"
|
rowKey="id"
|
||||||
style={{ height: "85%" }}
|
style={{ height: "85%" }}
|
||||||
dataSource={data.scheduled_out_today}
|
dataSource={data.scheduled_out_today}
|
||||||
|
size={isTvModeScheduledOut ? "small" : "middle"}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
@@ -188,6 +465,10 @@ export const DashboardScheduledOutTodayGql = `
|
|||||||
alt_transport
|
alt_transport
|
||||||
clm_no
|
clm_no
|
||||||
jobid: id
|
jobid: id
|
||||||
|
joblines(where: {removed: {_eq: false}}) {
|
||||||
|
mod_lb_hrs
|
||||||
|
mod_lbr_ty
|
||||||
|
}
|
||||||
ins_co_nm
|
ins_co_nm
|
||||||
iouparent
|
iouparent
|
||||||
ownerid
|
ownerid
|
||||||
@@ -200,6 +481,7 @@ export const DashboardScheduledOutTodayGql = `
|
|||||||
production_vars
|
production_vars
|
||||||
ro_number
|
ro_number
|
||||||
scheduled_completion
|
scheduled_completion
|
||||||
|
status
|
||||||
suspended
|
suspended
|
||||||
v_make_desc
|
v_make_desc
|
||||||
v_model_desc
|
v_model_desc
|
||||||
|
|||||||
@@ -275,26 +275,22 @@ const componentList = {
|
|||||||
h: 2,
|
h: 2,
|
||||||
},
|
},
|
||||||
ScheduleInToday: {
|
ScheduleInToday: {
|
||||||
label: i18next.t("dashboard.titles.scheduledintoday", {
|
label: i18next.t("dashboard.titles.scheduledintoday"),
|
||||||
date: moment().startOf("day").format("MM/DD/YYYY"),
|
|
||||||
}),
|
|
||||||
component: DashboardScheduledInToday,
|
component: DashboardScheduledInToday,
|
||||||
gqlFragment: DashboardScheduledInTodayGql,
|
gqlFragment: DashboardScheduledInTodayGql,
|
||||||
minW: 10,
|
minW: 6,
|
||||||
minH: 2,
|
minH: 2,
|
||||||
w: 10,
|
w: 10,
|
||||||
h: 2,
|
h: 3,
|
||||||
},
|
},
|
||||||
ScheduleOutToday: {
|
ScheduleOutToday: {
|
||||||
label: i18next.t("dashboard.titles.scheduledouttoday", {
|
label: i18next.t("dashboard.titles.scheduledouttoday"),
|
||||||
date: moment().startOf("day").format("MM/DD/YYYY"),
|
|
||||||
}),
|
|
||||||
component: DashboardScheduledOutToday,
|
component: DashboardScheduledOutToday,
|
||||||
gqlFragment: DashboardScheduledOutTodayGql,
|
gqlFragment: DashboardScheduledOutTodayGql,
|
||||||
minW: 10,
|
minW: 6,
|
||||||
minH: 2,
|
minH: 2,
|
||||||
w: 10,
|
w: 10,
|
||||||
h: 2,
|
h: 3,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -306,8 +302,7 @@ const createDashboardQuery = (state) => {
|
|||||||
.map((item, index) => componentList[item.i].gqlFragment || "")
|
.map((item, index) => componentList[item.i].gqlFragment || "")
|
||||||
.join("");
|
.join("");
|
||||||
return gql`
|
return gql`
|
||||||
query QUERY_DASHBOARD_DETAILS {
|
query QUERY_DASHBOARD_DETAILS { ${componentBasedAdditions || ""}
|
||||||
${componentBasedAdditions || ""}
|
|
||||||
monthly_sales: jobs(where: {_and: [
|
monthly_sales: jobs(where: {_and: [
|
||||||
{ voided: {_eq: false}},
|
{ voided: {_eq: false}},
|
||||||
{date_invoiced: {_gte: "${moment()
|
{date_invoiced: {_gte: "${moment()
|
||||||
@@ -317,11 +312,11 @@ const createDashboardQuery = (state) => {
|
|||||||
.endOf("month")
|
.endOf("month")
|
||||||
.endOf("day")
|
.endOf("day")
|
||||||
.toISOString()}"}}]}) {
|
.toISOString()}"}}]}) {
|
||||||
id
|
id
|
||||||
ro_number
|
ro_number
|
||||||
date_invoiced
|
date_invoiced
|
||||||
job_totals
|
job_totals
|
||||||
rate_la1
|
rate_la1
|
||||||
rate_la2
|
rate_la2
|
||||||
rate_la3
|
rate_la3
|
||||||
rate_la4
|
rate_la4
|
||||||
@@ -344,43 +339,42 @@ const createDashboardQuery = (state) => {
|
|||||||
rate_mapa
|
rate_mapa
|
||||||
rate_mash
|
rate_mash
|
||||||
rate_matd
|
rate_matd
|
||||||
joblines(where: { removed: { _eq: false } }) {
|
joblines(where: { removed: { _eq: false } }) {
|
||||||
id
|
id
|
||||||
mod_lbr_ty
|
mod_lbr_ty
|
||||||
mod_lb_hrs
|
mod_lb_hrs
|
||||||
act_price
|
act_price
|
||||||
part_qty
|
part_qty
|
||||||
part_type
|
part_type
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
production_jobs: jobs(where: { inproduction: { _eq: true } }) {
|
||||||
production_jobs: jobs(where: { inproduction: { _eq: true } }) {
|
id
|
||||||
|
ro_number
|
||||||
|
ins_co_nm
|
||||||
|
job_totals
|
||||||
|
joblines(where: { removed: { _eq: false } }) {
|
||||||
id
|
id
|
||||||
ro_number
|
mod_lbr_ty
|
||||||
ins_co_nm
|
mod_lb_hrs
|
||||||
job_totals
|
act_price
|
||||||
joblines(where: { removed: { _eq: false } }) {
|
part_qty
|
||||||
id
|
part_type
|
||||||
mod_lbr_ty
|
}
|
||||||
mod_lb_hrs
|
labhrs: joblines_aggregate(where: { mod_lbr_ty: { _neq: "LAR" }, removed: { _eq: false } }) {
|
||||||
act_price
|
aggregate {
|
||||||
part_qty
|
sum {
|
||||||
part_type
|
mod_lb_hrs
|
||||||
}
|
|
||||||
labhrs: joblines_aggregate(where: { mod_lbr_ty: { _neq: "LAR" }, removed: { _eq: false } }) {
|
|
||||||
aggregate {
|
|
||||||
sum {
|
|
||||||
mod_lb_hrs
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
larhrs: joblines_aggregate(where: { mod_lbr_ty: { _eq: "LAR" }, removed: { _eq: false } }) {
|
}
|
||||||
aggregate {
|
larhrs: joblines_aggregate(where: { mod_lbr_ty: { _eq: "LAR" }, removed: { _eq: false } }) {
|
||||||
sum {
|
aggregate {
|
||||||
mod_lb_hrs
|
sum {
|
||||||
}
|
mod_lb_hrs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
}`;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -128,7 +128,7 @@
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
.ant-card-body {
|
.ant-card-body {
|
||||||
height: 80%;
|
height: calc(100% - 2rem);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
// // background-color: red;
|
// // background-color: red;
|
||||||
// height: 90%;
|
// height: 90%;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { Button, Col, Collapse, Result, Row, Space } from "antd";
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { withTranslation } from "react-i18next";
|
import { withTranslation } from "react-i18next";
|
||||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||||
|
import * as Sentry from "@sentry/react";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import {
|
import {
|
||||||
@@ -138,7 +138,6 @@ class ErrorBoundary extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect(
|
export default Sentry.withErrorBoundary(
|
||||||
mapStateToProps,
|
connect(mapStateToProps, mapDispatchToProps)(withTranslation()(ErrorBoundary))
|
||||||
mapDispatchToProps
|
);
|
||||||
)(withTranslation()(ErrorBoundary));
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import Icon, {
|
|||||||
FileFilled,
|
FileFilled,
|
||||||
//GlobalOutlined,
|
//GlobalOutlined,
|
||||||
HomeFilled,
|
HomeFilled,
|
||||||
ImportOutlined,
|
ImportOutlined, InfoCircleOutlined,
|
||||||
LineChartOutlined,
|
LineChartOutlined,
|
||||||
PaperClipOutlined,
|
PaperClipOutlined,
|
||||||
PhoneOutlined,
|
PhoneOutlined,
|
||||||
@@ -26,8 +26,8 @@ import Icon, {
|
|||||||
UserOutlined,
|
UserOutlined,
|
||||||
} from "@ant-design/icons";
|
} from "@ant-design/icons";
|
||||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||||
import { Layout, Menu } from "antd";
|
import {Layout, Menu, Switch, Tooltip} from "antd";
|
||||||
import React from "react";
|
import React, {useEffect, useState} from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { BsKanban } from "react-icons/bs";
|
import { BsKanban } from "react-icons/bs";
|
||||||
import {
|
import {
|
||||||
@@ -52,6 +52,7 @@ import {
|
|||||||
selectBodyshop,
|
selectBodyshop,
|
||||||
selectCurrentUser,
|
selectCurrentUser,
|
||||||
} from "../../redux/user/user.selectors";
|
} from "../../redux/user/user.selectors";
|
||||||
|
import {handleBeta, setBeta, checkBeta} from "../../utils/handleBeta";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
currentUser: selectCurrentUser,
|
currentUser: selectCurrentUser,
|
||||||
@@ -102,9 +103,21 @@ function Header({
|
|||||||
{},
|
{},
|
||||||
bodyshop && bodyshop.imexshopid
|
bodyshop && bodyshop.imexshopid
|
||||||
);
|
);
|
||||||
|
const [betaSwitch, setBetaSwitch] = useState(false);
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const isBeta = checkBeta();
|
||||||
|
setBetaSwitch(isBeta);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const betaSwitchChange = (checked) => {
|
||||||
|
setBeta(checked);
|
||||||
|
setBetaSwitch(checked);
|
||||||
|
handleBeta();
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout.Header>
|
<Layout.Header>
|
||||||
<Menu
|
<Menu
|
||||||
@@ -431,6 +444,17 @@ function Header({
|
|||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
))}
|
))}
|
||||||
</Menu.SubMenu>
|
</Menu.SubMenu>
|
||||||
|
<Menu.Item style={{marginLeft: 'auto'}} key="profile">
|
||||||
|
<Tooltip title="A more modern ImEX Online is ready for you to try! You can switch back at any time.">
|
||||||
|
<InfoCircleOutlined/>
|
||||||
|
<span style={{marginRight: 8}}>Try the new ImEX Online</span>
|
||||||
|
<Switch
|
||||||
|
checked={betaSwitch}
|
||||||
|
onChange={betaSwitchChange}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
</Menu.Item>
|
||||||
|
|
||||||
</Menu>
|
</Menu>
|
||||||
</Layout.Header>
|
</Layout.Header>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -7,21 +7,31 @@ import { connect } from "react-redux";
|
|||||||
import { useHistory } from "react-router";
|
import { useHistory } from "react-router";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import { UPDATE_JOB_LINES_IOU } from "../../graphql/jobs-lines.queries";
|
import { UPDATE_JOB_LINES_IOU } from "../../graphql/jobs-lines.queries";
|
||||||
|
import { selectTechnician } from "../../redux/tech/tech.selectors";
|
||||||
import {
|
import {
|
||||||
selectBodyshop,
|
selectBodyshop,
|
||||||
selectCurrentUser,
|
selectCurrentUser,
|
||||||
} from "../../redux/user/user.selectors";
|
} from "../../redux/user/user.selectors";
|
||||||
import { CreateIouForJob } from "../jobs-detail-header-actions/jobs-detail-header-actions.duplicate.util";
|
import { CreateIouForJob } from "../jobs-detail-header-actions/jobs-detail-header-actions.duplicate.util";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
currentUser: selectCurrentUser,
|
currentUser: selectCurrentUser,
|
||||||
|
technician: selectTechnician,
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||||
});
|
});
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(JobCreateIOU);
|
export default connect(mapStateToProps, mapDispatchToProps)(JobCreateIOU);
|
||||||
|
|
||||||
export function JobCreateIOU({ bodyshop, currentUser, job, selectedJobLines }) {
|
export function JobCreateIOU({
|
||||||
|
bodyshop,
|
||||||
|
currentUser,
|
||||||
|
job,
|
||||||
|
selectedJobLines,
|
||||||
|
technician,
|
||||||
|
}) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const client = useApolloClient();
|
const client = useApolloClient();
|
||||||
@@ -79,13 +89,19 @@ export function JobCreateIOU({ bodyshop, currentUser, job, selectedJobLines }) {
|
|||||||
title={t("jobs.labels.createiouwarning")}
|
title={t("jobs.labels.createiouwarning")}
|
||||||
onConfirm={handleCreateIou}
|
onConfirm={handleCreateIou}
|
||||||
disabled={
|
disabled={
|
||||||
!selectedJobLines || selectedJobLines.length === 0 || !job.converted
|
!selectedJobLines ||
|
||||||
|
selectedJobLines.length === 0 ||
|
||||||
|
!job.converted ||
|
||||||
|
technician
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
loading={loading}
|
loading={loading}
|
||||||
disabled={
|
disabled={
|
||||||
!selectedJobLines || selectedJobLines.length === 0 || !job.converted
|
!selectedJobLines ||
|
||||||
|
selectedJobLines.length === 0 ||
|
||||||
|
!job.converted ||
|
||||||
|
technician
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{t("jobs.actions.createiou")}
|
{t("jobs.actions.createiou")}
|
||||||
|
|||||||
@@ -1,15 +1,37 @@
|
|||||||
import React from "react";
|
|
||||||
import { Card } from "antd";
|
import { Card } from "antd";
|
||||||
|
import React from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { connect } from "react-redux";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import { selectTechnician } from "../../redux/tech/tech.selectors";
|
||||||
|
|
||||||
export default function JobDetailCardTemplate({
|
const mapStateToProps = createStructuredSelector({
|
||||||
|
technician: selectTechnician,
|
||||||
|
});
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(JobDetailCardTemplate);
|
||||||
|
|
||||||
|
export function JobDetailCardTemplate({
|
||||||
loading,
|
loading,
|
||||||
title,
|
title,
|
||||||
extraLink,
|
extraLink,
|
||||||
|
technician,
|
||||||
...otherProps
|
...otherProps
|
||||||
}) {
|
}) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
let extra;
|
let extra;
|
||||||
if (extraLink) extra = { extra: <Link to={extraLink}>More</Link> };
|
if (extraLink && !technician)
|
||||||
|
extra = {
|
||||||
|
extra: <Link to={extraLink}>{t("jobs.labels.cards.more")}</Link>,
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
size="small"
|
size="small"
|
||||||
|
|||||||
246
client/src/components/job-lifecycle/job-lifecycle.component.jsx
Normal file
246
client/src/components/job-lifecycle/job-lifecycle.component.jsx
Normal file
@@ -0,0 +1,246 @@
|
|||||||
|
import React, {useCallback, useEffect, useState} from 'react';
|
||||||
|
import moment from "moment";
|
||||||
|
import axios from 'axios';
|
||||||
|
import {Badge, Card, Space, Table, Tag} from 'antd';
|
||||||
|
import {gql, useQuery} from "@apollo/client";
|
||||||
|
import {DateTimeFormatterFunction} from "../../utils/DateFormatter";
|
||||||
|
import {isEmpty} from "lodash";
|
||||||
|
import {useTranslation} from "react-i18next";
|
||||||
|
|
||||||
|
require('./job-lifecycle.styles.scss');
|
||||||
|
|
||||||
|
// show text on bar if text can fit
|
||||||
|
export function JobLifecycleComponent({job, statuses, ...rest}) {
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
const [lifecycleData, setLifecycleData] = useState(null);
|
||||||
|
const {t} = useTranslation(); // Used for tracking external state changes.
|
||||||
|
|
||||||
|
const {data} = useQuery(gql`
|
||||||
|
query get_job_test($id: uuid!){
|
||||||
|
jobs_by_pk(id:$id){
|
||||||
|
id
|
||||||
|
status
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`, {
|
||||||
|
variables: {
|
||||||
|
id: job.id
|
||||||
|
},
|
||||||
|
fetchPolicy: 'cache-only'
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the lifecycle data for the job.
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
const getLifecycleData = useCallback(async () => {
|
||||||
|
if (job && job.id && statuses && statuses.statuses) {
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
const response = await axios.post("/job/lifecycle", {
|
||||||
|
jobids: job.id,
|
||||||
|
statuses: statuses.statuses
|
||||||
|
});
|
||||||
|
const data = response.data.transition[job.id];
|
||||||
|
setLifecycleData(data);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`${t('job_lifecycle.errors.fetch')}: ${err.message}`);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [job, statuses, t]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!data) return;
|
||||||
|
setTimeout(() => {
|
||||||
|
getLifecycleData().catch(err => console.error(`${t('job_lifecycle.errors.fetch')}: ${err.message}`));
|
||||||
|
}, 500);
|
||||||
|
}, [data, getLifecycleData, t]);
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: t('job_lifecycle.columns.value'),
|
||||||
|
dataIndex: 'value',
|
||||||
|
key: 'value',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('job_lifecycle.columns.start'),
|
||||||
|
dataIndex: 'start',
|
||||||
|
key: 'start',
|
||||||
|
render: (text) => DateTimeFormatterFunction(text),
|
||||||
|
sorter: (a, b) => moment(a.start).unix() - moment(b.start).unix(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('job_lifecycle.columns.relative_start'),
|
||||||
|
dataIndex: 'start_readable',
|
||||||
|
key: 'start_readable',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('job_lifecycle.columns.end'),
|
||||||
|
dataIndex: 'end',
|
||||||
|
key: 'end',
|
||||||
|
sorter: (a, b) => {
|
||||||
|
if (isEmpty(a.end) || isEmpty(b.end)) {
|
||||||
|
if (isEmpty(a.end) && isEmpty(b.end)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return isEmpty(a.end) ? 1 : -1;
|
||||||
|
}
|
||||||
|
return moment(a.end).unix() - moment(b.end).unix();
|
||||||
|
},
|
||||||
|
render: (text) => isEmpty(text) ? t('job_lifecycle.content.not_available') : DateTimeFormatterFunction(text)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('job_lifecycle.columns.relative_end'),
|
||||||
|
dataIndex: 'end_readable',
|
||||||
|
key: 'end_readable',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('job_lifecycle.columns.duration'),
|
||||||
|
dataIndex: 'duration_readable',
|
||||||
|
key: 'duration_readable',
|
||||||
|
sorter: (a, b) => a.duration - b.duration,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card loading={loading} title={t('job_lifecycle.content.title')}>
|
||||||
|
{!loading ? (
|
||||||
|
lifecycleData && lifecycleData.lifecycle && lifecycleData.durations ? (
|
||||||
|
<Space direction='vertical' style={{width: '100%'}}>
|
||||||
|
<Card
|
||||||
|
type='inner'
|
||||||
|
title={(
|
||||||
|
<Space direction='horizontal' size='small'>
|
||||||
|
<Badge status='processing' count={lifecycleData.durations.totalStatuses}/>
|
||||||
|
{t('job_lifecycle.content.title_durations')}
|
||||||
|
</Space>
|
||||||
|
|
||||||
|
)}
|
||||||
|
style={{width: '100%'}}
|
||||||
|
>
|
||||||
|
<div id="bar-container" style={{
|
||||||
|
display: 'flex',
|
||||||
|
width: '100%',
|
||||||
|
height: '100px',
|
||||||
|
textAlign: 'center',
|
||||||
|
borderRadius: '5px',
|
||||||
|
borderWidth: '5px',
|
||||||
|
borderStyle: 'solid',
|
||||||
|
borderColor: '#f0f2f5',
|
||||||
|
margin: 0,
|
||||||
|
padding: 0
|
||||||
|
}}>
|
||||||
|
{lifecycleData.durations.summations.map((key, index, array) => {
|
||||||
|
const isFirst = index === 0;
|
||||||
|
const isLast = index === array.length - 1;
|
||||||
|
return (
|
||||||
|
<div key={key.status} style={{
|
||||||
|
overflow: 'hidden',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
margin: 0,
|
||||||
|
padding: 0,
|
||||||
|
|
||||||
|
borderTop: '1px solid #f0f2f5',
|
||||||
|
borderBottom: '1px solid #f0f2f5',
|
||||||
|
borderLeft: isFirst ? '1px solid #f0f2f5' : undefined,
|
||||||
|
borderRight: isLast ? '1px solid #f0f2f5' : undefined,
|
||||||
|
|
||||||
|
backgroundColor: key.color,
|
||||||
|
width: `${key.percentage}%`
|
||||||
|
}}
|
||||||
|
aria-label={`${key.status} | ${key.roundedPercentage} | ${key.humanReadable}`}
|
||||||
|
title={`${key.status} | ${key.roundedPercentage} | ${key.humanReadable}`}
|
||||||
|
>
|
||||||
|
|
||||||
|
{key.percentage > 15 ?
|
||||||
|
<>
|
||||||
|
<div>{key.roundedPercentage}</div>
|
||||||
|
<div style={{
|
||||||
|
backgroundColor: '#f0f2f5',
|
||||||
|
borderRadius: '5px',
|
||||||
|
paddingRight: '2px',
|
||||||
|
paddingLeft: '2px',
|
||||||
|
fontSize: '0.8rem',
|
||||||
|
}}>
|
||||||
|
{key.status}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
: null}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
<Card type='inner' title={t('job_lifecycle.content.legend_title')}
|
||||||
|
style={{marginTop: '10px'}}>
|
||||||
|
<div>
|
||||||
|
{lifecycleData.durations.summations.map((key) => (
|
||||||
|
<Tag color={key.color} style={{width: '13vh', padding: '4px', margin: '4px'}}>
|
||||||
|
<div
|
||||||
|
aria-label={`${key.status} | ${key.roundedPercentage} | ${key.humanReadable}`}
|
||||||
|
title={`${key.status} | ${key.roundedPercentage} | ${key.humanReadable}`}
|
||||||
|
style={{
|
||||||
|
backgroundColor: '#f0f2f5',
|
||||||
|
color: '#000',
|
||||||
|
padding: '4px',
|
||||||
|
textAlign: 'center'
|
||||||
|
}}>
|
||||||
|
{key.status} ({key.roundedPercentage})
|
||||||
|
</div>
|
||||||
|
</Tag>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
{(lifecycleData?.durations?.humanReadableTotal) ||
|
||||||
|
(lifecycleData.lifecycle[0] && lifecycleData.lifecycle[0].value && lifecycleData?.durations?.totalCurrentStatusDuration?.humanReadable) ?
|
||||||
|
<Card style={{marginTop: '10px'}}>
|
||||||
|
<ul>
|
||||||
|
{lifecycleData.durations && lifecycleData.durations.humanReadableTotal &&
|
||||||
|
<li>
|
||||||
|
<span
|
||||||
|
style={{fontWeight: 'bold'}}>{t('job_lifecycle.content.previous_status_accumulated_time')}:</span> {lifecycleData.durations.humanReadableTotal}
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
{lifecycleData.lifecycle[0] && lifecycleData.lifecycle[0].value && lifecycleData?.durations?.totalCurrentStatusDuration?.humanReadable &&
|
||||||
|
<li>
|
||||||
|
<span
|
||||||
|
style={{fontWeight: 'bold'}}>{t('job_lifecycle.content.current_status_accumulated_time')} ({lifecycleData.lifecycle[0].value}):</span> {lifecycleData.durations.totalCurrentStatusDuration.humanReadable}
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
</Card>
|
||||||
|
: null}
|
||||||
|
</Card>
|
||||||
|
<Card type='inner' title={(
|
||||||
|
<>
|
||||||
|
<Space direction="horizontal" size="small">
|
||||||
|
<Badge status='processing' count={lifecycleData.lifecycle.length}/>
|
||||||
|
{t('job_lifecycle.content.title_transitions')}
|
||||||
|
</Space>
|
||||||
|
</>
|
||||||
|
)}>
|
||||||
|
<Table style={{
|
||||||
|
overflow: 'auto',
|
||||||
|
width: '100%',
|
||||||
|
}} columns={columns} dataSource={lifecycleData.lifecycle}/>
|
||||||
|
</Card>
|
||||||
|
</Space>
|
||||||
|
) : (
|
||||||
|
<Card type='inner' style={{textAlign: 'center'}}>
|
||||||
|
{t('job_lifecycle.content.data_unavailable')}
|
||||||
|
</Card>
|
||||||
|
)
|
||||||
|
) : (
|
||||||
|
<Card type='inner' title={t('job_lifecycle.content.title_loading')}>
|
||||||
|
{t('job_lifecycle.content.loading')}
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default JobLifecycleComponent;
|
||||||
@@ -1,16 +1,16 @@
|
|||||||
import { useMutation, useLazyQuery } from "@apollo/client";
|
|
||||||
import { CheckCircleOutlined } from "@ant-design/icons";
|
import { CheckCircleOutlined } from "@ant-design/icons";
|
||||||
|
import { useLazyQuery, useMutation } from "@apollo/client";
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
Card,
|
Card,
|
||||||
Form,
|
Form,
|
||||||
InputNumber,
|
InputNumber,
|
||||||
notification,
|
|
||||||
Popover,
|
Popover,
|
||||||
Space,
|
Space,
|
||||||
|
notification,
|
||||||
} from "antd";
|
} from "antd";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import React, { useState, useEffect } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||||
import {
|
import {
|
||||||
@@ -50,6 +50,7 @@ export default function ScoreboardAddButton({
|
|||||||
|
|
||||||
const handleFinish = async (values) => {
|
const handleFinish = async (values) => {
|
||||||
logImEXEvent("job_close_add_to_scoreboard");
|
logImEXEvent("job_close_add_to_scoreboard");
|
||||||
|
values.date = moment(values.date).format("YYYY-MM-DD");
|
||||||
|
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
let result;
|
let result;
|
||||||
@@ -177,7 +178,7 @@ export default function ScoreboardAddButton({
|
|||||||
return acc + job.lbr_adjustments[val];
|
return acc + job.lbr_adjustments[val];
|
||||||
}, 0);
|
}, 0);
|
||||||
form.setFieldsValue({
|
form.setFieldsValue({
|
||||||
date: new moment(),
|
date: moment(),
|
||||||
bodyhrs: Math.round(v.bodyhrs * 10) / 10,
|
bodyhrs: Math.round(v.bodyhrs * 10) / 10,
|
||||||
painthrs: Math.round(v.painthrs * 10) / 10,
|
painthrs: Math.round(v.painthrs * 10) / 10,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -53,12 +53,14 @@ export function JobsAdminStatus({ insertAuditTrail, bodyshop, job }) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dropdown overlay={statusmenu} trigger={["click"]} key="changestatus">
|
<>
|
||||||
<Button shape="round">
|
<Dropdown overlay={statusmenu} trigger={["click"]} key="changestatus">
|
||||||
<span>{job.status}</span>
|
<Button shape="round">
|
||||||
|
<span>{job.status}</span>
|
||||||
|
|
||||||
<DownCircleFilled />
|
<DownCircleFilled />
|
||||||
</Button>
|
</Button>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,34 +1,18 @@
|
|||||||
import { useMutation } from "@apollo/client";
|
import { useMutation } from "@apollo/client";
|
||||||
import { Button, notification } from "antd";
|
import { Button, Space, notification } from "antd";
|
||||||
import { gql } from "@apollo/client";
|
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import {
|
||||||
|
DELETE_DELIVERY_CHECKLIST,
|
||||||
|
DELETE_INTAKE_CHECKLIST,
|
||||||
|
} from "../../graphql/jobs.queries";
|
||||||
|
|
||||||
export default function JobAdminDeleteIntake({ job }) {
|
export default function JobAdminDeleteIntake({ job }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [deleteIntake] = useMutation(gql`
|
|
||||||
mutation DELETE_INTAKE($jobId: uuid!) {
|
|
||||||
update_jobs_by_pk(
|
|
||||||
pk_columns: { id: $jobId }
|
|
||||||
_set: { intakechecklist: null }
|
|
||||||
) {
|
|
||||||
id
|
|
||||||
intakechecklist
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
|
|
||||||
const [DELETE_DELIVERY] = useMutation(gql`
|
const [deleteIntake] = useMutation(DELETE_INTAKE_CHECKLIST);
|
||||||
mutation DELETE_DELIVERY($jobId: uuid!) {
|
const [deleteDelivery] = useMutation(DELETE_DELIVERY_CHECKLIST);
|
||||||
update_jobs_by_pk(
|
|
||||||
pk_columns: { id: $jobId }
|
|
||||||
_set: { deliverchecklist: null }
|
|
||||||
) {
|
|
||||||
id
|
|
||||||
deliverchecklist
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
|
|
||||||
const handleDelete = async (values) => {
|
const handleDelete = async (values) => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
@@ -50,7 +34,7 @@ export default function JobAdminDeleteIntake({ job }) {
|
|||||||
|
|
||||||
const handleDeleteDelivery = async (values) => {
|
const handleDeleteDelivery = async (values) => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
const result = await DELETE_DELIVERY({
|
const result = await deleteDelivery({
|
||||||
variables: { jobId: job.id },
|
variables: { jobId: job.id },
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -68,12 +52,22 @@ export default function JobAdminDeleteIntake({ job }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Button loading={loading} onClick={handleDelete}>
|
<Space wrap>
|
||||||
{t("jobs.labels.deleteintake")}
|
<Button
|
||||||
</Button>
|
loading={loading}
|
||||||
<Button loading={loading} onClick={handleDeleteDelivery}>
|
onClick={handleDelete}
|
||||||
{t("jobs.labels.deletedelivery")}
|
disabled={!job.intakechecklist}
|
||||||
</Button>
|
>
|
||||||
|
{t("jobs.labels.deleteintake")}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
loading={loading}
|
||||||
|
onClick={handleDeleteDelivery}
|
||||||
|
disabled={!job.deliverychecklist}
|
||||||
|
>
|
||||||
|
{t("jobs.labels.deletedelivery")}
|
||||||
|
</Button>
|
||||||
|
</Space>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { gql, useMutation } from "@apollo/client";
|
import { useMutation } from "@apollo/client";
|
||||||
import { Button, notification } from "antd";
|
import { Button, Space, notification } from "antd";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
@@ -7,6 +7,11 @@ import moment from "moment";
|
|||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries";
|
import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries";
|
||||||
|
import {
|
||||||
|
MARK_JOB_AS_EXPORTED,
|
||||||
|
MARK_JOB_AS_UNINVOICED,
|
||||||
|
MARK_JOB_FOR_REEXPORT,
|
||||||
|
} from "../../graphql/jobs.queries";
|
||||||
import { insertAuditTrail } from "../../redux/application/application.actions";
|
import { insertAuditTrail } from "../../redux/application/application.actions";
|
||||||
import {
|
import {
|
||||||
selectBodyshop,
|
selectBodyshop,
|
||||||
@@ -35,58 +40,18 @@ export function JobAdminMarkReexport({
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [insertExportLog] = useMutation(INSERT_EXPORT_LOG);
|
const [insertExportLog] = useMutation(INSERT_EXPORT_LOG);
|
||||||
const [markJobForReexport] = useMutation(gql`
|
|
||||||
mutation MARK_JOB_FOR_REEXPORT($jobId: uuid!) {
|
|
||||||
update_jobs_by_pk(
|
|
||||||
pk_columns: { id: $jobId }
|
|
||||||
_set: { date_exported: null
|
|
||||||
status: "${bodyshop.md_ro_statuses.default_invoiced}"
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
id
|
|
||||||
date_exported
|
|
||||||
status
|
|
||||||
date_invoiced
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
|
|
||||||
const [markJobExported] = useMutation(gql`
|
const [markJobForReexport] = useMutation(MARK_JOB_FOR_REEXPORT);
|
||||||
mutation MARK_JOB_AS_EXPORTED($jobId: uuid!, $date_exported: timestamptz!) {
|
const [markJobExported] = useMutation(MARK_JOB_AS_EXPORTED);
|
||||||
update_jobs_by_pk(
|
const [markJobUninvoiced] = useMutation(MARK_JOB_AS_UNINVOICED);
|
||||||
pk_columns: { id: $jobId }
|
|
||||||
_set: { date_exported: $date_exported
|
|
||||||
status: "${bodyshop.md_ro_statuses.default_exported}"
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
id
|
|
||||||
date_exported
|
|
||||||
date_invoiced
|
|
||||||
status
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
const [markJobUninvoiced] = useMutation(gql`
|
|
||||||
mutation MARK_JOB_AS_UNINVOICED($jobId: uuid!, ) {
|
|
||||||
update_jobs_by_pk(
|
|
||||||
pk_columns: { id: $jobId }
|
|
||||||
_set: { date_exported: null
|
|
||||||
date_invoiced: null
|
|
||||||
status: "${bodyshop.md_ro_statuses.default_delivered}"
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
id
|
|
||||||
date_exported
|
|
||||||
date_invoiced
|
|
||||||
status
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
|
|
||||||
const handleMarkForExport = async () => {
|
const handleMarkForExport = async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
const result = await markJobForReexport({
|
const result = await markJobForReexport({
|
||||||
variables: { jobId: job.id },
|
variables: {
|
||||||
|
jobId: job.id,
|
||||||
|
default_invoiced: bodyshop.md_ro_statuses.default_invoiced,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!result.errors) {
|
if (!result.errors) {
|
||||||
@@ -108,7 +73,11 @@ export function JobAdminMarkReexport({
|
|||||||
const handleMarkExported = async () => {
|
const handleMarkExported = async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
const result = await markJobExported({
|
const result = await markJobExported({
|
||||||
variables: { jobId: job.id, date_exported: moment() },
|
variables: {
|
||||||
|
jobId: job.id,
|
||||||
|
date_exported: moment(),
|
||||||
|
default_exported: bodyshop.md_ro_statuses.default_exported,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
await insertExportLog({
|
await insertExportLog({
|
||||||
@@ -144,7 +113,10 @@ export function JobAdminMarkReexport({
|
|||||||
const handleUninvoice = async () => {
|
const handleUninvoice = async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
const result = await markJobUninvoiced({
|
const result = await markJobUninvoiced({
|
||||||
variables: { jobId: job.id },
|
variables: {
|
||||||
|
jobId: job.id,
|
||||||
|
default_delivered: bodyshop.md_ro_statuses.default_delivered,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!result.errors) {
|
if (!result.errors) {
|
||||||
@@ -165,27 +137,29 @@ export function JobAdminMarkReexport({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Button
|
<Space wrap>
|
||||||
loading={loading}
|
<Button
|
||||||
disabled={!job.date_exported}
|
loading={loading}
|
||||||
onClick={handleMarkForExport}
|
disabled={!job.date_exported}
|
||||||
>
|
onClick={handleMarkForExport}
|
||||||
{t("jobs.labels.markforreexport")}
|
>
|
||||||
</Button>
|
{t("jobs.labels.markforreexport")}
|
||||||
<Button
|
</Button>
|
||||||
loading={loading}
|
<Button
|
||||||
disabled={job.date_exported}
|
loading={loading}
|
||||||
onClick={handleMarkExported}
|
disabled={job.date_exported}
|
||||||
>
|
onClick={handleMarkExported}
|
||||||
{t("jobs.actions.markasexported")}
|
>
|
||||||
</Button>
|
{t("jobs.actions.markasexported")}
|
||||||
<Button
|
</Button>
|
||||||
loading={loading}
|
<Button
|
||||||
disabled={!job.date_invoiced || job.date_exported}
|
loading={loading}
|
||||||
onClick={handleUninvoice}
|
disabled={!job.date_invoiced || job.date_exported}
|
||||||
>
|
onClick={handleUninvoice}
|
||||||
{t("jobs.actions.uninvoice")}
|
>
|
||||||
</Button>
|
{t("jobs.actions.uninvoice")}
|
||||||
|
</Button>
|
||||||
|
</Space>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,65 @@
|
|||||||
|
import { useMutation } from "@apollo/client";
|
||||||
|
import { Switch, notification } from "antd";
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import { UPDATE_REMOVE_FROM_AR } from "../../graphql/jobs.queries";
|
||||||
|
import { insertAuditTrail } from "../../redux/application/application.actions";
|
||||||
|
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
||||||
|
|
||||||
|
const mapStateToProps = createStructuredSelector({});
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
insertAuditTrail: ({ jobid, operation }) =>
|
||||||
|
dispatch(insertAuditTrail({ jobid, operation })),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(JobsAdminRemoveAR);
|
||||||
|
|
||||||
|
export function JobsAdminRemoveAR({ insertAuditTrail, job }) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [switchValue, setSwitchValue] = useState(job.remove_from_ar);
|
||||||
|
|
||||||
|
const [mutationUpdateRemoveFromAR] = useMutation(UPDATE_REMOVE_FROM_AR);
|
||||||
|
|
||||||
|
const handleChange = async (value) => {
|
||||||
|
setLoading(true);
|
||||||
|
const result = await mutationUpdateRemoveFromAR({
|
||||||
|
variables: { jobId: job.id, remove_from_ar: value },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!result.errors) {
|
||||||
|
notification["success"]({ message: t("jobs.successes.save") });
|
||||||
|
insertAuditTrail({
|
||||||
|
jobid: job.id,
|
||||||
|
operation: AuditTrailMapping.admin_job_remove_from_ar(value),
|
||||||
|
});
|
||||||
|
setSwitchValue(value);
|
||||||
|
} else {
|
||||||
|
notification["error"]({
|
||||||
|
message: t("jobs.errors.saving", {
|
||||||
|
error: JSON.stringify(result.errors),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
setLoading(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div style={{ display: "flex", alignItems: "center" }}>
|
||||||
|
<div style={{ marginRight: "10px" }}>
|
||||||
|
{t("jobs.labels.remove_from_ar")}:
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Switch
|
||||||
|
checked={switchValue}
|
||||||
|
loading={loading}
|
||||||
|
onChange={handleChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
import { gql, useMutation } from "@apollo/client";
|
import { useMutation } from "@apollo/client";
|
||||||
import { Button, notification } from "antd";
|
import { Button, notification } from "antd";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import { UNVOID_JOB } from "../../graphql/jobs.queries";
|
||||||
import { insertAuditTrail } from "../../redux/application/application.actions";
|
import { insertAuditTrail } from "../../redux/application/application.actions";
|
||||||
import {
|
import {
|
||||||
selectBodyshop,
|
selectBodyshop,
|
||||||
@@ -29,66 +30,17 @@ export function JobsAdminUnvoid({
|
|||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [updateJob] = useMutation(gql`
|
const [mutationUnvoidJob] = useMutation(UNVOID_JOB);
|
||||||
mutation UNVOID_JOB($jobId: uuid!) {
|
|
||||||
update_jobs_by_pk(pk_columns: {id: $jobId}, _set: {voided: false, status: "${
|
|
||||||
bodyshop.md_ro_statuses.default_imported
|
|
||||||
}", date_void: null}) {
|
|
||||||
id
|
|
||||||
date_void
|
|
||||||
voided
|
|
||||||
status
|
|
||||||
}
|
|
||||||
insert_notes(objects: {jobid: $jobId, audit: true, created_by: "${
|
|
||||||
currentUser.email
|
|
||||||
}", text: "${t("jobs.labels.unvoidnote")}"}) {
|
|
||||||
returning {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
`);
|
|
||||||
|
|
||||||
// const result = await voidJob({
|
|
||||||
// variables: {
|
|
||||||
// jobId: job.id,
|
|
||||||
// job: {
|
|
||||||
// status: bodyshop.md_ro_statuses.default_void,
|
|
||||||
// voided: true,
|
|
||||||
// },
|
|
||||||
// note: [
|
|
||||||
// {
|
|
||||||
// jobid: job.id,
|
|
||||||
// created_by: currentUser.email,
|
|
||||||
// audit: true,
|
|
||||||
// text: t("jobs.labels.voidnote", {
|
|
||||||
// date: moment().format("MM/DD/yyy"),
|
|
||||||
// time: moment().format("hh:mm a"),
|
|
||||||
// }),
|
|
||||||
// },
|
|
||||||
// ],
|
|
||||||
// },
|
|
||||||
// });
|
|
||||||
|
|
||||||
// if (!!!result.errors) {
|
|
||||||
// notification["success"]({
|
|
||||||
// message: t("jobs.successes.voided"),
|
|
||||||
// });
|
|
||||||
// //go back to jobs list.
|
|
||||||
// history.push(`/manage/`);
|
|
||||||
// } else {
|
|
||||||
// notification["error"]({
|
|
||||||
// message: t("jobs.errors.voiding", {
|
|
||||||
// error: JSON.stringify(result.errors),
|
|
||||||
// }),
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
const handleUpdate = async (values) => {
|
const handleUpdate = async (values) => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
const result = await updateJob({
|
const result = await mutationUnvoidJob({
|
||||||
variables: { jobId: job.id },
|
variables: {
|
||||||
|
jobId: job.id,
|
||||||
|
default_imported: bodyshop.md_ro_statuses.default_imported,
|
||||||
|
currentUserEmail: currentUser.email,
|
||||||
|
text: t("jobs.labels.unvoidnote"),
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!result.errors) {
|
if (!result.errors) {
|
||||||
@@ -110,8 +62,10 @@ mutation UNVOID_JOB($jobId: uuid!) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Button loading={loading} disabled={!job.voided} onClick={handleUpdate}>
|
<>
|
||||||
{t("jobs.actions.unvoid")}
|
<Button loading={loading} disabled={!job.voided} onClick={handleUpdate}>
|
||||||
</Button>
|
{t("jobs.actions.unvoid")}
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,10 +6,10 @@ import {
|
|||||||
useQuery,
|
useQuery,
|
||||||
} from "@apollo/client";
|
} from "@apollo/client";
|
||||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||||
import { Col, notification, Row } from "antd";
|
import { Col, Row, notification } from "antd";
|
||||||
import Axios from "axios";
|
import Axios from "axios";
|
||||||
import Dinero from "dinero.js";
|
import Dinero from "dinero.js";
|
||||||
import moment from "moment";
|
import moment from "moment-business-days";
|
||||||
import queryString from "query-string";
|
import queryString from "query-string";
|
||||||
import React, { useCallback, useEffect, useState } from "react";
|
import React, { useCallback, useEffect, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
@@ -30,8 +30,8 @@ import {
|
|||||||
selectBodyshop,
|
selectBodyshop,
|
||||||
selectCurrentUser,
|
selectCurrentUser,
|
||||||
} from "../../redux/user/user.selectors";
|
} from "../../redux/user/user.selectors";
|
||||||
import confirmDialog from "../../utils/asyncConfirm";
|
|
||||||
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
||||||
|
import confirmDialog from "../../utils/asyncConfirm";
|
||||||
import CriticalPartsScan from "../../utils/criticalPartsScan";
|
import CriticalPartsScan from "../../utils/criticalPartsScan";
|
||||||
import AlertComponent from "../alert/alert.component";
|
import AlertComponent from "../alert/alert.component";
|
||||||
import JobsAvailableScan from "../jobs-available-scan/jobs-available-scan.component";
|
import JobsAvailableScan from "../jobs-available-scan/jobs-available-scan.component";
|
||||||
@@ -73,7 +73,15 @@ export function JobsAvailableContainer({
|
|||||||
|
|
||||||
const [selectedJob, setSelectedJob] = useState(null);
|
const [selectedJob, setSelectedJob] = useState(null);
|
||||||
const [selectedOwner, setSelectedOwner] = useState(null);
|
const [selectedOwner, setSelectedOwner] = useState(null);
|
||||||
const [partsQueueToggle, setPartsQueueToggle] = useState(bodyshop.md_functionality_toggles.parts_queue_toggle);
|
const [partsQueueToggle, setPartsQueueToggle] = useState(
|
||||||
|
bodyshop.md_functionality_toggles.parts_queue_toggle
|
||||||
|
);
|
||||||
|
const [updateSchComp, setSchComp] = useState({
|
||||||
|
actual_in: moment(),
|
||||||
|
checked: false,
|
||||||
|
scheduled_completion: moment(),
|
||||||
|
automatic: false,
|
||||||
|
});
|
||||||
|
|
||||||
const [insertLoading, setInsertLoading] = useState(false);
|
const [insertLoading, setInsertLoading] = useState(false);
|
||||||
|
|
||||||
@@ -197,11 +205,16 @@ export function JobsAvailableContainer({
|
|||||||
notification["error"]({
|
notification["error"]({
|
||||||
message: t("jobs.errors.creating", { error: err.message }),
|
message: t("jobs.errors.creating", { error: err.message }),
|
||||||
});
|
});
|
||||||
refetch().catch(e => {console.error(`Something went wrong in jobs available table container - ${err.message || ''}`)});
|
refetch().catch((e) => {
|
||||||
|
console.error(
|
||||||
|
`Something went wrong in jobs available table container - ${
|
||||||
|
err.message || ""
|
||||||
|
}`
|
||||||
|
);
|
||||||
|
});
|
||||||
setInsertLoading(false);
|
setInsertLoading(false);
|
||||||
setPartsQueueToggle(bodyshop.md_functionality_toggles.parts_queue_toggle);
|
setPartsQueueToggle(bodyshop.md_functionality_toggles.parts_queue_toggle);
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//Supplement scenario
|
//Supplement scenario
|
||||||
@@ -225,6 +238,22 @@ export function JobsAvailableContainer({
|
|||||||
//IO-539 Check for Parts Rate on PAL for SGI use case.
|
//IO-539 Check for Parts Rate on PAL for SGI use case.
|
||||||
await CheckTaxRates(supp, bodyshop);
|
await CheckTaxRates(supp, bodyshop);
|
||||||
|
|
||||||
|
if (updateSchComp.checked === true) {
|
||||||
|
if (updateSchComp.automatic === true) {
|
||||||
|
const job_hrs = supp.joblines.data.reduce(
|
||||||
|
(acc, val) => acc + val.mod_lb_hrs,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
const num_days = job_hrs / bodyshop.target_touchtime;
|
||||||
|
supp.actual_in = updateSchComp.actual_in;
|
||||||
|
supp.scheduled_completion = moment(
|
||||||
|
updateSchComp.actual_in
|
||||||
|
).businessAdd(num_days, "days");
|
||||||
|
} else {
|
||||||
|
supp.scheduled_completion = updateSchComp.scheduled_completion;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
delete supp.owner;
|
delete supp.owner;
|
||||||
delete supp.vehicle;
|
delete supp.vehicle;
|
||||||
delete supp.ins_co_nm;
|
delete supp.ins_co_nm;
|
||||||
@@ -261,9 +290,9 @@ export function JobsAvailableContainer({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
setPartsQueueToggle(bodyshop.md_functionality_toggles.parts_queue_toggle);
|
setPartsQueueToggle(bodyshop.md_functionality_toggles.parts_queue_toggle);
|
||||||
|
|
||||||
if (CriticalPartsScanning.treatment === "on") {
|
if (CriticalPartsScanning.treatment === "on") {
|
||||||
CriticalPartsScan(updateResult.data.update_jobs.returning[0].id);
|
CriticalPartsScan(updateResult.data.update_jobs.returning[0].id);
|
||||||
}
|
}
|
||||||
if (updateResult.errors) {
|
if (updateResult.errors) {
|
||||||
@@ -367,7 +396,6 @@ export function JobsAvailableContainer({
|
|||||||
|
|
||||||
if (error) return <AlertComponent type="error" message={error.message} />;
|
if (error) return <AlertComponent type="error" message={error.message} />;
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LoadingSpinner
|
<LoadingSpinner
|
||||||
loading={insertLoading}
|
loading={insertLoading}
|
||||||
@@ -384,7 +412,6 @@ export function JobsAvailableContainer({
|
|||||||
visible={ownerModalVisible}
|
visible={ownerModalVisible}
|
||||||
onOk={onOwnerFindModalOk}
|
onOk={onOwnerFindModalOk}
|
||||||
onCancel={onOwnerModalCancel}
|
onCancel={onOwnerModalCancel}
|
||||||
|
|
||||||
/>
|
/>
|
||||||
<JobsFindModalContainer
|
<JobsFindModalContainer
|
||||||
loading={estDataRaw.loading}
|
loading={estDataRaw.loading}
|
||||||
@@ -398,6 +425,8 @@ export function JobsAvailableContainer({
|
|||||||
modalSearchState={modalSearchState}
|
modalSearchState={modalSearchState}
|
||||||
partsQueueToggle={partsQueueToggle}
|
partsQueueToggle={partsQueueToggle}
|
||||||
setPartsQueueToggle={setPartsQueueToggle}
|
setPartsQueueToggle={setPartsQueueToggle}
|
||||||
|
updateSchComp={updateSchComp}
|
||||||
|
setSchComp={setSchComp}
|
||||||
/>
|
/>
|
||||||
<Row gutter={[16, 16]}>
|
<Row gutter={[16, 16]}>
|
||||||
<Col span={24}>
|
<Col span={24}>
|
||||||
|
|||||||
@@ -19,10 +19,13 @@ export default function JobsCreateOwnerInfoNewComponent() {
|
|||||||
label={t("owners.fields.ownr_ln")}
|
label={t("owners.fields.ownr_ln")}
|
||||||
name={["owner", "data", "ownr_ln"]}
|
name={["owner", "data", "ownr_ln"]}
|
||||||
rules={[
|
rules={[
|
||||||
{
|
({ getFieldValue }) => ({
|
||||||
required: state.owner.new,
|
required:
|
||||||
|
state.owner.new &&
|
||||||
|
(!getFieldValue(["owner", "data", "ownr_co_nm"]) ||
|
||||||
|
getFieldValue(["owner", "data", "ownr_co_nm"]) === ""),
|
||||||
//message: t("general.validation.required"),
|
//message: t("general.validation.required"),
|
||||||
},
|
}),
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<Input disabled={!state.owner.new} />
|
<Input disabled={!state.owner.new} />
|
||||||
@@ -31,10 +34,13 @@ export default function JobsCreateOwnerInfoNewComponent() {
|
|||||||
label={t("owners.fields.ownr_fn")}
|
label={t("owners.fields.ownr_fn")}
|
||||||
name={["owner", "data", "ownr_fn"]}
|
name={["owner", "data", "ownr_fn"]}
|
||||||
rules={[
|
rules={[
|
||||||
{
|
({ getFieldValue }) => ({
|
||||||
required: state.owner.new,
|
required:
|
||||||
|
state.owner.new &&
|
||||||
|
(!getFieldValue(["owner", "data", "ownr_co_nm"]) ||
|
||||||
|
getFieldValue(["owner", "data", "ownr_co_nm"]) === ""),
|
||||||
//message: t("general.validation.required"),
|
//message: t("general.validation.required"),
|
||||||
},
|
}),
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<Input disabled={!state.owner.new} />
|
<Input disabled={!state.owner.new} />
|
||||||
@@ -51,6 +57,17 @@ export default function JobsCreateOwnerInfoNewComponent() {
|
|||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("owners.fields.ownr_co_nm")}
|
label={t("owners.fields.ownr_co_nm")}
|
||||||
name={["owner", "data", "ownr_co_nm"]}
|
name={["owner", "data", "ownr_co_nm"]}
|
||||||
|
rules={[
|
||||||
|
({ getFieldValue }) => ({
|
||||||
|
required:
|
||||||
|
state.owner.new &&
|
||||||
|
(!getFieldValue(["owner", "data", "ownr_ln"]) ||
|
||||||
|
!getFieldValue(["owner", "data", "ownr_fn"]) ||
|
||||||
|
getFieldValue(["owner", "data", "ownr_ln"]) === "" ||
|
||||||
|
getFieldValue(["owner", "data", "ownr_fn"]) === ""),
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
}),
|
||||||
|
]}
|
||||||
>
|
>
|
||||||
<Input disabled={!state.owner.new} />
|
<Input disabled={!state.owner.new} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import {
|
import {
|
||||||
|
BranchesOutlined,
|
||||||
ExclamationCircleFilled,
|
ExclamationCircleFilled,
|
||||||
PauseCircleOutlined,
|
PauseCircleOutlined,
|
||||||
WarningFilled,
|
WarningFilled,
|
||||||
BranchesOutlined,
|
|
||||||
} from "@ant-design/icons";
|
} from "@ant-design/icons";
|
||||||
import { Card, Col, Row, Space, Tag, Tooltip } from "antd";
|
import { Card, Col, Row, Space, Tag, Tooltip } from "antd";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
@@ -15,6 +15,7 @@ import { setModalContext } from "../../redux/modals/modals.actions";
|
|||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||||
import { DateTimeFormatter } from "../../utils/DateFormatter";
|
import { DateTimeFormatter } from "../../utils/DateFormatter";
|
||||||
|
import PhoneNumberFormatter from "../../utils/PhoneFormatter";
|
||||||
import ChatOpenButton from "../chat-open-button/chat-open-button.component";
|
import ChatOpenButton from "../chat-open-button/chat-open-button.component";
|
||||||
import DataLabel from "../data-label/data-label.component";
|
import DataLabel from "../data-label/data-label.component";
|
||||||
import JobAltTransportChange from "../job-at-change/job-at-change.component";
|
import JobAltTransportChange from "../job-at-change/job-at-change.component";
|
||||||
@@ -122,20 +123,23 @@ export function JobsDetailHeader({ job, bodyshop, disabled }) {
|
|||||||
</DataLabel>
|
</DataLabel>
|
||||||
{job.cccontracts.length > 0 && (
|
{job.cccontracts.length > 0 && (
|
||||||
<DataLabel label={t("jobs.labels.contracts")}>
|
<DataLabel label={t("jobs.labels.contracts")}>
|
||||||
{job.cccontracts.map((c) => (
|
{job.cccontracts.map((c, index) => (
|
||||||
<Link
|
<Space wrap>
|
||||||
key={c.id}
|
<Link
|
||||||
to={`/manage/courtesycars/contracts/${c.id}`}
|
key={c.id}
|
||||||
>{`${c.agreementnumber} - ${c.courtesycar.fleetnumber} ${c.courtesycar.year} ${c.courtesycar.make} ${c.courtesycar.model}`}</Link>
|
to={`/manage/courtesycars/contracts/${c.id}`}
|
||||||
|
>
|
||||||
|
{`${c.agreementnumber} - ${c.courtesycar.fleetnumber} ${c.courtesycar.year} ${c.courtesycar.make} ${c.courtesycar.model}`}
|
||||||
|
{index !== job.cccontracts.length - 1 ? "," : null}
|
||||||
|
</Link>
|
||||||
|
</Space>
|
||||||
))}
|
))}
|
||||||
</DataLabel>
|
</DataLabel>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<DataLabel label={t("jobs.fields.production_vars.note")}>
|
<DataLabel label={t("jobs.fields.production_vars.note")}>
|
||||||
<ProductionListColumnProductionNote record={job} />
|
<ProductionListColumnProductionNote record={job} />
|
||||||
</DataLabel>
|
</DataLabel>
|
||||||
|
<Space wrap>
|
||||||
<Space>
|
|
||||||
{job.special_coverage_policy && (
|
{job.special_coverage_policy && (
|
||||||
<Tag color="tomato">
|
<Tag color="tomato">
|
||||||
<Space>
|
<Space>
|
||||||
@@ -160,19 +164,35 @@ export function JobsDetailHeader({ job, bodyshop, disabled }) {
|
|||||||
<Card
|
<Card
|
||||||
style={{ height: "100%" }}
|
style={{ height: "100%" }}
|
||||||
title={
|
title={
|
||||||
<Link to={disabled ? "#" : `/manage/owners/${job.owner.id}`}>
|
disabled ? (
|
||||||
{ownerTitle.length > 0
|
<>
|
||||||
? ownerTitle
|
{ownerTitle.length > 0
|
||||||
: t("owner.labels.noownerinfo")}
|
? ownerTitle
|
||||||
</Link>
|
: t("owner.labels.noownerinfo")}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<Link to={`/manage/owners/${job.owner.id}`}>
|
||||||
|
{ownerTitle.length > 0
|
||||||
|
? ownerTitle
|
||||||
|
: t("owner.labels.noownerinfo")}
|
||||||
|
</Link>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<DataLabel key="2" label={t("jobs.fields.ownr_ph1")}>
|
<DataLabel key="2" label={t("jobs.fields.ownr_ph1")}>
|
||||||
<ChatOpenButton phone={job.ownr_ph1} jobid={job.id} />
|
{disabled ? (
|
||||||
|
<PhoneNumberFormatter>{job.ownr_ph1}</PhoneNumberFormatter>
|
||||||
|
) : (
|
||||||
|
<ChatOpenButton phone={job.ownr_ph1} jobid={job.id} />
|
||||||
|
)}
|
||||||
</DataLabel>
|
</DataLabel>
|
||||||
<DataLabel key="22" label={t("jobs.fields.ownr_ph2")}>
|
<DataLabel key="22" label={t("jobs.fields.ownr_ph2")}>
|
||||||
<ChatOpenButton phone={job.ownr_ph2} jobid={job.id} />
|
{disabled ? (
|
||||||
|
<PhoneNumberFormatter>{job.ownr_ph2}</PhoneNumberFormatter>
|
||||||
|
) : (
|
||||||
|
<ChatOpenButton phone={job.ownr_ph2} jobid={job.id} />
|
||||||
|
)}
|
||||||
</DataLabel>
|
</DataLabel>
|
||||||
<DataLabel key="3" label={t("owners.fields.address")}>
|
<DataLabel key="3" label={t("owners.fields.address")}>
|
||||||
{`${job.ownr_addr1 || ""} ${job.ownr_addr2 || ""} ${
|
{`${job.ownr_addr1 || ""} ${job.ownr_addr2 || ""} ${
|
||||||
@@ -180,7 +200,11 @@ export function JobsDetailHeader({ job, bodyshop, disabled }) {
|
|||||||
} ${job.ownr_st || ""} ${job.ownr_zip || ""}`}
|
} ${job.ownr_st || ""} ${job.ownr_zip || ""}`}
|
||||||
</DataLabel>
|
</DataLabel>
|
||||||
<DataLabel key="4" label={t("owners.fields.ownr_ea")}>
|
<DataLabel key="4" label={t("owners.fields.ownr_ea")}>
|
||||||
{job.ownr_ea || ""}
|
{disabled ? (
|
||||||
|
<>{job.ownr_ea || ""}</>
|
||||||
|
) : job.ownr_ea ? (
|
||||||
|
<a href={`mailto:${job.ownr_ea}`}>{job.ownr_ea}</a>
|
||||||
|
) : null}
|
||||||
</DataLabel>
|
</DataLabel>
|
||||||
{job.owner?.tax_number && (
|
{job.owner?.tax_number && (
|
||||||
<DataLabel key="5" label={t("owners.fields.tax_number")}>
|
<DataLabel key="5" label={t("owners.fields.tax_number")}>
|
||||||
@@ -195,17 +219,19 @@ export function JobsDetailHeader({ job, bodyshop, disabled }) {
|
|||||||
style={{ height: "100%" }}
|
style={{ height: "100%" }}
|
||||||
title={
|
title={
|
||||||
job.vehicle ? (
|
job.vehicle ? (
|
||||||
<Link
|
disabled ? (
|
||||||
to={
|
<>
|
||||||
disabled
|
{vehicleTitle.length > 0
|
||||||
? "#"
|
? vehicleTitle
|
||||||
: job.vehicle && `/manage/vehicles/${job.vehicle.id}`
|
: t("vehicles.labels.novehinfo")}{" "}
|
||||||
}
|
</>
|
||||||
>
|
) : (
|
||||||
{vehicleTitle.length > 0
|
<Link to={job.vehicle && `/manage/vehicles/${job.vehicle.id}`}>
|
||||||
? vehicleTitle
|
{vehicleTitle.length > 0
|
||||||
: t("vehicles.labels.novehinfo")}
|
? vehicleTitle
|
||||||
</Link>
|
: t("vehicles.labels.novehinfo")}
|
||||||
|
</Link>
|
||||||
|
)
|
||||||
) : (
|
) : (
|
||||||
<span></span>
|
<span></span>
|
||||||
)
|
)
|
||||||
@@ -221,9 +247,19 @@ export function JobsDetailHeader({ job, bodyshop, disabled }) {
|
|||||||
<VehicleVinDisplay>
|
<VehicleVinDisplay>
|
||||||
{`${job.v_vin || t("general.labels.na")}`}
|
{`${job.v_vin || t("general.labels.na")}`}
|
||||||
</VehicleVinDisplay>
|
</VehicleVinDisplay>
|
||||||
|
{bodyshop.pbs_serialnumber || bodyshop.cdk_dealerid ? (
|
||||||
|
job.v_vin?.length !== 17 ? (
|
||||||
|
<WarningFilled
|
||||||
|
style={{ color: "tomato", marginLeft: ".3rem" }}
|
||||||
|
/>
|
||||||
|
) : null
|
||||||
|
) : null}
|
||||||
|
</DataLabel>
|
||||||
|
<DataLabel label={t("jobs.fields.regie_number")}>
|
||||||
|
{job.regie_number || t("general.labels.na")}
|
||||||
</DataLabel>
|
</DataLabel>
|
||||||
<DataLabel label={t("jobs.labels.relatedros")}>
|
<DataLabel label={t("jobs.labels.relatedros")}>
|
||||||
<JobsRelatedRos jobid={job.id} job={job} />
|
<JobsRelatedRos jobid={job.id} job={job} disabled={disabled} />
|
||||||
</DataLabel>
|
</DataLabel>
|
||||||
{job.vehicle && job.vehicle.notes && (
|
{job.vehicle && job.vehicle.notes && (
|
||||||
<DataLabel
|
<DataLabel
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
import { SyncOutlined } from "@ant-design/icons";
|
import { SyncOutlined } from "@ant-design/icons";
|
||||||
import { Checkbox, Divider, Input, Table, Button } from "antd";
|
import { Button, Checkbox, Divider, Input, Space, Table } from "antd";
|
||||||
import React from "react";
|
import moment from "moment";
|
||||||
|
import React, { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import PhoneFormatter from "../../utils/PhoneFormatter";
|
import PhoneFormatter from "../../utils/PhoneFormatter";
|
||||||
|
import FormDateTimePickerComponent from "../form-date-time-picker/form-date-time-picker.component";
|
||||||
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
||||||
|
|
||||||
export default function JobsFindModalComponent({
|
export default function JobsFindModalComponent({
|
||||||
@@ -16,11 +18,13 @@ export default function JobsFindModalComponent({
|
|||||||
jobsListRefetch,
|
jobsListRefetch,
|
||||||
partsQueueToggle,
|
partsQueueToggle,
|
||||||
setPartsQueueToggle,
|
setPartsQueueToggle,
|
||||||
|
updateSchComp,
|
||||||
|
setSchComp,
|
||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [modalSearch, setModalSearch] = modalSearchState;
|
const [modalSearch, setModalSearch] = modalSearchState;
|
||||||
const [importOptions, setImportOptions] = importOptionsState;
|
const [importOptions, setImportOptions] = importOptionsState;
|
||||||
|
const [checkUTT, setCheckUTT] = useState(false);
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
title: t("jobs.fields.ro_number"),
|
title: t("jobs.fields.ro_number"),
|
||||||
@@ -142,6 +146,35 @@ export default function JobsFindModalComponent({
|
|||||||
if (record) {
|
if (record) {
|
||||||
if (record.id) {
|
if (record.id) {
|
||||||
setSelectedJob(record.id);
|
setSelectedJob(record.id);
|
||||||
|
if (record.actual_in && record.scheduled_completion) {
|
||||||
|
setSchComp({
|
||||||
|
...updateSchComp,
|
||||||
|
actual_in: record.actual_in,
|
||||||
|
scheduled_completion: record.scheduled_completion,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (record.actual_in && !record.scheduled_completion) {
|
||||||
|
setSchComp({
|
||||||
|
...updateSchComp,
|
||||||
|
actual_in: record.actual_in,
|
||||||
|
scheduled_completion: moment(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (!record.actual_in && record.scheduled_completion) {
|
||||||
|
setSchComp({
|
||||||
|
...updateSchComp,
|
||||||
|
actual_in: moment(),
|
||||||
|
scheduled_completion: moment(record.scheduled_completion),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (!record.actual_in && !record.scheduled_completion) {
|
||||||
|
setSchComp({
|
||||||
|
...updateSchComp,
|
||||||
|
actual_in: moment(),
|
||||||
|
scheduled_completion: moment(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -177,6 +210,35 @@ export default function JobsFindModalComponent({
|
|||||||
rowSelection={{
|
rowSelection={{
|
||||||
onSelect: (props) => {
|
onSelect: (props) => {
|
||||||
setSelectedJob(props.id);
|
setSelectedJob(props.id);
|
||||||
|
if (props.actual_in && props.scheduled_completion) {
|
||||||
|
setSchComp({
|
||||||
|
...updateSchComp,
|
||||||
|
actual_in: props.actual_in,
|
||||||
|
scheduled_completion: props.scheduled_completion,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (props.actual_in && !props.scheduled_completion) {
|
||||||
|
setSchComp({
|
||||||
|
...updateSchComp,
|
||||||
|
actual_in: props.actual_in,
|
||||||
|
scheduled_completion: moment(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (!props.actual_in && props.scheduled_completion) {
|
||||||
|
setSchComp({
|
||||||
|
...updateSchComp,
|
||||||
|
actual_in: moment(),
|
||||||
|
scheduled_completion: moment(props.scheduled_completion),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (!props.actual_in && !props.scheduled_completion) {
|
||||||
|
setSchComp({
|
||||||
|
...updateSchComp,
|
||||||
|
actual_in: moment(),
|
||||||
|
scheduled_completion: moment(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
type: "radio",
|
type: "radio",
|
||||||
selectedRowKeys: [selectedJob],
|
selectedRowKeys: [selectedJob],
|
||||||
@@ -190,23 +252,58 @@ export default function JobsFindModalComponent({
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Divider />
|
<Divider />
|
||||||
<Checkbox
|
<Space>
|
||||||
defaultChecked={importOptions.overrideHeader}
|
<Checkbox
|
||||||
onChange={(e) =>
|
defaultChecked={importOptions.overrideHeader}
|
||||||
setImportOptions({
|
onChange={(e) =>
|
||||||
...importOptions,
|
setImportOptions({
|
||||||
overrideHeaders: e.target.checked,
|
...importOptions,
|
||||||
})
|
overrideHeaders: e.target.checked,
|
||||||
}
|
})
|
||||||
>
|
}
|
||||||
{t("jobs.labels.override_header")}
|
>
|
||||||
</Checkbox>
|
{t("jobs.labels.override_header")}
|
||||||
<Checkbox
|
</Checkbox>
|
||||||
|
<Checkbox
|
||||||
checked={partsQueueToggle}
|
checked={partsQueueToggle}
|
||||||
onChange={(e) => setPartsQueueToggle(e.target.checked)}
|
onChange={(e) => setPartsQueueToggle(e.target.checked)}
|
||||||
>
|
>
|
||||||
{t("bodyshop.fields.md_functionality_toggles.parts_queue_toggle")}
|
{t("bodyshop.fields.md_functionality_toggles.parts_queue_toggle")}
|
||||||
</Checkbox>
|
</Checkbox>
|
||||||
|
<Checkbox
|
||||||
|
checked={updateSchComp.checked}
|
||||||
|
onChange={(e) =>
|
||||||
|
setSchComp({ ...updateSchComp, checked: e.target.checked })
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{t("jobs.labels.update_scheduled_completion")}
|
||||||
|
</Checkbox>
|
||||||
|
{updateSchComp.checked === true ? (
|
||||||
|
<>
|
||||||
|
{checkUTT === false ? (
|
||||||
|
<FormDateTimePickerComponent
|
||||||
|
value={updateSchComp.scheduled_completion}
|
||||||
|
onChange={(e) => {
|
||||||
|
setSchComp({ ...updateSchComp, scheduled_completion: e });
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
<Checkbox
|
||||||
|
checked={checkUTT}
|
||||||
|
onChange={(e) => {
|
||||||
|
setCheckUTT(e.target.checked);
|
||||||
|
setSchComp({
|
||||||
|
...updateSchComp,
|
||||||
|
scheduled_completion: null,
|
||||||
|
automatic: true,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t("jobs.labels.calc_scheuled_completion")}
|
||||||
|
</Checkbox>
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
|
</Space>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ export default connect(
|
|||||||
modalSearchState,
|
modalSearchState,
|
||||||
partsQueueToggle,
|
partsQueueToggle,
|
||||||
setPartsQueueToggle,
|
setPartsQueueToggle,
|
||||||
|
updateSchComp,
|
||||||
|
setSchComp,
|
||||||
...modalProps
|
...modalProps
|
||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@@ -95,6 +97,8 @@ export default connect(
|
|||||||
modalSearchState={modalSearchState}
|
modalSearchState={modalSearchState}
|
||||||
partsQueueToggle={partsQueueToggle}
|
partsQueueToggle={partsQueueToggle}
|
||||||
setPartsQueueToggle={setPartsQueueToggle}
|
setPartsQueueToggle={setPartsQueueToggle}
|
||||||
|
updateSchComp={updateSchComp}
|
||||||
|
setSchComp={setSchComp}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|||||||
@@ -10,9 +10,10 @@ import { Link, useHistory, useLocation } from "react-router-dom";
|
|||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||||
|
import { pageLimit } from "../../utils/config";
|
||||||
|
import useLocalStorage from "../../utils/useLocalStorage";
|
||||||
import StartChatButton from "../chat-open-button/chat-open-button.component";
|
import StartChatButton from "../chat-open-button/chat-open-button.component";
|
||||||
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
||||||
import {pageLimit} from "../../utils/config";
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
//currentUser: selectCurrentUser
|
//currentUser: selectCurrentUser
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
@@ -25,6 +26,7 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
|
|||||||
const search = queryString.parse(useLocation().search);
|
const search = queryString.parse(useLocation().search);
|
||||||
const [openSearchResults, setOpenSearchResults] = useState([]);
|
const [openSearchResults, setOpenSearchResults] = useState([]);
|
||||||
const [searchLoading, setSearchLoading] = useState(false);
|
const [searchLoading, setSearchLoading] = useState(false);
|
||||||
|
const [filter, setFilter] = useLocalStorage("filter_jobs_all", null);
|
||||||
const { page, sortcolumn, sortorder } = search;
|
const { page, sortcolumn, sortorder } = search;
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
|
||||||
@@ -93,6 +95,7 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
|
|||||||
render: (text, record) => {
|
render: (text, record) => {
|
||||||
return record.status || t("general.labels.na");
|
return record.status || t("general.labels.na");
|
||||||
},
|
},
|
||||||
|
filteredValue: filter?.status || null,
|
||||||
filters: bodyshop.md_ro_statuses.statuses.map((s) => {
|
filters: bodyshop.md_ro_statuses.statuses.map((s) => {
|
||||||
return { text: s, value: [s] };
|
return { text: s, value: [s] };
|
||||||
}),
|
}),
|
||||||
@@ -189,6 +192,7 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
|
|||||||
} else {
|
} else {
|
||||||
delete search.statusFilters;
|
delete search.statusFilters;
|
||||||
}
|
}
|
||||||
|
setFilter(filters);
|
||||||
history.push({ search: queryString.stringify(search) });
|
history.push({ search: queryString.stringify(search) });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import {
|
import {
|
||||||
SyncOutlined,
|
BranchesOutlined,
|
||||||
ExclamationCircleFilled,
|
ExclamationCircleFilled,
|
||||||
PauseCircleOutlined,
|
PauseCircleOutlined,
|
||||||
BranchesOutlined,
|
SyncOutlined,
|
||||||
} from "@ant-design/icons";
|
} from "@ant-design/icons";
|
||||||
import { useQuery } from "@apollo/client";
|
import { useQuery } from "@apollo/client";
|
||||||
import { Button, Card, Grid, Input, Space, Table, Tooltip } from "antd";
|
import { Button, Card, Grid, Input, Space, Table, Tooltip } from "antd";
|
||||||
@@ -14,382 +14,389 @@ import { Link, useHistory, useLocation } from "react-router-dom";
|
|||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import { QUERY_ALL_ACTIVE_JOBS } from "../../graphql/jobs.queries";
|
import { QUERY_ALL_ACTIVE_JOBS } from "../../graphql/jobs.queries";
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
import { onlyUnique } from "../../utils/arrayHelper";
|
|
||||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||||
import { alphaSort } from "../../utils/sorters";
|
import { onlyUnique } from "../../utils/arrayHelper";
|
||||||
|
import { alphaSort, statusSort } from "../../utils/sorters";
|
||||||
|
import useLocalStorage from "../../utils/useLocalStorage";
|
||||||
import AlertComponent from "../alert/alert.component";
|
import AlertComponent from "../alert/alert.component";
|
||||||
import ChatOpenButton from "../chat-open-button/chat-open-button.component";
|
import ChatOpenButton from "../chat-open-button/chat-open-button.component";
|
||||||
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
});
|
});
|
||||||
|
|
||||||
export function JobsList({ bodyshop }) {
|
export function JobsList({ bodyshop }) {
|
||||||
const searchParams = queryString.parse(useLocation().search);
|
const searchParams = queryString.parse(useLocation().search);
|
||||||
const { selected } = searchParams;
|
const { selected } = searchParams;
|
||||||
const selectedBreakpoint = Object.entries(Grid.useBreakpoint())
|
const selectedBreakpoint = Object.entries(Grid.useBreakpoint())
|
||||||
.filter((screen) => !!screen[1])
|
.filter((screen) => !!screen[1])
|
||||||
.slice(-1)[0];
|
.slice(-1)[0];
|
||||||
const { loading, error, data, refetch } = useQuery(QUERY_ALL_ACTIVE_JOBS, {
|
const { loading, error, data, refetch } = useQuery(QUERY_ALL_ACTIVE_JOBS, {
|
||||||
variables: {
|
variables: {
|
||||||
statuses: bodyshop.md_ro_statuses.active_statuses || ["Open", "Open*"],
|
statuses: bodyshop.md_ro_statuses.active_statuses || ["Open", "Open*"],
|
||||||
},
|
},
|
||||||
fetchPolicy: "network-only",
|
fetchPolicy: "network-only",
|
||||||
nextFetchPolicy: "network-only",
|
nextFetchPolicy: "network-only",
|
||||||
});
|
});
|
||||||
|
|
||||||
const [state, setState] = useState({
|
const [state, setState] = useState({ sortedInfo: {} });
|
||||||
sortedInfo: {},
|
const [filter, setFilter] = useLocalStorage("filter_jobs_list", null);
|
||||||
filteredInfo: { text: "" },
|
|
||||||
});
|
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const [searchText, setSearchText] = useState("");
|
const [searchText, setSearchText] = useState("");
|
||||||
|
|
||||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||||
|
|
||||||
const jobs = data
|
const jobs = data
|
||||||
? searchText === ""
|
? searchText === ""
|
||||||
? data.jobs
|
? data.jobs
|
||||||
: data.jobs.filter(
|
: data.jobs.filter(
|
||||||
(j) =>
|
(j) =>
|
||||||
(j.ro_number || "")
|
(j.ro_number || "")
|
||||||
.toString()
|
.toString()
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.includes(searchText.toLowerCase()) ||
|
.includes(searchText.toLowerCase()) ||
|
||||||
(j.ownr_co_nm || "")
|
(j.ownr_co_nm || "")
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.includes(searchText.toLowerCase()) ||
|
.includes(searchText.toLowerCase()) ||
|
||||||
(j.comments || "")
|
(j.comments || "")
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.includes(searchText.toLowerCase()) ||
|
.includes(searchText.toLowerCase()) ||
|
||||||
(j.ownr_fn || "")
|
(j.ownr_fn || "")
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.includes(searchText.toLowerCase()) ||
|
.includes(searchText.toLowerCase()) ||
|
||||||
(j.ownr_ln || "")
|
(j.ownr_ln || "")
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.includes(searchText.toLowerCase()) ||
|
.includes(searchText.toLowerCase()) ||
|
||||||
(j.clm_no || "").toLowerCase().includes(searchText.toLowerCase()) ||
|
(j.clm_no || "").toLowerCase().includes(searchText.toLowerCase()) ||
|
||||||
(j.plate_no || "")
|
(j.plate_no || "")
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.includes(searchText.toLowerCase()) ||
|
.includes(searchText.toLowerCase()) ||
|
||||||
(j.v_model_desc || "")
|
(j.v_model_desc || "")
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.includes(searchText.toLowerCase()) ||
|
.includes(searchText.toLowerCase()) ||
|
||||||
(j.est_ct_fn || "")
|
(j.est_ct_fn || "")
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.includes(searchText.toLowerCase()) ||
|
.includes(searchText.toLowerCase()) ||
|
||||||
(j.est_ct_ln || "")
|
(j.est_ct_ln || "")
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.includes(searchText.toLowerCase()) ||
|
.includes(searchText.toLowerCase()) ||
|
||||||
(j.v_make_desc || "")
|
(j.v_make_desc || "")
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.includes(searchText.toLowerCase())
|
.includes(searchText.toLowerCase())
|
||||||
)
|
)
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
const handleTableChange = (pagination, filters, sorter) => {
|
const handleTableChange = (pagination, filters, sorter) => {
|
||||||
setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
|
setState({ ...state, sortedInfo: sorter });
|
||||||
};
|
setFilter(filters);
|
||||||
|
};
|
||||||
|
|
||||||
const handleOnRowClick = (record) => {
|
const handleOnRowClick = (record) => {
|
||||||
if (record) {
|
if (record) {
|
||||||
if (record.id) {
|
if (record.id) {
|
||||||
history.push({
|
history.push({
|
||||||
search: queryString.stringify({
|
search: queryString.stringify({
|
||||||
...searchParams,
|
...searchParams,
|
||||||
selected: record.id,
|
selected: record.id,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
title: t("jobs.fields.ro_number"),
|
title: t("jobs.fields.ro_number"),
|
||||||
dataIndex: "ro_number",
|
dataIndex: "ro_number",
|
||||||
key: "ro_number",
|
key: "ro_number",
|
||||||
sorter: (a, b) =>
|
sorter: (a, b) =>
|
||||||
parseInt((a.ro_number || "0").replace(/\D/g, "")) -
|
parseInt((a.ro_number || "0").replace(/\D/g, "")) -
|
||||||
parseInt((b.ro_number || "0").replace(/\D/g, "")),
|
parseInt((b.ro_number || "0").replace(/\D/g, "")),
|
||||||
sortOrder:
|
sortOrder:
|
||||||
state.sortedInfo.columnKey === "ro_number" && state.sortedInfo.order,
|
state.sortedInfo.columnKey === "ro_number" && state.sortedInfo.order,
|
||||||
|
render: (text, record) => (
|
||||||
render: (text, record) => (
|
<Link
|
||||||
<Link
|
to={"/manage/jobs/" + record.id}
|
||||||
to={"/manage/jobs/" + record.id}
|
onClick={(e) => e.stopPropagation()}
|
||||||
onClick={(e) => e.stopPropagation()}
|
>
|
||||||
>
|
<Space>
|
||||||
<Space>
|
{record.ro_number || t("general.labels.na")}
|
||||||
{record.ro_number || t("general.labels.na")}
|
{record.production_vars && record.production_vars.alert ? (
|
||||||
{record.production_vars && record.production_vars.alert ? (
|
<ExclamationCircleFilled className="production-alert" />
|
||||||
<ExclamationCircleFilled className="production-alert" />
|
) : null}
|
||||||
) : null}
|
{record.suspended && (
|
||||||
{record.suspended && (
|
<PauseCircleOutlined style={{ color: "orangered" }} />
|
||||||
<PauseCircleOutlined style={{ color: "orangered" }} />
|
)}
|
||||||
)}
|
{record.iouparent && (
|
||||||
{record.iouparent && (
|
<Tooltip title={t("jobs.labels.iou")}>
|
||||||
<Tooltip title={t("jobs.labels.iou")}>
|
<BranchesOutlined style={{ color: "orangered" }} />
|
||||||
<BranchesOutlined style={{ color: "orangered" }} />
|
</Tooltip>
|
||||||
</Tooltip>
|
)}
|
||||||
)}
|
</Space>
|
||||||
</Space>
|
</Link>
|
||||||
</Link>
|
),
|
||||||
),
|
},
|
||||||
},
|
{
|
||||||
{
|
title: t("jobs.fields.owner"),
|
||||||
title: t("jobs.fields.owner"),
|
dataIndex: "owner",
|
||||||
dataIndex: "owner",
|
key: "owner",
|
||||||
key: "owner",
|
ellipsis: true,
|
||||||
ellipsis: true,
|
responsive: ["md"],
|
||||||
|
sorter: (a, b) => alphaSort(a.ownr_ln, b.ownr_ln),
|
||||||
responsive: ["md"],
|
sortOrder:
|
||||||
sorter: (a, b) => alphaSort(a.ownr_ln, b.ownr_ln),
|
state.sortedInfo.columnKey === "owner" && state.sortedInfo.order,
|
||||||
sortOrder:
|
render: (text, record) => {
|
||||||
state.sortedInfo.columnKey === "owner" && state.sortedInfo.order,
|
return record.ownerid ? (
|
||||||
render: (text, record) => {
|
<Link
|
||||||
return record.ownerid ? (
|
to={"/manage/owners/" + record.ownerid}
|
||||||
<Link
|
onClick={(e) => e.stopPropagation()}
|
||||||
to={"/manage/owners/" + record.ownerid}
|
>
|
||||||
onClick={(e) => e.stopPropagation()}
|
<OwnerNameDisplay ownerObject={record} />
|
||||||
>
|
</Link>
|
||||||
<OwnerNameDisplay ownerObject={record} />
|
) : (
|
||||||
</Link>
|
<span>
|
||||||
) : (
|
|
||||||
<span>
|
|
||||||
<OwnerNameDisplay ownerObject={record} />
|
<OwnerNameDisplay ownerObject={record} />
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("jobs.fields.ownr_ph1"),
|
||||||
|
dataIndex: "ownr_ph1",
|
||||||
|
key: "ownr_ph1",
|
||||||
|
ellipsis: true,
|
||||||
|
responsive: ["md"],
|
||||||
|
render: (text, record) => (
|
||||||
|
<ChatOpenButton phone={record.ownr_ph1} jobid={record.id} />
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("jobs.fields.ownr_ph2"),
|
||||||
|
dataIndex: "ownr_ph2",
|
||||||
|
key: "ownr_ph2",
|
||||||
|
ellipsis: true,
|
||||||
|
responsive: ["md"],
|
||||||
|
render: (text, record) => (
|
||||||
|
<ChatOpenButton phone={record.ownr_ph2} jobid={record.id} />
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("jobs.fields.status"),
|
||||||
|
dataIndex: "status",
|
||||||
|
key: "status",
|
||||||
|
ellipsis: true,
|
||||||
|
sorter: (a, b) => alphaSort(a.status, b.status),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "status" && state.sortedInfo.order,
|
||||||
|
filteredValue: filter?.status || null,
|
||||||
|
filters:
|
||||||
|
(jobs &&
|
||||||
|
jobs
|
||||||
|
.map((j) => j.status)
|
||||||
|
.filter(onlyUnique)
|
||||||
|
.map((s) => {
|
||||||
|
return {
|
||||||
|
text: s || "No Status*",
|
||||||
|
value: [s],
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.sort((a, b) =>
|
||||||
|
statusSort(
|
||||||
|
a.text,
|
||||||
|
b.text,
|
||||||
|
bodyshop.md_ro_statuses.active_statuses
|
||||||
|
)
|
||||||
|
)) ||
|
||||||
|
[],
|
||||||
|
onFilter: (value, record) => value.includes(record.status),
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
title: t("jobs.fields.vehicle"),
|
||||||
|
dataIndex: "vehicle",
|
||||||
|
key: "vehicle",
|
||||||
|
ellipsis: true,
|
||||||
|
render: (text, record) => {
|
||||||
|
return record.vehicleid ? (
|
||||||
|
<Link
|
||||||
|
to={"/manage/vehicles/" + record.vehicleid}
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
>
|
||||||
|
{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
|
||||||
|
record.v_model_desc || ""
|
||||||
|
}`}
|
||||||
|
</Link>
|
||||||
|
) : (
|
||||||
|
<span>{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
|
||||||
|
record.v_model_desc || ""
|
||||||
|
}`}</span>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("vehicles.fields.plate_no"),
|
||||||
|
dataIndex: "plate_no",
|
||||||
|
key: "plate_no",
|
||||||
|
ellipsis: true,
|
||||||
|
|
||||||
|
responsive: ["md"],
|
||||||
|
sorter: (a, b) => alphaSort(a.plate_no, b.plate_no),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "plate_no" && state.sortedInfo.order,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("jobs.fields.clm_no"),
|
||||||
|
dataIndex: "clm_no",
|
||||||
|
key: "clm_no",
|
||||||
|
ellipsis: true,
|
||||||
|
responsive: ["md"],
|
||||||
|
sorter: (a, b) => alphaSort(a.clm_no, b.clm_no),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "clm_no" && state.sortedInfo.order,
|
||||||
|
render: (text, record) =>
|
||||||
|
`${record.clm_no || ""}${
|
||||||
|
record.po_number ? ` (PO: ${record.po_number})` : ""
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("jobs.fields.ins_co_nm"),
|
||||||
|
dataIndex: "ins_co_nm",
|
||||||
|
key: "ins_co_nm",
|
||||||
|
ellipsis: true,
|
||||||
|
filteredValue: filter?.ins_co_nm || null,
|
||||||
|
filters:
|
||||||
|
(jobs &&
|
||||||
|
jobs
|
||||||
|
.map((j) => j.ins_co_nm)
|
||||||
|
.filter(onlyUnique)
|
||||||
|
.map((s) => {
|
||||||
|
return {
|
||||||
|
text: s || "No Ins. Co.*",
|
||||||
|
value: [s],
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.sort((a, b) => alphaSort(a.text, b.text))) ||
|
||||||
|
[],
|
||||||
|
onFilter: (value, record) => value.includes(record.ins_co_nm),
|
||||||
|
responsive: ["md"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("jobs.fields.clm_total"),
|
||||||
|
dataIndex: "clm_total",
|
||||||
|
key: "clm_total",
|
||||||
|
responsive: ["md"],
|
||||||
|
ellipsis: true,
|
||||||
|
sorter: (a, b) => a.clm_total - b.clm_total,
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "clm_total" && state.sortedInfo.order,
|
||||||
|
render: (text, record) => (
|
||||||
|
<CurrencyFormatter>{record.clm_total}</CurrencyFormatter>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("jobs.labels.estimator"),
|
||||||
|
dataIndex: "jobs.labels.estimator",
|
||||||
|
key: "estimator",
|
||||||
|
ellipsis: true,
|
||||||
|
responsive: ["xl"],
|
||||||
|
filterSearch: true,
|
||||||
|
filteredValue: filter?.estimator || null,
|
||||||
|
filters:
|
||||||
|
(jobs &&
|
||||||
|
jobs
|
||||||
|
.map((j) => `${j.est_ct_fn || ""} ${j.est_ct_ln || ""}`.trim())
|
||||||
|
.filter(onlyUnique)
|
||||||
|
.map((s) => {
|
||||||
|
return {
|
||||||
|
text: s || "No Estimator*",
|
||||||
|
value: [s],
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.sort((a, b) => alphaSort(a.text, b.text))) ||
|
||||||
|
[],
|
||||||
|
onFilter: (value, record) =>
|
||||||
|
value.includes(
|
||||||
|
`${record.est_ct_fn || ""} ${record.est_ct_ln || ""}`.trim()
|
||||||
|
),
|
||||||
|
render: (text, record) =>
|
||||||
|
`${record.est_ct_fn || ""} ${record.est_ct_ln || ""}`.trim(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("jobs.fields.comment"),
|
||||||
|
dataIndex: "comment",
|
||||||
|
key: "comment",
|
||||||
|
ellipsis: true,
|
||||||
|
responsive: ["md"],
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// title: t("jobs.fields.owner_owing"),
|
||||||
|
// dataIndex: "owner_owing",
|
||||||
|
// key: "owner_owing",
|
||||||
|
// responsive: ["md"],
|
||||||
|
// render: (text, record) => (
|
||||||
|
// <CurrencyFormatter>{record.owner_owing}</CurrencyFormatter>
|
||||||
|
// ),
|
||||||
|
// },
|
||||||
|
];
|
||||||
|
|
||||||
|
const scrollMapper = {
|
||||||
|
xs: true,
|
||||||
|
sm: true,
|
||||||
|
md: true,
|
||||||
|
lg: "100%",
|
||||||
|
xl: "100%",
|
||||||
|
xxl: "100%",
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
title={t("titles.bc.jobs-active")}
|
||||||
|
extra={
|
||||||
|
<Space wrap>
|
||||||
|
<Button onClick={() => refetch()}>
|
||||||
|
<SyncOutlined />
|
||||||
|
</Button>
|
||||||
|
<Input.Search
|
||||||
|
placeholder={t("general.labels.search")}
|
||||||
|
onChange={(e) => {
|
||||||
|
setSearchText(e.target.value);
|
||||||
|
}}
|
||||||
|
value={searchText}
|
||||||
|
enterButton
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Table
|
||||||
|
loading={loading}
|
||||||
|
pagination={{ defaultPageSize: 50 }}
|
||||||
|
columns={columns}
|
||||||
|
rowKey="id"
|
||||||
|
dataSource={jobs}
|
||||||
|
scroll={{
|
||||||
|
x: selectedBreakpoint ? scrollMapper[selectedBreakpoint[0]] : "100%",
|
||||||
|
}}
|
||||||
|
rowSelection={{
|
||||||
|
onSelect: (record) => {
|
||||||
|
handleOnRowClick(record);
|
||||||
|
},
|
||||||
|
selectedRowKeys: [selected],
|
||||||
|
type: "radio",
|
||||||
|
}}
|
||||||
|
onChange={handleTableChange}
|
||||||
|
onRow={(record, rowIndex) => {
|
||||||
|
return {
|
||||||
|
onClick: (event) => {
|
||||||
|
handleOnRowClick(record);
|
||||||
},
|
},
|
||||||
},
|
};
|
||||||
{
|
}}
|
||||||
title: t("jobs.fields.ownr_ph1"),
|
/>
|
||||||
dataIndex: "ownr_ph1",
|
</Card>
|
||||||
key: "ownr_ph1",
|
);
|
||||||
ellipsis: true,
|
|
||||||
responsive: ["md"],
|
|
||||||
render: (text, record) => (
|
|
||||||
<ChatOpenButton phone={record.ownr_ph1} jobid={record.id} />
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t("jobs.fields.ownr_ph2"),
|
|
||||||
dataIndex: "ownr_ph2",
|
|
||||||
key: "ownr_ph2",
|
|
||||||
ellipsis: true,
|
|
||||||
responsive: ["md"],
|
|
||||||
render: (text, record) => (
|
|
||||||
<ChatOpenButton phone={record.ownr_ph2} jobid={record.id} />
|
|
||||||
),
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
title: t("jobs.fields.status"),
|
|
||||||
dataIndex: "status",
|
|
||||||
key: "status",
|
|
||||||
ellipsis: true,
|
|
||||||
|
|
||||||
sorter: (a, b) => alphaSort(a.status, b.status),
|
|
||||||
sortOrder:
|
|
||||||
state.sortedInfo.columnKey === "status" && state.sortedInfo.order,
|
|
||||||
filters:
|
|
||||||
(jobs &&
|
|
||||||
jobs
|
|
||||||
.map((j) => j.status)
|
|
||||||
.filter(onlyUnique)
|
|
||||||
.map((s) => {
|
|
||||||
return {
|
|
||||||
text: s || "No Status*",
|
|
||||||
value: [s],
|
|
||||||
};
|
|
||||||
})) ||
|
|
||||||
[],
|
|
||||||
onFilter: (value, record) => value.includes(record.status),
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
title: t("jobs.fields.vehicle"),
|
|
||||||
dataIndex: "vehicle",
|
|
||||||
key: "vehicle",
|
|
||||||
ellipsis: true,
|
|
||||||
render: (text, record) => {
|
|
||||||
return record.vehicleid ? (
|
|
||||||
<Link
|
|
||||||
to={"/manage/vehicles/" + record.vehicleid}
|
|
||||||
onClick={(e) => e.stopPropagation()}
|
|
||||||
>
|
|
||||||
{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
|
|
||||||
record.v_model_desc || ""
|
|
||||||
}`}
|
|
||||||
</Link>
|
|
||||||
) : (
|
|
||||||
<span>{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
|
|
||||||
record.v_model_desc || ""
|
|
||||||
}`}</span>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t("vehicles.fields.plate_no"),
|
|
||||||
dataIndex: "plate_no",
|
|
||||||
key: "plate_no",
|
|
||||||
ellipsis: true,
|
|
||||||
|
|
||||||
responsive: ["md"],
|
|
||||||
sorter: (a, b) => alphaSort(a.plate_no, b.plate_no),
|
|
||||||
sortOrder:
|
|
||||||
state.sortedInfo.columnKey === "plate_no" && state.sortedInfo.order,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t("jobs.fields.clm_no"),
|
|
||||||
dataIndex: "clm_no",
|
|
||||||
key: "clm_no",
|
|
||||||
ellipsis: true,
|
|
||||||
responsive: ["md"],
|
|
||||||
sorter: (a, b) => alphaSort(a.clm_no, b.clm_no),
|
|
||||||
sortOrder:
|
|
||||||
state.sortedInfo.columnKey === "clm_no" && state.sortedInfo.order,
|
|
||||||
render: (text, record) =>
|
|
||||||
`${record.clm_no || ""}${
|
|
||||||
record.po_number ? ` (PO: ${record.po_number})` : ""
|
|
||||||
}`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t("jobs.fields.ins_co_nm"),
|
|
||||||
dataIndex: "ins_co_nm",
|
|
||||||
key: "ins_co_nm",
|
|
||||||
ellipsis: true,
|
|
||||||
filters:
|
|
||||||
(jobs &&
|
|
||||||
jobs
|
|
||||||
.map((j) => j.ins_co_nm)
|
|
||||||
.filter(onlyUnique)
|
|
||||||
.map((s) => {
|
|
||||||
return {
|
|
||||||
text: s,
|
|
||||||
value: [s],
|
|
||||||
};
|
|
||||||
})) ||
|
|
||||||
[],
|
|
||||||
onFilter: (value, record) => value.includes(record.ins_co_nm),
|
|
||||||
responsive: ["md"],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t("jobs.fields.clm_total"),
|
|
||||||
dataIndex: "clm_total",
|
|
||||||
key: "clm_total",
|
|
||||||
responsive: ["md"],
|
|
||||||
ellipsis: true,
|
|
||||||
|
|
||||||
sorter: (a, b) => a.clm_total - b.clm_total,
|
|
||||||
sortOrder:
|
|
||||||
state.sortedInfo.columnKey === "clm_total" && state.sortedInfo.order,
|
|
||||||
render: (text, record) => (
|
|
||||||
<CurrencyFormatter>{record.clm_total}</CurrencyFormatter>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t("jobs.labels.estimator"),
|
|
||||||
dataIndex: "jobs.labels.estimator",
|
|
||||||
key: "jobs.labels.estimator",
|
|
||||||
ellipsis: true,
|
|
||||||
responsive: ["xl"],
|
|
||||||
filterSearch: true,
|
|
||||||
filters:
|
|
||||||
(jobs &&
|
|
||||||
jobs
|
|
||||||
.map((j) => `${j.est_ct_fn || ""} ${j.est_ct_ln || ""}`.trim())
|
|
||||||
.filter(onlyUnique)
|
|
||||||
.map((s) => {
|
|
||||||
return {
|
|
||||||
text: s || "N/A",
|
|
||||||
value: [s],
|
|
||||||
};
|
|
||||||
})) ||
|
|
||||||
[],
|
|
||||||
onFilter: (value, record) =>
|
|
||||||
value.includes(
|
|
||||||
`${record.est_ct_fn || ""} ${record.est_ct_ln || ""}`.trim()
|
|
||||||
),
|
|
||||||
render: (text, record) =>
|
|
||||||
`${record.est_ct_fn || ""} ${record.est_ct_ln || ""}`.trim(),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t("jobs.fields.comment"),
|
|
||||||
dataIndex: "comment",
|
|
||||||
key: "comment",
|
|
||||||
ellipsis: true,
|
|
||||||
responsive: ["md"],
|
|
||||||
},
|
|
||||||
// {
|
|
||||||
// title: t("jobs.fields.owner_owing"),
|
|
||||||
// dataIndex: "owner_owing",
|
|
||||||
// key: "owner_owing",
|
|
||||||
// responsive: ["md"],
|
|
||||||
// render: (text, record) => (
|
|
||||||
// <CurrencyFormatter>{record.owner_owing}</CurrencyFormatter>
|
|
||||||
// ),
|
|
||||||
// },
|
|
||||||
];
|
|
||||||
|
|
||||||
const scrollMapper = {
|
|
||||||
xs: true,
|
|
||||||
sm: true,
|
|
||||||
md: true,
|
|
||||||
lg: "100%",
|
|
||||||
xl: "100%",
|
|
||||||
xxl: "100%",
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Card
|
|
||||||
title={t("titles.bc.jobs-active")}
|
|
||||||
extra={
|
|
||||||
<Space wrap>
|
|
||||||
<Button onClick={() => refetch()}>
|
|
||||||
<SyncOutlined />
|
|
||||||
</Button>
|
|
||||||
<Input.Search
|
|
||||||
placeholder={t("general.labels.search")}
|
|
||||||
onChange={(e) => {
|
|
||||||
setSearchText(e.target.value);
|
|
||||||
}}
|
|
||||||
value={searchText}
|
|
||||||
enterButton
|
|
||||||
/>
|
|
||||||
</Space>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Table
|
|
||||||
loading={loading}
|
|
||||||
pagination={{ defaultPageSize: 50 }}
|
|
||||||
columns={columns}
|
|
||||||
rowKey="id"
|
|
||||||
dataSource={jobs}
|
|
||||||
scroll={{
|
|
||||||
x: selectedBreakpoint ? scrollMapper[selectedBreakpoint[0]] : "100%",
|
|
||||||
}}
|
|
||||||
rowSelection={{
|
|
||||||
onSelect: (record) => {
|
|
||||||
handleOnRowClick(record);
|
|
||||||
},
|
|
||||||
selectedRowKeys: [selected],
|
|
||||||
type: "radio",
|
|
||||||
}}
|
|
||||||
onChange={handleTableChange}
|
|
||||||
onRow={(record, rowIndex) => {
|
|
||||||
return {
|
|
||||||
onClick: (event) => {
|
|
||||||
handleOnRowClick(record);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect(mapStateToProps, null)(JobsList);
|
export default connect(mapStateToProps, null)(JobsList);
|
||||||
|
|||||||
@@ -16,11 +16,12 @@ import { QUERY_ALL_ACTIVE_JOBS } from "../../graphql/jobs.queries";
|
|||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||||
import { onlyUnique } from "../../utils/arrayHelper";
|
import { onlyUnique } from "../../utils/arrayHelper";
|
||||||
import { alphaSort } from "../../utils/sorters";
|
import { pageLimit } from "../../utils/config";
|
||||||
|
import { alphaSort, statusSort } from "../../utils/sorters";
|
||||||
|
import useLocalStorage from "../../utils/useLocalStorage";
|
||||||
import AlertComponent from "../alert/alert.component";
|
import AlertComponent from "../alert/alert.component";
|
||||||
import ChatOpenButton from "../chat-open-button/chat-open-button.component";
|
import ChatOpenButton from "../chat-open-button/chat-open-button.component";
|
||||||
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
||||||
import {pageLimit} from "../../utils/config";
|
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
@@ -53,10 +54,8 @@ export function JobsReadyList({ bodyshop }) {
|
|||||||
nextFetchPolicy: "network-only",
|
nextFetchPolicy: "network-only",
|
||||||
});
|
});
|
||||||
|
|
||||||
const [state, setState] = useState({
|
const [state, setState] = useState({ sortedInfo: {} });
|
||||||
sortedInfo: {},
|
const [filter, setFilter] = useLocalStorage("filter_jobs_ready", null);
|
||||||
filteredInfo: { text: "" },
|
|
||||||
});
|
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
@@ -105,7 +104,8 @@ export function JobsReadyList({ bodyshop }) {
|
|||||||
: [];
|
: [];
|
||||||
|
|
||||||
const handleTableChange = (pagination, filters, sorter) => {
|
const handleTableChange = (pagination, filters, sorter) => {
|
||||||
setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
|
setState({ ...state, sortedInfo: sorter });
|
||||||
|
setFilter(filters);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleOnRowClick = (record) => {
|
const handleOnRowClick = (record) => {
|
||||||
@@ -129,7 +129,6 @@ export function JobsReadyList({ bodyshop }) {
|
|||||||
sorter: (a, b) => alphaSort(a.ro_number, b.ro_number),
|
sorter: (a, b) => alphaSort(a.ro_number, b.ro_number),
|
||||||
sortOrder:
|
sortOrder:
|
||||||
state.sortedInfo.columnKey === "ro_number" && state.sortedInfo.order,
|
state.sortedInfo.columnKey === "ro_number" && state.sortedInfo.order,
|
||||||
|
|
||||||
render: (text, record) => (
|
render: (text, record) => (
|
||||||
<Link
|
<Link
|
||||||
to={"/manage/jobs/" + record.id}
|
to={"/manage/jobs/" + record.id}
|
||||||
@@ -157,7 +156,6 @@ export function JobsReadyList({ bodyshop }) {
|
|||||||
dataIndex: "owner",
|
dataIndex: "owner",
|
||||||
key: "owner",
|
key: "owner",
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
|
|
||||||
responsive: ["md"],
|
responsive: ["md"],
|
||||||
sorter: (a, b) => alphaSort(a.ownr_ln, b.ownr_ln),
|
sorter: (a, b) => alphaSort(a.ownr_ln, b.ownr_ln),
|
||||||
sortOrder:
|
sortOrder:
|
||||||
@@ -197,16 +195,15 @@ export function JobsReadyList({ bodyshop }) {
|
|||||||
<ChatOpenButton phone={record.ownr_ph2} jobid={record.id} />
|
<ChatOpenButton phone={record.ownr_ph2} jobid={record.id} />
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
title: t("jobs.fields.status"),
|
title: t("jobs.fields.status"),
|
||||||
dataIndex: "status",
|
dataIndex: "status",
|
||||||
key: "status",
|
key: "status",
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
|
|
||||||
sorter: (a, b) => alphaSort(a.status, b.status),
|
sorter: (a, b) => alphaSort(a.status, b.status),
|
||||||
sortOrder:
|
sortOrder:
|
||||||
state.sortedInfo.columnKey === "status" && state.sortedInfo.order,
|
state.sortedInfo.columnKey === "status" && state.sortedInfo.order,
|
||||||
|
filteredValue: filter?.status || null,
|
||||||
filters:
|
filters:
|
||||||
(jobs &&
|
(jobs &&
|
||||||
jobs
|
jobs
|
||||||
@@ -217,11 +214,17 @@ export function JobsReadyList({ bodyshop }) {
|
|||||||
text: s || "No Status*",
|
text: s || "No Status*",
|
||||||
value: [s],
|
value: [s],
|
||||||
};
|
};
|
||||||
})) ||
|
})
|
||||||
|
.sort((a, b) =>
|
||||||
|
statusSort(
|
||||||
|
a.text,
|
||||||
|
b.text,
|
||||||
|
bodyshop.md_ro_statuses.active_statuses
|
||||||
|
)
|
||||||
|
)) ||
|
||||||
[],
|
[],
|
||||||
onFilter: (value, record) => value.includes(record.status),
|
onFilter: (value, record) => value.includes(record.status),
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
title: t("jobs.fields.vehicle"),
|
title: t("jobs.fields.vehicle"),
|
||||||
dataIndex: "vehicle",
|
dataIndex: "vehicle",
|
||||||
@@ -274,6 +277,7 @@ export function JobsReadyList({ bodyshop }) {
|
|||||||
dataIndex: "ins_co_nm",
|
dataIndex: "ins_co_nm",
|
||||||
key: "ins_co_nm",
|
key: "ins_co_nm",
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
|
filteredValue: filter?.ins_co_nm || null,
|
||||||
filters:
|
filters:
|
||||||
(jobs &&
|
(jobs &&
|
||||||
jobs
|
jobs
|
||||||
@@ -281,10 +285,11 @@ export function JobsReadyList({ bodyshop }) {
|
|||||||
.filter(onlyUnique)
|
.filter(onlyUnique)
|
||||||
.map((s) => {
|
.map((s) => {
|
||||||
return {
|
return {
|
||||||
text: s,
|
text: s || "No Ins Co.*",
|
||||||
value: [s],
|
value: [s],
|
||||||
};
|
};
|
||||||
})) ||
|
})
|
||||||
|
.sort((a, b) => alphaSort(a.text, b.text))) ||
|
||||||
[],
|
[],
|
||||||
onFilter: (value, record) => value.includes(record.ins_co_nm),
|
onFilter: (value, record) => value.includes(record.ins_co_nm),
|
||||||
responsive: ["md"],
|
responsive: ["md"],
|
||||||
@@ -295,7 +300,6 @@ export function JobsReadyList({ bodyshop }) {
|
|||||||
key: "clm_total",
|
key: "clm_total",
|
||||||
responsive: ["md"],
|
responsive: ["md"],
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
|
|
||||||
sorter: (a, b) => a.clm_total - b.clm_total,
|
sorter: (a, b) => a.clm_total - b.clm_total,
|
||||||
sortOrder:
|
sortOrder:
|
||||||
state.sortedInfo.columnKey === "clm_total" && state.sortedInfo.order,
|
state.sortedInfo.columnKey === "clm_total" && state.sortedInfo.order,
|
||||||
@@ -306,9 +310,10 @@ export function JobsReadyList({ bodyshop }) {
|
|||||||
{
|
{
|
||||||
title: t("jobs.labels.estimator"),
|
title: t("jobs.labels.estimator"),
|
||||||
dataIndex: "jobs.labels.estimator",
|
dataIndex: "jobs.labels.estimator",
|
||||||
key: "jobs.labels.estimator",
|
key: "estimator",
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
responsive: ["xl"],
|
responsive: ["xl"],
|
||||||
|
filteredValue: filter?.estimator || null,
|
||||||
filterSearch: true,
|
filterSearch: true,
|
||||||
filters:
|
filters:
|
||||||
(jobs &&
|
(jobs &&
|
||||||
@@ -317,10 +322,11 @@ export function JobsReadyList({ bodyshop }) {
|
|||||||
.filter(onlyUnique)
|
.filter(onlyUnique)
|
||||||
.map((s) => {
|
.map((s) => {
|
||||||
return {
|
return {
|
||||||
text: s || "N/A",
|
text: s || "No Estimator*",
|
||||||
value: [s],
|
value: [s],
|
||||||
};
|
};
|
||||||
})) ||
|
})
|
||||||
|
.sort((a, b) => alphaSort(a.text, b.text))) ||
|
||||||
[],
|
[],
|
||||||
onFilter: (value, record) =>
|
onFilter: (value, record) =>
|
||||||
value.includes(
|
value.includes(
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { Space, Tag } from "antd";
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
export default function JobsRelatedRos({ jobid, job }) {
|
export default function JobsRelatedRos({ jobid, job, disabled }) {
|
||||||
if (!(job && job.vehicle && job.vehicle.jobs)) return null;
|
if (!(job && job.vehicle && job.vehicle.jobs)) return null;
|
||||||
return (
|
return (
|
||||||
<Space wrap>
|
<Space wrap>
|
||||||
@@ -10,9 +10,15 @@ export default function JobsRelatedRos({ jobid, job }) {
|
|||||||
.filter((j) => j.id !== job.id)
|
.filter((j) => j.id !== job.id)
|
||||||
.map((j) => (
|
.map((j) => (
|
||||||
<Tag key={j.id}>
|
<Tag key={j.id}>
|
||||||
<Link to={`/manage/jobs/${j?.id}`}>{`${j.ro_number || "N/A"}${
|
{disabled ? (
|
||||||
j.clm_no ? ` | ${j.clm_no}` : ""
|
<>{`${j.ro_number || "N/A"}${j.clm_no ? ` | ${j.clm_no}` : ""}${
|
||||||
}${j.status ? ` | ${j.status}` : ""}`}</Link>
|
j.status ? ` | ${j.status}` : ""
|
||||||
|
}`}</>
|
||||||
|
) : (
|
||||||
|
<Link to={`/manage/jobs/${j?.id}`}>{`${j.ro_number || "N/A"}${
|
||||||
|
j.clm_no ? ` | ${j.clm_no}` : ""
|
||||||
|
}${j.status ? ` | ${j.status}` : ""}`}</Link>
|
||||||
|
)}
|
||||||
</Tag>
|
</Tag>
|
||||||
))}
|
))}
|
||||||
</Space>
|
</Space>
|
||||||
|
|||||||
@@ -0,0 +1,77 @@
|
|||||||
|
import { useQuery } from "@apollo/client";
|
||||||
|
import { Card, Divider, Drawer, Grid } from "antd";
|
||||||
|
import queryString from "query-string";
|
||||||
|
import React from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { Link, useHistory, useLocation } from "react-router-dom";
|
||||||
|
import { QUERY_PARTS_QUEUE_CARD_DETAILS } from "../../graphql/jobs.queries";
|
||||||
|
import AlertComponent from "../alert/alert.component";
|
||||||
|
import JobsDetailHeader from "../jobs-detail-header/jobs-detail-header.component";
|
||||||
|
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
|
||||||
|
import PartsQueueJobLinesComponent from "./parts-queue-job-lines.component";
|
||||||
|
|
||||||
|
export default function PartsQueueDetailCard() {
|
||||||
|
const selectedBreakpoint = Object.entries(Grid.useBreakpoint())
|
||||||
|
.filter((screen) => !!screen[1])
|
||||||
|
.slice(-1)[0];
|
||||||
|
|
||||||
|
const bpoints = {
|
||||||
|
xs: "100%",
|
||||||
|
sm: "100%",
|
||||||
|
md: "100%",
|
||||||
|
lg: "75%",
|
||||||
|
xl: "75%",
|
||||||
|
xxl: "60%",
|
||||||
|
};
|
||||||
|
const drawerPercentage = selectedBreakpoint
|
||||||
|
? bpoints[selectedBreakpoint[0]]
|
||||||
|
: "100%";
|
||||||
|
|
||||||
|
const searchParams = queryString.parse(useLocation().search);
|
||||||
|
const { selected } = searchParams;
|
||||||
|
const history = useHistory();
|
||||||
|
const { loading, error, data } = useQuery(QUERY_PARTS_QUEUE_CARD_DETAILS, {
|
||||||
|
variables: { id: selected },
|
||||||
|
skip: !selected,
|
||||||
|
fetchPolicy: "network-only",
|
||||||
|
nextFetchPolicy: "network-only",
|
||||||
|
});
|
||||||
|
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const handleDrawerClose = () => {
|
||||||
|
delete searchParams.selected;
|
||||||
|
history.push({
|
||||||
|
search: queryString.stringify({
|
||||||
|
...searchParams,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Drawer
|
||||||
|
visible={!!selected}
|
||||||
|
destroyOnClose
|
||||||
|
width={drawerPercentage}
|
||||||
|
placement="right"
|
||||||
|
onClose={handleDrawerClose}
|
||||||
|
>
|
||||||
|
{loading ? <LoadingSpinner /> : null}
|
||||||
|
{error ? <AlertComponent message={error.message} type="error" /> : null}
|
||||||
|
{data ? (
|
||||||
|
<Card
|
||||||
|
title={
|
||||||
|
<Link to={`/manage/jobs/${data.jobs_by_pk.id}`}>
|
||||||
|
{data.jobs_by_pk.ro_number || t("general.labels.na")}
|
||||||
|
</Link>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<JobsDetailHeader job={data ? data.jobs_by_pk : null} />
|
||||||
|
<Divider type="horizontal" />
|
||||||
|
<PartsQueueJobLinesComponent
|
||||||
|
jobLines={data.jobs_by_pk ? data.jobs_by_pk.joblines : null}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
) : null}
|
||||||
|
</Drawer>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,208 @@
|
|||||||
|
import { Card, Table } from "antd";
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
||||||
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
|
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||||
|
import { onlyUnique } from "../../utils/arrayHelper";
|
||||||
|
import { alphaSort } from "../../utils/sorters";
|
||||||
|
|
||||||
|
const mapStateToProps = createStructuredSelector({
|
||||||
|
bodyshop: selectBodyshop,
|
||||||
|
jobRO: selectJobReadOnly,
|
||||||
|
});
|
||||||
|
|
||||||
|
const mapDispatchToProps = (dispatch) => ({});
|
||||||
|
|
||||||
|
export function PartsQueueJobLinesComponent({ jobRO, loading, jobLines }) {
|
||||||
|
const [state, setState] = useState({
|
||||||
|
sortedInfo: {},
|
||||||
|
filteredInfo: {},
|
||||||
|
});
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: "#",
|
||||||
|
dataIndex: "line_no",
|
||||||
|
key: "line_no",
|
||||||
|
sorter: (a, b) => a.line_no - b.line_no,
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "line_no" && state.sortedInfo.order,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("joblines.fields.line_desc"),
|
||||||
|
dataIndex: "line_desc",
|
||||||
|
key: "line_desc",
|
||||||
|
sorter: (a, b) => alphaSort(a.line_desc, b.line_desc),
|
||||||
|
onCell: (record) => ({
|
||||||
|
className: record.manual_line && "job-line-manual",
|
||||||
|
style: {
|
||||||
|
...(record.critical ? { boxShadow: " -.5em 0 0 #FFC107" } : {}),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "line_desc" && state.sortedInfo.order,
|
||||||
|
ellipsis: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("joblines.fields.oem_partno"),
|
||||||
|
dataIndex: "oem_partno",
|
||||||
|
key: "oem_partno",
|
||||||
|
sorter: (a, b) => alphaSort(a.oem_partno, b.oem_partno),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "oem_partno" && state.sortedInfo.order,
|
||||||
|
ellipsis: true,
|
||||||
|
render: (text, record) =>
|
||||||
|
`${record.oem_partno || ""} ${
|
||||||
|
record.alt_partno ? `(${record.alt_partno})` : ""
|
||||||
|
}`.trim(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("joblines.fields.part_type"),
|
||||||
|
dataIndex: "part_type",
|
||||||
|
key: "part_type",
|
||||||
|
filteredValue: state.filteredInfo.part_type || null,
|
||||||
|
sorter: (a, b) => alphaSort(a.part_type, b.part_type),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "part_type" && state.sortedInfo.order,
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
text: t("jobs.labels.partsfilter"),
|
||||||
|
value: [
|
||||||
|
"PAN",
|
||||||
|
"PAC",
|
||||||
|
"PAR",
|
||||||
|
"PAL",
|
||||||
|
"PAA",
|
||||||
|
"PAM",
|
||||||
|
"PAP",
|
||||||
|
"PAS",
|
||||||
|
"PASL",
|
||||||
|
"PAG",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: t("joblines.fields.part_types.PAN"),
|
||||||
|
value: ["PAN"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: t("joblines.fields.part_types.PAP"),
|
||||||
|
value: ["PAP"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: t("joblines.fields.part_types.PAL"),
|
||||||
|
value: ["PAL"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: t("joblines.fields.part_types.PAA"),
|
||||||
|
value: ["PAA"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: t("joblines.fields.part_types.PAG"),
|
||||||
|
value: ["PAG"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: t("joblines.fields.part_types.PAS"),
|
||||||
|
value: ["PAS"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: t("joblines.fields.part_types.PASL"),
|
||||||
|
value: ["PASL"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: t("joblines.fields.part_types.PAC"),
|
||||||
|
value: ["PAC"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: t("joblines.fields.part_types.PAR"),
|
||||||
|
value: ["PAR"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: t("joblines.fields.part_types.PAM"),
|
||||||
|
value: ["PAM"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
onFilter: (value, record) => value.includes(record.part_type),
|
||||||
|
render: (text, record) =>
|
||||||
|
record.part_type
|
||||||
|
? t(`joblines.fields.part_types.${record.part_type}`)
|
||||||
|
: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("joblines.fields.part_qty"),
|
||||||
|
dataIndex: "part_qty",
|
||||||
|
key: "part_qty",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("joblines.fields.act_price"),
|
||||||
|
dataIndex: "act_price",
|
||||||
|
key: "act_price",
|
||||||
|
sorter: (a, b) => a.act_price - b.act_price,
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "act_price" && state.sortedInfo.order,
|
||||||
|
ellipsis: true,
|
||||||
|
render: (text, record) => (
|
||||||
|
<CurrencyFormatter>
|
||||||
|
{record.db_ref === "900510" || record.db_ref === "900511"
|
||||||
|
? record.prt_dsmk_m
|
||||||
|
: record.act_price}
|
||||||
|
</CurrencyFormatter>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("joblines.fields.location"),
|
||||||
|
dataIndex: "location",
|
||||||
|
key: "location",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("joblines.fields.status"),
|
||||||
|
dataIndex: "status",
|
||||||
|
key: "status",
|
||||||
|
sorter: (a, b) => alphaSort(a.status, b.status),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "status" && state.sortedInfo.order,
|
||||||
|
filteredValue: state.filteredInfo.status || null,
|
||||||
|
filters:
|
||||||
|
(jobLines &&
|
||||||
|
jobLines
|
||||||
|
.map((l) => l.status)
|
||||||
|
.filter(onlyUnique)
|
||||||
|
.map((s) => {
|
||||||
|
return {
|
||||||
|
text: s || "No Status*",
|
||||||
|
value: [s],
|
||||||
|
};
|
||||||
|
})) ||
|
||||||
|
[],
|
||||||
|
onFilter: (value, record) => value.includes(record.status),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const handleTableChange = (pagination, filters, sorter) => {
|
||||||
|
setState((state) => ({
|
||||||
|
...state,
|
||||||
|
filteredInfo: filters,
|
||||||
|
sortedInfo: sorter,
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card title={t("jobs.labels.parts_lines")}>
|
||||||
|
<Table
|
||||||
|
columns={columns}
|
||||||
|
rowKey="id"
|
||||||
|
loading={loading}
|
||||||
|
pagination={false}
|
||||||
|
dataSource={jobLines}
|
||||||
|
onChange={handleTableChange}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(PartsQueueJobLinesComponent);
|
||||||
@@ -8,30 +8,28 @@ import { useTranslation } from "react-i18next";
|
|||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { Link, useHistory, useLocation } from "react-router-dom";
|
import { Link, useHistory, useLocation } from "react-router-dom";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import AlertComponent from "../../components/alert/alert.component";
|
|
||||||
import JobPartsQueueCount from "../../components/job-parts-queue-count/job-parts-queue-count.component";
|
|
||||||
import JobRemoveFromPartsQueue from "../../components/job-remove-from-parst-queue/job-remove-from-parts-queue.component";
|
|
||||||
import OwnerNameDisplay from "../../components/owner-name-display/owner-name-display.component";
|
|
||||||
import ProductionListColumnComment from "../../components/production-list-columns/production-list-columns.comment.component";
|
|
||||||
import { QUERY_PARTS_QUEUE } from "../../graphql/jobs.queries";
|
import { QUERY_PARTS_QUEUE } from "../../graphql/jobs.queries";
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
import { DateTimeFormatter, TimeAgoFormatter } from "../../utils/DateFormatter";
|
import { DateTimeFormatter, TimeAgoFormatter } from "../../utils/DateFormatter";
|
||||||
|
import { onlyUnique } from "../../utils/arrayHelper";
|
||||||
|
import { pageLimit } from "../../utils/config";
|
||||||
import { alphaSort, dateSort } from "../../utils/sorters";
|
import { alphaSort, dateSort } from "../../utils/sorters";
|
||||||
import useLocalStorage from "../../utils/useLocalStorage";
|
import useLocalStorage from "../../utils/useLocalStorage";
|
||||||
import {pageLimit} from "../../utils/config";
|
import AlertComponent from "../alert/alert.component";
|
||||||
|
import JobPartsQueueCount from "../job-parts-queue-count/job-parts-queue-count.component";
|
||||||
|
import JobRemoveFromPartsQueue from "../job-remove-from-parst-queue/job-remove-from-parts-queue.component";
|
||||||
|
import OwnerNameDisplay, {
|
||||||
|
OwnerNameDisplayFunction,
|
||||||
|
} from "../owner-name-display/owner-name-display.component";
|
||||||
|
import ProductionListColumnComment from "../production-list-columns/production-list-columns.comment.component";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
});
|
});
|
||||||
|
|
||||||
export function PartsQueuePageComponent({ bodyshop }) {
|
export function PartsQueueListComponent({ bodyshop }) {
|
||||||
const searchParams = queryString.parse(useLocation().search);
|
const searchParams = queryString.parse(useLocation().search);
|
||||||
const {
|
const { selected, sortcolumn, sortorder, statusFilters } = searchParams;
|
||||||
//page,
|
|
||||||
sortcolumn,
|
|
||||||
sortorder,
|
|
||||||
statusFilters,
|
|
||||||
} = searchParams;
|
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const [filter, setFilter] = useLocalStorage("filter_parts_queue", null);
|
const [filter, setFilter] = useLocalStorage("filter_parts_queue", null);
|
||||||
|
|
||||||
@@ -39,19 +37,8 @@ export function PartsQueuePageComponent({ bodyshop }) {
|
|||||||
fetchPolicy: "network-only",
|
fetchPolicy: "network-only",
|
||||||
nextFetchPolicy: "network-only",
|
nextFetchPolicy: "network-only",
|
||||||
variables: {
|
variables: {
|
||||||
// offset: page ? (page - 1) * 25 : 0,
|
|
||||||
// limit: 25,
|
|
||||||
statuses: (statusFilters && JSON.parse(statusFilters)) ||
|
statuses: (statusFilters && JSON.parse(statusFilters)) ||
|
||||||
bodyshop.md_ro_statuses.active_statuses || ["Open", "Open*"],
|
bodyshop.md_ro_statuses.active_statuses || ["Open", "Open*"],
|
||||||
order: [
|
|
||||||
{
|
|
||||||
[sortcolumn || "ro_number"]: sortorder
|
|
||||||
? sortorder === "descend"
|
|
||||||
? "desc"
|
|
||||||
: "asc"
|
|
||||||
: "desc",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -107,6 +94,19 @@ export function PartsQueuePageComponent({ bodyshop }) {
|
|||||||
history.push({ search: queryString.stringify(searchParams) });
|
history.push({ search: queryString.stringify(searchParams) });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleOnRowClick = (record) => {
|
||||||
|
if (record) {
|
||||||
|
if (record.id) {
|
||||||
|
history.push({
|
||||||
|
search: queryString.stringify({
|
||||||
|
...searchParams,
|
||||||
|
selected: record.id,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
title: t("jobs.fields.ro_number"),
|
title: t("jobs.fields.ro_number"),
|
||||||
@@ -125,7 +125,8 @@ export function PartsQueuePageComponent({ bodyshop }) {
|
|||||||
title: t("jobs.fields.owner"),
|
title: t("jobs.fields.owner"),
|
||||||
dataIndex: "ownr_ln",
|
dataIndex: "ownr_ln",
|
||||||
key: "ownr_ln",
|
key: "ownr_ln",
|
||||||
sorter: (a, b) => alphaSort(a.ownr_ln, b.ownr_ln),
|
sorter: (a, b) =>
|
||||||
|
alphaSort(OwnerNameDisplayFunction(a), OwnerNameDisplayFunction(b)),
|
||||||
sortOrder: sortcolumn === "ownr_ln" && sortorder,
|
sortOrder: sortcolumn === "ownr_ln" && sortorder,
|
||||||
render: (text, record) => {
|
render: (text, record) => {
|
||||||
return record.ownerid ? (
|
return record.ownerid ? (
|
||||||
@@ -139,6 +140,56 @@ export function PartsQueuePageComponent({ bodyshop }) {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: t("jobs.fields.vehicle"),
|
||||||
|
dataIndex: "vehicle",
|
||||||
|
key: "vehicle",
|
||||||
|
ellipsis: true,
|
||||||
|
sorter: (a, b) =>
|
||||||
|
alphaSort(
|
||||||
|
`${a.v_model_yr || ""} ${a.v_make_desc || ""} ${
|
||||||
|
a.v_model_desc || ""
|
||||||
|
}`,
|
||||||
|
`${b.v_model_yr || ""} ${b.v_make_desc || ""} ${b.v_model_desc || ""}`
|
||||||
|
),
|
||||||
|
sortOrder: sortcolumn === "vehicle" && sortorder,
|
||||||
|
render: (text, record) => {
|
||||||
|
return record.vehicleid ? (
|
||||||
|
<Link to={"/manage/vehicles/" + record.vehicleid}>
|
||||||
|
{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
|
||||||
|
record.v_model_desc || ""
|
||||||
|
}`}
|
||||||
|
</Link>
|
||||||
|
) : (
|
||||||
|
<span>{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
|
||||||
|
record.v_model_desc || ""
|
||||||
|
}`}</span>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("jobs.fields.ins_co_nm_short"),
|
||||||
|
dataIndex: "ins_co_nm",
|
||||||
|
key: "ins_co_nm",
|
||||||
|
ellipsis: true,
|
||||||
|
sorter: (a, b) => alphaSort(a.ins_co_nm, b.ins_co_nm),
|
||||||
|
sortOrder: sortcolumn === "ins_co_nm" && sortorder,
|
||||||
|
filteredValue: filter?.ins_co_nm || null,
|
||||||
|
filters:
|
||||||
|
(jobs &&
|
||||||
|
jobs
|
||||||
|
.map((j) => j.ins_co_nm)
|
||||||
|
.filter(onlyUnique)
|
||||||
|
.map((s) => {
|
||||||
|
return {
|
||||||
|
text: s || "No Ins. Co.*",
|
||||||
|
value: [s],
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.sort((a, b) => alphaSort(a.text, b.text))) ||
|
||||||
|
[],
|
||||||
|
onFilter: (value, record) => value.includes(record.ins_co_nm),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: t("jobs.fields.status"),
|
title: t("jobs.fields.status"),
|
||||||
dataIndex: "status",
|
dataIndex: "status",
|
||||||
@@ -170,23 +221,16 @@ export function PartsQueuePageComponent({ bodyshop }) {
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("jobs.fields.vehicle"),
|
title: t("jobs.fields.scheduled_completion"),
|
||||||
dataIndex: "vehicle",
|
dataIndex: "scheduled_completion",
|
||||||
key: "vehicle",
|
key: "scheduled_completion",
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
render: (text, record) => {
|
sorter: (a, b) =>
|
||||||
return record.vehicleid ? (
|
dateSort(a.scheduled_completion, b.scheduled_completion),
|
||||||
<Link to={"/manage/vehicles/" + record.vehicleid}>
|
sortOrder: sortcolumn === "scheduled_completion" && sortorder,
|
||||||
{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
|
render: (text, record) => (
|
||||||
record.v_model_desc || ""
|
<DateTimeFormatter>{record.scheduled_completion}</DateTimeFormatter>
|
||||||
}`}
|
),
|
||||||
</Link>
|
|
||||||
) : (
|
|
||||||
<span>{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
|
|
||||||
record.v_model_desc || ""
|
|
||||||
}`}</span>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
// {
|
// {
|
||||||
// title: t("vehicles.fields.plate_no"),
|
// title: t("vehicles.fields.plate_no"),
|
||||||
@@ -198,14 +242,6 @@ export function PartsQueuePageComponent({ bodyshop }) {
|
|||||||
// return record.plate_no ? record.plate_no : "";
|
// return record.plate_no ? record.plate_no : "";
|
||||||
// },
|
// },
|
||||||
// },
|
// },
|
||||||
{
|
|
||||||
title: t("jobs.fields.clm_no"),
|
|
||||||
dataIndex: "clm_no",
|
|
||||||
key: "clm_no",
|
|
||||||
ellipsis: true,
|
|
||||||
sorter: (a, b) => alphaSort(a.clm_no, b.clm_no),
|
|
||||||
sortOrder: sortcolumn === "clm_no" && sortorder,
|
|
||||||
},
|
|
||||||
// {
|
// {
|
||||||
// title: t("jobs.fields.clm_total"),
|
// title: t("jobs.fields.clm_total"),
|
||||||
// dataIndex: "clm_total",
|
// dataIndex: "clm_total",
|
||||||
@@ -307,9 +343,16 @@ export function PartsQueuePageComponent({ bodyshop }) {
|
|||||||
style={{ height: "100%" }}
|
style={{ height: "100%" }}
|
||||||
scroll={{ x: true }}
|
scroll={{ x: true }}
|
||||||
onChange={handleTableChange}
|
onChange={handleTableChange}
|
||||||
|
rowSelection={{
|
||||||
|
onSelect: (record) => {
|
||||||
|
handleOnRowClick(record);
|
||||||
|
},
|
||||||
|
selectedRowKeys: [selected],
|
||||||
|
type: "radio",
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect(mapStateToProps, null)(PartsQueuePageComponent);
|
export default connect(mapStateToProps, null)(PartsQueueListComponent);
|
||||||
@@ -115,7 +115,7 @@ export function ProductionBoardKanbanComponent({
|
|||||||
// console.log("==> New Card is somewhere in the middle");
|
// console.log("==> New Card is somewhere in the middle");
|
||||||
movedCardNewKanbanParent = newChildCard.kanbanparent;
|
movedCardNewKanbanParent = newChildCard.kanbanparent;
|
||||||
} else {
|
} else {
|
||||||
throw new Error("==> !!!!!!Couldn't find a parent.!!!! <==");
|
console.log("==> !!!!!!Couldn't find a parent.!!!! <==");
|
||||||
}
|
}
|
||||||
const newChildCardNewParent = newChildCard ? card.id : null;
|
const newChildCardNewParent = newChildCard ? card.id : null;
|
||||||
const update = await client.mutate({
|
const update = await client.mutate({
|
||||||
@@ -153,7 +153,7 @@ export function ProductionBoardKanbanComponent({
|
|||||||
0
|
0
|
||||||
)
|
)
|
||||||
.toFixed(1);
|
.toFixed(1);
|
||||||
const totalLAB = data
|
const totalLAB = data
|
||||||
.reduce(
|
.reduce(
|
||||||
(acc, val) => acc + (val.labhrs?.aggregate?.sum?.mod_lb_hrs || 0),
|
(acc, val) => acc + (val.labhrs?.aggregate?.sum?.mod_lb_hrs || 0),
|
||||||
0
|
0
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React from "react";
|
|
||||||
import { Button, Dropdown, Menu } from "antd";
|
import { Button, Dropdown, Menu } from "antd";
|
||||||
import dataSource from "./production-list-columns.data";
|
import React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import dataSource from "./production-list-columns.data";
|
||||||
|
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
@@ -24,6 +24,7 @@ export function ProductionColumnsComponent({
|
|||||||
columnState,
|
columnState,
|
||||||
technician,
|
technician,
|
||||||
bodyshop,
|
bodyshop,
|
||||||
|
data,
|
||||||
tableState,
|
tableState,
|
||||||
}) {
|
}) {
|
||||||
const [columns, setColumns] = columnState;
|
const [columns, setColumns] = columnState;
|
||||||
@@ -36,6 +37,7 @@ export function ProductionColumnsComponent({
|
|||||||
bodyshop,
|
bodyshop,
|
||||||
technician,
|
technician,
|
||||||
state: tableState,
|
state: tableState,
|
||||||
|
data: data,
|
||||||
activeStatuses: bodyshop.md_ro_statuses.active_statuses,
|
activeStatuses: bodyshop.md_ro_statuses.active_statuses,
|
||||||
}).filter((i) => i.key === e.key),
|
}).filter((i) => i.key === e.key),
|
||||||
]);
|
]);
|
||||||
@@ -46,6 +48,7 @@ export function ProductionColumnsComponent({
|
|||||||
technician,
|
technician,
|
||||||
state: tableState,
|
state: tableState,
|
||||||
activeStatuses: bodyshop.md_ro_statuses.active_statuses,
|
activeStatuses: bodyshop.md_ro_statuses.active_statuses,
|
||||||
|
data: data,
|
||||||
});
|
});
|
||||||
const menu = (
|
const menu = (
|
||||||
<Menu
|
<Menu
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { PauseCircleOutlined, BranchesOutlined } from "@ant-design/icons";
|
import { BranchesOutlined, PauseCircleOutlined } from "@ant-design/icons";
|
||||||
import { Space, Tooltip } from "antd";
|
import { Space, Tooltip } from "antd";
|
||||||
import i18n from "i18next";
|
import i18n from "i18next";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
@@ -6,6 +6,7 @@ import { Link } from "react-router-dom";
|
|||||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||||
import { TimeFormatter } from "../../utils/DateFormatter";
|
import { TimeFormatter } from "../../utils/DateFormatter";
|
||||||
import PhoneFormatter from "../../utils/PhoneFormatter";
|
import PhoneFormatter from "../../utils/PhoneFormatter";
|
||||||
|
import { onlyUnique } from "../../utils/arrayHelper";
|
||||||
import { alphaSort, dateSort, statusSort } from "../../utils/sorters";
|
import { alphaSort, dateSort, statusSort } from "../../utils/sorters";
|
||||||
import JobAltTransportChange from "../job-at-change/job-at-change.component";
|
import JobAltTransportChange from "../job-at-change/job-at-change.component";
|
||||||
import JobPartsQueueCount from "../job-parts-queue-count/job-parts-queue-count.component";
|
import JobPartsQueueCount from "../job-parts-queue-count/job-parts-queue-count.component";
|
||||||
@@ -25,7 +26,7 @@ import ProductionListColumnCategory from "./production-list-columns.status.categ
|
|||||||
import ProductionListColumnStatus from "./production-list-columns.status.component";
|
import ProductionListColumnStatus from "./production-list-columns.status.component";
|
||||||
import ProductionlistColumnTouchTime from "./prodution-list-columns.touchtime.component";
|
import ProductionlistColumnTouchTime from "./prodution-list-columns.touchtime.component";
|
||||||
|
|
||||||
const r = ({ technician, state, activeStatuses, bodyshop }) => {
|
const r = ({ technician, state, activeStatuses, data, bodyshop }) => {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
title: i18n.t("jobs.actions.viewdetail"),
|
title: i18n.t("jobs.actions.viewdetail"),
|
||||||
@@ -75,7 +76,14 @@ const r = ({ technician, state, activeStatuses, bodyshop }) => {
|
|||||||
dataIndex: "ownr",
|
dataIndex: "ownr",
|
||||||
key: "ownr",
|
key: "ownr",
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
render: (text, record) => <OwnerNameDisplay ownerObject={record} />,
|
render: (text, record) =>
|
||||||
|
technician ? (
|
||||||
|
<OwnerNameDisplay ownerObject={record} />
|
||||||
|
) : (
|
||||||
|
<Link to={`/manage/owners/${record.ownerid}`}>
|
||||||
|
<OwnerNameDisplay ownerObject={record} />
|
||||||
|
</Link>
|
||||||
|
),
|
||||||
sorter: (a, b) => alphaSort(a.ownr_ln, b.ownr_ln),
|
sorter: (a, b) => alphaSort(a.ownr_ln, b.ownr_ln),
|
||||||
sortOrder:
|
sortOrder:
|
||||||
state.sortedInfo.columnKey === "ownr" && state.sortedInfo.order,
|
state.sortedInfo.columnKey === "ownr" && state.sortedInfo.order,
|
||||||
@@ -92,13 +100,18 @@ const r = ({ technician, state, activeStatuses, bodyshop }) => {
|
|||||||
),
|
),
|
||||||
sortOrder:
|
sortOrder:
|
||||||
state.sortedInfo.columnKey === "vehicle" && state.sortedInfo.order,
|
state.sortedInfo.columnKey === "vehicle" && state.sortedInfo.order,
|
||||||
render: (text, record) => (
|
render: (text, record) =>
|
||||||
<Link to={`/manage/vehicles/${record.vehicleid}`}>{`${
|
technician ? (
|
||||||
record.v_model_yr || ""
|
<>{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
|
||||||
} ${record.v_make_desc || ""} ${record.v_model_desc || ""} ${
|
record.v_model_desc || ""
|
||||||
record.v_color || ""
|
} ${record.v_color || ""} ${record.plate_no || ""}`}</>
|
||||||
} ${record.plate_no || ""}`}</Link>
|
) : (
|
||||||
),
|
<Link to={`/manage/vehicles/${record.vehicleid}`}>{`${
|
||||||
|
record.v_model_yr || ""
|
||||||
|
} ${record.v_make_desc || ""} ${record.v_model_desc || ""} ${
|
||||||
|
record.v_color || ""
|
||||||
|
} ${record.plate_no || ""}`}</Link>
|
||||||
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: i18n.t("jobs.fields.actual_in"),
|
title: i18n.t("jobs.fields.actual_in"),
|
||||||
@@ -536,6 +549,36 @@ const r = ({ technician, state, activeStatuses, bodyshop }) => {
|
|||||||
<JobPartsQueueCount parts={record.joblines_status} record={record} />
|
<JobPartsQueueCount parts={record.joblines_status} record={record} />
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: i18n.t("jobs.labels.estimator"),
|
||||||
|
dataIndex: "estimator",
|
||||||
|
key: "estimator",
|
||||||
|
sorter: (a, b) =>
|
||||||
|
alphaSort(
|
||||||
|
`${a.est_ct_fn || ""} ${a.est_ct_ln || ""}`.trim(),
|
||||||
|
`${b.est_ct_fn || ""} ${b.est_ct_ln || ""}`.trim()
|
||||||
|
),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "estimator" && state.sortedInfo.order,
|
||||||
|
filters:
|
||||||
|
(data &&
|
||||||
|
data
|
||||||
|
.map((j) => `${j.est_ct_fn || ""} ${j.est_ct_ln || ""}`.trim())
|
||||||
|
.filter(onlyUnique)
|
||||||
|
.map((s) => {
|
||||||
|
return {
|
||||||
|
text: s || "N/A",
|
||||||
|
value: [s],
|
||||||
|
};
|
||||||
|
})) ||
|
||||||
|
[],
|
||||||
|
onFilter: (value, record) =>
|
||||||
|
value.includes(
|
||||||
|
`${record.est_ct_fn || ""} ${record.est_ct_ln || ""}`.trim()
|
||||||
|
),
|
||||||
|
render: (text, record) =>
|
||||||
|
`${record.est_ct_fn || ""} ${record.est_ct_ln || ""}`.trim(),
|
||||||
|
},
|
||||||
|
|
||||||
//Added as a place holder for St Claude. Not implemented as it requires another join for a field used by only 1 client.
|
//Added as a place holder for St Claude. Not implemented as it requires another join for a field used by only 1 client.
|
||||||
// {
|
// {
|
||||||
|
|||||||
@@ -13,12 +13,14 @@ import { selectTechnician } from "../../redux/tech/tech.selectors";
|
|||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||||
import { DateFormatter } from "../../utils/DateFormatter";
|
import { DateFormatter } from "../../utils/DateFormatter";
|
||||||
|
import PhoneNumberFormatter from "../../utils/PhoneFormatter";
|
||||||
import AlertComponent from "../alert/alert.component";
|
import AlertComponent from "../alert/alert.component";
|
||||||
import StartChatButton from "../chat-open-button/chat-open-button.component";
|
import StartChatButton from "../chat-open-button/chat-open-button.component";
|
||||||
import JobAtChange from "../job-at-change/job-at-change.component";
|
import JobAtChange from "../job-at-change/job-at-change.component";
|
||||||
import JobDetailCardsDocumentsComponent from "../job-detail-cards/job-detail-cards.documents.component";
|
import JobDetailCardsDocumentsComponent from "../job-detail-cards/job-detail-cards.documents.component";
|
||||||
import JobDetailCardsNotesComponent from "../job-detail-cards/job-detail-cards.notes.component";
|
import JobDetailCardsNotesComponent from "../job-detail-cards/job-detail-cards.notes.component";
|
||||||
import JobDetailCardsPartsComponent from "../job-detail-cards/job-detail-cards.parts.component";
|
import JobDetailCardsPartsComponent from "../job-detail-cards/job-detail-cards.parts.component";
|
||||||
|
import CardTemplate from "../job-detail-cards/job-detail-cards.template.component";
|
||||||
import JobEmployeeAssignments from "../job-employee-assignments/job-employee-assignments.container";
|
import JobEmployeeAssignments from "../job-employee-assignments/job-employee-assignments.container";
|
||||||
import ScoreboardAddButton from "../job-scoreboard-add-button/job-scoreboard-add-button.component";
|
import ScoreboardAddButton from "../job-scoreboard-add-button/job-scoreboard-add-button.component";
|
||||||
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
|
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
|
||||||
@@ -103,7 +105,13 @@ export function ProductionListDetail({
|
|||||||
{error && <AlertComponent error={JSON.stringify(error)} />}
|
{error && <AlertComponent error={JSON.stringify(error)} />}
|
||||||
{!loading && data && (
|
{!loading && data && (
|
||||||
<div>
|
<div>
|
||||||
<JobEmployeeAssignments job={data.jobs_by_pk} refetch={refetch} />
|
<CardTemplate
|
||||||
|
title={t("jobs.labels.employeeassignments")}
|
||||||
|
loading={loading}
|
||||||
|
>
|
||||||
|
<JobEmployeeAssignments job={data.jobs_by_pk} refetch={refetch} />
|
||||||
|
</CardTemplate>
|
||||||
|
<div style={{ height: "8px" }} />
|
||||||
<Descriptions bordered column={1}>
|
<Descriptions bordered column={1}>
|
||||||
<Descriptions.Item label={t("jobs.fields.ro_number")}>
|
<Descriptions.Item label={t("jobs.fields.ro_number")}>
|
||||||
{theJob.ro_number || ""}
|
{theJob.ro_number || ""}
|
||||||
@@ -111,7 +119,7 @@ export function ProductionListDetail({
|
|||||||
<Descriptions.Item label={t("jobs.fields.alt_transport")}>
|
<Descriptions.Item label={t("jobs.fields.alt_transport")}>
|
||||||
<Space>
|
<Space>
|
||||||
{data.jobs_by_pk.alt_transport || ""}
|
{data.jobs_by_pk.alt_transport || ""}
|
||||||
<JobAtChange event={{ job: data.jobs_by_pk }} />
|
<JobAtChange job={data.jobs_by_pk} />
|
||||||
</Space>
|
</Space>
|
||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
<Descriptions.Item label={t("jobs.fields.clm_no")}>
|
<Descriptions.Item label={t("jobs.fields.clm_no")}>
|
||||||
@@ -121,15 +129,30 @@ export function ProductionListDetail({
|
|||||||
{theJob.ins_co_nm || ""}
|
{theJob.ins_co_nm || ""}
|
||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
<Descriptions.Item label={t("jobs.fields.owner")}>
|
<Descriptions.Item label={t("jobs.fields.owner")}>
|
||||||
<OwnerNameDisplay ownerObject={theJob} />
|
<Space>
|
||||||
<StartChatButton
|
<OwnerNameDisplay ownerObject={theJob} />
|
||||||
phone={data.jobs_by_pk.ownr_ph1}
|
{!technician ? (
|
||||||
jobid={data.jobs_by_pk.id}
|
<>
|
||||||
/>
|
<StartChatButton
|
||||||
<StartChatButton
|
phone={data.jobs_by_pk.ownr_ph1}
|
||||||
phone={data.jobs_by_pk.ownr_ph2}
|
jobid={data.jobs_by_pk.id}
|
||||||
jobid={data.jobs_by_pk.id}
|
/>
|
||||||
/>
|
<StartChatButton
|
||||||
|
phone={data.jobs_by_pk.ownr_ph2}
|
||||||
|
jobid={data.jobs_by_pk.id}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<PhoneNumberFormatter>
|
||||||
|
{data.jobs_by_pk.ownr_ph1}
|
||||||
|
</PhoneNumberFormatter>
|
||||||
|
<PhoneNumberFormatter>
|
||||||
|
{data.jobs_by_pk.ownr_ph2}
|
||||||
|
</PhoneNumberFormatter>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Space>
|
||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
<Descriptions.Item label={t("jobs.fields.vehicle")}>
|
<Descriptions.Item label={t("jobs.fields.vehicle")}>
|
||||||
{`${theJob.v_model_yr || ""} ${theJob.v_color || ""} ${
|
{`${theJob.v_model_yr || ""} ${theJob.v_color || ""} ${
|
||||||
@@ -146,21 +169,24 @@ export function ProductionListDetail({
|
|||||||
<DateFormatter>{theJob.scheduled_completion}</DateFormatter>
|
<DateFormatter>{theJob.scheduled_completion}</DateFormatter>
|
||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
</Descriptions>
|
</Descriptions>
|
||||||
|
<div style={{ height: "8px" }} />
|
||||||
<JobDetailCardsPartsComponent
|
<JobDetailCardsPartsComponent
|
||||||
loading={loading}
|
loading={loading}
|
||||||
data={data ? data.jobs_by_pk : null}
|
data={data ? data.jobs_by_pk : null}
|
||||||
/>
|
/>
|
||||||
|
<div style={{ height: "8px" }} />
|
||||||
<JobDetailCardsNotesComponent
|
<JobDetailCardsNotesComponent
|
||||||
loading={loading}
|
loading={loading}
|
||||||
data={data ? data.jobs_by_pk : null}
|
data={data ? data.jobs_by_pk : null}
|
||||||
/>
|
/>
|
||||||
{!bodyshop.uselocalmediaserver && (
|
{!bodyshop.uselocalmediaserver && (
|
||||||
<JobDetailCardsDocumentsComponent
|
<>
|
||||||
loading={loading}
|
<div style={{ height: "8px" }} />
|
||||||
data={data ? data.jobs_by_pk : null}
|
<JobDetailCardsDocumentsComponent
|
||||||
/>
|
loading={loading}
|
||||||
|
data={data ? data.jobs_by_pk : null}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ export function ProductionListTable({
|
|||||||
technician,
|
technician,
|
||||||
currentUser,
|
currentUser,
|
||||||
state,
|
state,
|
||||||
|
data,
|
||||||
setColumns,
|
setColumns,
|
||||||
setState,
|
setState,
|
||||||
}) {
|
}) {
|
||||||
@@ -41,6 +42,7 @@ export function ProductionListTable({
|
|||||||
bodyshop,
|
bodyshop,
|
||||||
technician,
|
technician,
|
||||||
state,
|
state,
|
||||||
|
data: data,
|
||||||
activeStatuses: bodyshop.md_ro_statuses.active_statuses,
|
activeStatuses: bodyshop.md_ro_statuses.active_statuses,
|
||||||
}).find((e) => e.key === k.key),
|
}).find((e) => e.key === k.key),
|
||||||
width: k.width,
|
width: k.width,
|
||||||
@@ -95,6 +97,7 @@ export function ProductionListTable({
|
|||||||
...ProductionListColumns({
|
...ProductionListColumns({
|
||||||
technician,
|
technician,
|
||||||
state,
|
state,
|
||||||
|
data: data,
|
||||||
activeStatuses: bodyshop.md_ro_statuses.active_statuses,
|
activeStatuses: bodyshop.md_ro_statuses.active_statuses,
|
||||||
}).find((e) => e.key === k.key),
|
}).find((e) => e.key === k.key),
|
||||||
width: k.width,
|
width: k.width,
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import {
|
|||||||
Statistic,
|
Statistic,
|
||||||
Table,
|
Table,
|
||||||
} from "antd";
|
} from "antd";
|
||||||
import React, { useMemo, useState } from "react";
|
import React, { useEffect, useMemo, useState } from "react";
|
||||||
import ReactDragListView from "react-drag-listview";
|
import ReactDragListView from "react-drag-listview";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
@@ -79,6 +79,7 @@ export function ProductionListTable({
|
|||||||
bodyshop,
|
bodyshop,
|
||||||
technician,
|
technician,
|
||||||
state,
|
state,
|
||||||
|
data: data,
|
||||||
activeStatuses: bodyshop.md_ro_statuses.active_statuses,
|
activeStatuses: bodyshop.md_ro_statuses.active_statuses,
|
||||||
}).find((e) => e.key === k.key),
|
}).find((e) => e.key === k.key),
|
||||||
width: k.width ?? 100,
|
width: k.width ?? 100,
|
||||||
@@ -87,6 +88,33 @@ export function ProductionListTable({
|
|||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const newColumns =
|
||||||
|
(state &&
|
||||||
|
matchingColumnConfig &&
|
||||||
|
matchingColumnConfig.columns.columnKeys.map((k) => {
|
||||||
|
return {
|
||||||
|
...ProductionListColumns({
|
||||||
|
bodyshop,
|
||||||
|
technician,
|
||||||
|
state,
|
||||||
|
data: data,
|
||||||
|
activeStatuses: bodyshop.md_ro_statuses.active_statuses,
|
||||||
|
}).find((e) => e.key === k.key),
|
||||||
|
width: k.width ?? 100,
|
||||||
|
};
|
||||||
|
})) ||
|
||||||
|
[];
|
||||||
|
setColumns(newColumns);
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [
|
||||||
|
//state,
|
||||||
|
matchingColumnConfig,
|
||||||
|
bodyshop,
|
||||||
|
technician,
|
||||||
|
data,
|
||||||
|
]); //State removed from dependency array as it causes race condition when removing columns from table view and is not needed.
|
||||||
|
|
||||||
const handleTableChange = (pagination, filters, sorter) => {
|
const handleTableChange = (pagination, filters, sorter) => {
|
||||||
setState({
|
setState({
|
||||||
...state,
|
...state,
|
||||||
@@ -104,7 +132,8 @@ export function ProductionListTable({
|
|||||||
|
|
||||||
const removeColumn = (e) => {
|
const removeColumn = (e) => {
|
||||||
const { key } = e;
|
const { key } = e;
|
||||||
setColumns(columns.filter((i) => i.key !== key));
|
const newColumns = columns.filter((i) => i.key !== key);
|
||||||
|
setColumns(newColumns);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleResize =
|
const handleResize =
|
||||||
@@ -227,6 +256,7 @@ export function ProductionListTable({
|
|||||||
<ProductionListColumnsAdd
|
<ProductionListColumnsAdd
|
||||||
columnState={[columns, setColumns]}
|
columnState={[columns, setColumns]}
|
||||||
tableState={state}
|
tableState={state}
|
||||||
|
data={data}
|
||||||
/>
|
/>
|
||||||
<ProductionListSaveConfigButton
|
<ProductionListSaveConfigButton
|
||||||
columns={columns}
|
columns={columns}
|
||||||
@@ -237,6 +267,7 @@ export function ProductionListTable({
|
|||||||
state={state}
|
state={state}
|
||||||
setState={setState}
|
setState={setState}
|
||||||
setColumns={setColumns}
|
setColumns={setColumns}
|
||||||
|
data={data}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Input
|
<Input
|
||||||
|
|||||||
@@ -55,10 +55,11 @@ const ret = {
|
|||||||
"shiftclock:view": 2,
|
"shiftclock:view": 2,
|
||||||
|
|
||||||
"shop:config": 4,
|
"shop:config": 4,
|
||||||
"shop:rbac": 5,
|
|
||||||
"shop:vendors": 2,
|
|
||||||
"shop:dashboard": 3,
|
"shop:dashboard": 3,
|
||||||
|
"shop:rbac": 5,
|
||||||
|
"shop:reportcenter": 2,
|
||||||
"shop:templates": 4,
|
"shop:templates": 4,
|
||||||
|
"shop:vendors": 2,
|
||||||
|
|
||||||
"temporarydocs:view": 2,
|
"temporarydocs:view": 2,
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,369 @@
|
|||||||
|
import {Button, Card, Checkbox, Col, Form, Input, InputNumber, Row, Select} from "antd";
|
||||||
|
import React, {useCallback, useEffect, useMemo, useState} from "react";
|
||||||
|
import {fetchFilterData} from "../../utils/RenderTemplate";
|
||||||
|
import {DeleteFilled} from "@ant-design/icons";
|
||||||
|
import {useTranslation} from "react-i18next";
|
||||||
|
import {getOrderOperatorsByType, getWhereOperatorsByType} from "../../utils/graphQLmodifier";
|
||||||
|
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
|
||||||
|
import {generateInternalReflections} from "./report-center-modal-utils";
|
||||||
|
|
||||||
|
|
||||||
|
export default function ReportCenterModalFiltersSortersComponent({form, bodyshop}) {
|
||||||
|
return (
|
||||||
|
<Form.Item style={{margin: 0, padding: 0}} dependencies={["key"]}>
|
||||||
|
{() => {
|
||||||
|
const key = form.getFieldValue("key");
|
||||||
|
return <RenderFilters form={form} templateId={key} bodyshop={bodyshop}/>;
|
||||||
|
}}
|
||||||
|
</Form.Item>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filters Section
|
||||||
|
* @param filters
|
||||||
|
* @param form
|
||||||
|
* @param bodyshop
|
||||||
|
* @returns {JSX.Element}
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
function FiltersSection({filters, form, bodyshop}) {
|
||||||
|
const {t} = useTranslation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card type='inner' title={t('reportcenter.labels.advanced_filters_filters')} style={{marginTop: '10px'}}>
|
||||||
|
<Form.List name={["filters"]}>
|
||||||
|
{(fields, {add, remove, move}) => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{fields.map((field, index) => (
|
||||||
|
<Form.Item key={field.key}>
|
||||||
|
<Row gutter={[16, 16]}>
|
||||||
|
<Col span={10}>
|
||||||
|
<Form.Item
|
||||||
|
key={`${index}field`}
|
||||||
|
label={t('reportcenter.labels.advanced_filters_filter_field')}
|
||||||
|
name={[field.name, "field"]}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
getPopupContainer={trigger => trigger.parentNode}
|
||||||
|
onChange={() => {
|
||||||
|
// Clear related Fields
|
||||||
|
form.setFieldValue(['filters', field.name, 'value'], null);
|
||||||
|
form.setFieldValue(['filters', field.name, 'operator'], null);
|
||||||
|
}}
|
||||||
|
options={
|
||||||
|
filters.map((f) => {
|
||||||
|
return {
|
||||||
|
value: f.name,
|
||||||
|
label: f?.translation ? (t(f.translation) === f.translation ? f.label : t(f.translation)) : f.label,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
<Col span={6}>
|
||||||
|
<Form.Item dependencies={[['filters', field.name, "field"]]}>
|
||||||
|
{
|
||||||
|
() => {
|
||||||
|
const name = form.getFieldValue(['filters', field.name, "field"]);
|
||||||
|
const type = filters.find(f => f.name === name)?.type;
|
||||||
|
|
||||||
|
return <Form.Item
|
||||||
|
key={`${index}operator`}
|
||||||
|
label={t('reportcenter.labels.advanced_filters_filter_operator')}
|
||||||
|
name={[field.name, "operator"]}
|
||||||
|
dependencies={[]}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
getPopupContainer={trigger => trigger.parentNode}
|
||||||
|
options={getWhereOperatorsByType(type)}/>
|
||||||
|
</Form.Item>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
<Col span={6}>
|
||||||
|
<Form.Item dependencies={[['filters', field.name, "field"]]}>
|
||||||
|
{
|
||||||
|
() => {
|
||||||
|
const name = form.getFieldValue(['filters', field.name, "field"]);
|
||||||
|
const type = filters.find(f => f.name === name)?.type;
|
||||||
|
const reflector = filters.find(f => f.name === name)?.reflector;
|
||||||
|
|
||||||
|
return <Form.Item
|
||||||
|
key={`${index}value`}
|
||||||
|
label={t('reportcenter.labels.advanced_filters_filter_value')}
|
||||||
|
name={[field.name, "value"]}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
{
|
||||||
|
(() => {
|
||||||
|
const generateReflections = (reflector) => {
|
||||||
|
if (!reflector) return [];
|
||||||
|
|
||||||
|
const {name} = reflector;
|
||||||
|
const path = name?.split('.');
|
||||||
|
const upperPath = path?.[0];
|
||||||
|
const finalPath = path?.slice(1).join('.');
|
||||||
|
|
||||||
|
return generateInternalReflections({
|
||||||
|
bodyshop,
|
||||||
|
upperPath,
|
||||||
|
finalPath
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const reflections = reflector ? generateReflections(reflector) : [];
|
||||||
|
const fieldPath = [[field.name, "value"]];
|
||||||
|
|
||||||
|
if (reflections.length > 0) {
|
||||||
|
return (
|
||||||
|
<Select
|
||||||
|
options={reflections}
|
||||||
|
getPopupContainer={trigger => trigger.parentNode}
|
||||||
|
onChange={(value) => {
|
||||||
|
form.setFieldValue(fieldPath, value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === "number") {
|
||||||
|
return (
|
||||||
|
<InputNumber
|
||||||
|
onChange={(value) => form.setFieldValue(fieldPath, value)}/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Input
|
||||||
|
onChange={(e) => form.setFieldValue(fieldPath, e.target.value)}/>
|
||||||
|
);
|
||||||
|
})()
|
||||||
|
}
|
||||||
|
</Form.Item>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
</Col>
|
||||||
|
<Col span={2}>
|
||||||
|
<DeleteFilled
|
||||||
|
style={{margin: "1rem", paddingTop: '23px'}}
|
||||||
|
onClick={() => {
|
||||||
|
remove(field.name);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</Form.Item>
|
||||||
|
))}
|
||||||
|
<Form.Item>
|
||||||
|
<Button
|
||||||
|
type="dashed"
|
||||||
|
onClick={() => {
|
||||||
|
add();
|
||||||
|
}}
|
||||||
|
style={{width: "100%"}}
|
||||||
|
>
|
||||||
|
{t("general.actions.add")}
|
||||||
|
</Button>
|
||||||
|
</Form.Item>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</Form.List>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sorters Section
|
||||||
|
* @param sorters
|
||||||
|
* @param form
|
||||||
|
* @returns {JSX.Element}
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
function SortersSection({sorters, form}) {
|
||||||
|
const {t} = useTranslation();
|
||||||
|
return (
|
||||||
|
<Card type='inner' title={t('reportcenter.labels.advanced_filters_sorters')} style={{marginTop: '10px'}}>
|
||||||
|
<Form.List name={["sorters"]}>
|
||||||
|
{(fields, {add, remove, move}) => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
Sorters
|
||||||
|
{fields.map((field, index) => (
|
||||||
|
<Form.Item key={field.key}>
|
||||||
|
<Row gutter={[16, 16]}>
|
||||||
|
<Col span={11}>
|
||||||
|
<Form.Item
|
||||||
|
key={`${index}field`}
|
||||||
|
label={t('reportcenter.labels.advanced_filters_sorter_field')}
|
||||||
|
name={[field.name, "field"]}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
options={
|
||||||
|
sorters.map((f) => ({
|
||||||
|
value: f.name,
|
||||||
|
label: f?.translation ? (t(f.translation) === f.translation ? f.label : t(f.translation)) : f.label,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
getPopupContainer={trigger => trigger.parentNode}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
<Col span={11}>
|
||||||
|
<Form.Item
|
||||||
|
key={`${index}direction`}
|
||||||
|
label={t('reportcenter.labels.advanced_filters_sorter_direction')}
|
||||||
|
name={[field.name, "direction"]}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
options={getOrderOperatorsByType()}
|
||||||
|
getPopupContainer={trigger => trigger.parentNode}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
|
||||||
|
<Col span={2}>
|
||||||
|
<DeleteFilled
|
||||||
|
style={{margin: "1rem", paddingTop: '23px'}}
|
||||||
|
onClick={() => {
|
||||||
|
remove(field.name);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</Form.Item>
|
||||||
|
))}
|
||||||
|
<Form.Item>
|
||||||
|
<Button
|
||||||
|
type="dashed"
|
||||||
|
onClick={() => {
|
||||||
|
add();
|
||||||
|
}}
|
||||||
|
style={{width: "100%"}}
|
||||||
|
>
|
||||||
|
{t("general.actions.add")}
|
||||||
|
</Button>
|
||||||
|
</Form.Item>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</Form.List>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render Filters
|
||||||
|
* @param templateId
|
||||||
|
* @param form
|
||||||
|
* @param bodyshop
|
||||||
|
* @returns {JSX.Element|null}
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
function RenderFilters({templateId, form, bodyshop}) {
|
||||||
|
const [state, setState] = useState(null);
|
||||||
|
const [visible, setVisible] = useState(false);
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
const {t} = useTranslation();
|
||||||
|
|
||||||
|
const fetch = useCallback(async () => {
|
||||||
|
// Reset all the filters and Sorters.
|
||||||
|
form.resetFields(['filters']);
|
||||||
|
form.resetFields(['sorters']);
|
||||||
|
form.resetFields(['defaultSorters']);
|
||||||
|
|
||||||
|
setIsLoading(true);
|
||||||
|
|
||||||
|
const data = await fetchFilterData({name: templateId});
|
||||||
|
|
||||||
|
// We have Success
|
||||||
|
if (data?.success) {
|
||||||
|
if (data?.data?.sorters && data?.data?.sorters.length > 0) {
|
||||||
|
const defaultSorters = data?.data?.sorters.filter((sorter) => sorter.hasOwnProperty('default')).map((sorter) => {
|
||||||
|
return {
|
||||||
|
field: sorter.name,
|
||||||
|
direction: sorter.default.direction
|
||||||
|
};
|
||||||
|
}).sort((a, b) => a.default.order - b.default.order);
|
||||||
|
|
||||||
|
form.setFieldValue('defaultSorters', JSON.stringify(defaultSorters));
|
||||||
|
}
|
||||||
|
// Set the state
|
||||||
|
setState(data.data);
|
||||||
|
}
|
||||||
|
// Something went wrong fetching filter data
|
||||||
|
else {
|
||||||
|
setState(null);
|
||||||
|
}
|
||||||
|
setIsLoading(false);
|
||||||
|
}, [templateId, form]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (templateId) {
|
||||||
|
fetch();
|
||||||
|
|
||||||
|
}
|
||||||
|
}, [templateId, fetch]);
|
||||||
|
|
||||||
|
const filters = useMemo(() => state?.filters || [], [state]);
|
||||||
|
const sorters = useMemo(() => state?.sorters || [], [state]);
|
||||||
|
|
||||||
|
if (!templateId) return null;
|
||||||
|
if (isLoading) return <LoadingSkeleton/>;
|
||||||
|
if (!state) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{marginTop: '10px'}}>
|
||||||
|
<Checkbox
|
||||||
|
checked={visible}
|
||||||
|
onChange={(e) => setVisible(e.target.checked)}
|
||||||
|
children={t('reportcenter.labels.advanced_filters')}
|
||||||
|
/>
|
||||||
|
{visible && (
|
||||||
|
<div>
|
||||||
|
{filters.length > 0 && (
|
||||||
|
<FiltersSection filters={filters} form={form} bodyshop={bodyshop}/>
|
||||||
|
)}
|
||||||
|
{sorters.length > 0 && (
|
||||||
|
<SortersSection sorters={sorters} form={form}/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
import {uniqBy} from "lodash";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get value from path
|
||||||
|
* @param obj
|
||||||
|
* @param path
|
||||||
|
* @returns {*}
|
||||||
|
*/
|
||||||
|
const getValueFromPath = (obj, path) => path.split('.').reduce((prev, curr) => prev?.[curr], obj);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Valid internal reflections
|
||||||
|
* Note: This is intended for future functionality
|
||||||
|
* @type {{special: string[], bodyshop: [{name: string, type: string}]}}
|
||||||
|
*/
|
||||||
|
const VALID_INTERNAL_REFLECTIONS = {
|
||||||
|
bodyshop: [
|
||||||
|
{
|
||||||
|
name: 'md_ro_statuses.statuses',
|
||||||
|
type: 'kv-to-v'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate options
|
||||||
|
* @param bodyshop
|
||||||
|
* @param path
|
||||||
|
* @param labelPath
|
||||||
|
* @param valuePath
|
||||||
|
* @returns {{label: *, value: *}[]}
|
||||||
|
*/
|
||||||
|
const generateOptionsFromObject = (bodyshop, path, labelPath, valuePath) => {
|
||||||
|
const options = getValueFromPath(bodyshop, path);
|
||||||
|
return uniqBy(Object.values(options).map((value) => ({
|
||||||
|
label: value[labelPath],
|
||||||
|
value: value[valuePath],
|
||||||
|
})), 'value');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate special reflections
|
||||||
|
* @param bodyshop
|
||||||
|
* @param finalPath
|
||||||
|
* @returns {{label: *, value: *}[]|{label: *, value: *}[]|{label: string, value: *}[]|*[]}
|
||||||
|
*/
|
||||||
|
const generateSpecialReflections = (bodyshop, finalPath) => {
|
||||||
|
switch (finalPath) {
|
||||||
|
case 'cost_centers':
|
||||||
|
return generateOptionsFromObject(bodyshop, 'md_responsibility_centers.costs', 'name', 'name');
|
||||||
|
// Special case because Categories is an Array, not an Object.
|
||||||
|
case 'categories':
|
||||||
|
const catOptions = getValueFromPath(bodyshop, 'md_categories');
|
||||||
|
return uniqBy(catOptions.map((value) => ({
|
||||||
|
label: value,
|
||||||
|
value: value,
|
||||||
|
})), 'value');
|
||||||
|
case 'insurance_companies':
|
||||||
|
return generateOptionsFromObject(bodyshop, 'md_ins_cos', 'name', 'name');
|
||||||
|
case 'employee_teams':
|
||||||
|
return generateOptionsFromObject(bodyshop, 'employee_teams', 'name', 'id');
|
||||||
|
// Special case because Employees uses a concatenation of first_name and last_name
|
||||||
|
case 'employees':
|
||||||
|
const employeesOptions = getValueFromPath(bodyshop, 'employees');
|
||||||
|
return uniqBy(Object.values(employeesOptions).map((value) => ({
|
||||||
|
label: `${value.first_name} ${value.last_name}`,
|
||||||
|
value: value.id,
|
||||||
|
})), 'value');
|
||||||
|
case 'last_names':
|
||||||
|
return generateOptionsFromObject(bodyshop, 'employees', 'last_name', 'last_name');
|
||||||
|
case 'first_names':
|
||||||
|
return generateOptionsFromObject(bodyshop, 'employees', 'first_name', 'first_name');
|
||||||
|
case 'job_statuses':
|
||||||
|
const statusOptions = getValueFromPath(bodyshop, 'md_ro_statuses.statuses');
|
||||||
|
return Object.values(statusOptions).map((value) => ({
|
||||||
|
label: value,
|
||||||
|
value
|
||||||
|
}));
|
||||||
|
default:
|
||||||
|
console.error('Invalid Special reflection provided by Report Filters');
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate bodyshop reflections
|
||||||
|
* @param bodyshop
|
||||||
|
* @param finalPath
|
||||||
|
* @returns {{label: *, value: *}[]|*[]}
|
||||||
|
*/
|
||||||
|
const generateBodyshopReflections = (bodyshop, finalPath) => {
|
||||||
|
const options = getValueFromPath(bodyshop, finalPath);
|
||||||
|
const reflectionRenderer = VALID_INTERNAL_REFLECTIONS.bodyshop.find(reflection => reflection.name === finalPath);
|
||||||
|
if (reflectionRenderer?.type === 'kv-to-v') {
|
||||||
|
return Object.values(options).map((value) => ({
|
||||||
|
label: value,
|
||||||
|
value
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate internal reflections based on the path and bodyshop
|
||||||
|
* @param bodyshop
|
||||||
|
* @param upperPath
|
||||||
|
* @param finalPath
|
||||||
|
* @returns {{label: *, value: *}[]|[]|{label: *, value: *}[]|{label: string, value: *}[]|{label: *, value: *}[]|*[]}
|
||||||
|
*/
|
||||||
|
const generateInternalReflections = ({bodyshop, upperPath, finalPath}) => {
|
||||||
|
switch (upperPath) {
|
||||||
|
case 'special':
|
||||||
|
return generateSpecialReflections(bodyshop, finalPath);
|
||||||
|
case 'bodyshop':
|
||||||
|
return generateBodyshopReflections(bodyshop, finalPath);
|
||||||
|
default:
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export {generateInternalReflections,}
|
||||||
@@ -1,305 +1,321 @@
|
|||||||
import { useLazyQuery } from "@apollo/client";
|
import {useLazyQuery} from "@apollo/client";
|
||||||
import {
|
import {Button, Card, Col, DatePicker, Form, Input, Radio, Row, Typography,} from "antd";
|
||||||
Button,
|
|
||||||
Card,
|
|
||||||
Col,
|
|
||||||
DatePicker,
|
|
||||||
Form,
|
|
||||||
Input,
|
|
||||||
Radio,
|
|
||||||
Row,
|
|
||||||
Typography,
|
|
||||||
} from "antd";
|
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import React, { useState } from "react";
|
import React, {useState} from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import {useTranslation} from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import {connect} from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import {createStructuredSelector} from "reselect";
|
||||||
import { QUERY_ACTIVE_EMPLOYEES } from "../../graphql/employees.queries";
|
import {QUERY_ACTIVE_EMPLOYEES} from "../../graphql/employees.queries";
|
||||||
import { QUERY_ALL_VENDORS } from "../../graphql/vendors.queries";
|
import {QUERY_ALL_VENDORS} from "../../graphql/vendors.queries";
|
||||||
import { selectReportCenter } from "../../redux/modals/modals.selectors";
|
import {selectReportCenter} from "../../redux/modals/modals.selectors";
|
||||||
import DatePIckerRanges from "../../utils/DatePickerRanges";
|
import DatePickerRanges from "../../utils/DatePickerRanges";
|
||||||
import { GenerateDocument } from "../../utils/RenderTemplate";
|
import {GenerateDocument} from "../../utils/RenderTemplate";
|
||||||
import { TemplateList } from "../../utils/TemplateConstants";
|
import {TemplateList} from "../../utils/TemplateConstants";
|
||||||
import EmployeeSearchSelect from "../employee-search-select/employee-search-select.component";
|
import EmployeeSearchSelect from "../employee-search-select/employee-search-select.component";
|
||||||
import VendorSearchSelect from "../vendor-search-select/vendor-search-select.component";
|
import VendorSearchSelect from "../vendor-search-select/vendor-search-select.component";
|
||||||
import "./report-center-modal.styles.scss";
|
import "./report-center-modal.styles.scss";
|
||||||
|
import ReportCenterModalFiltersSortersComponent from "./report-center-modal-filters-sorters-component";
|
||||||
|
import {selectBodyshop} from "../../redux/user/user.selectors";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
reportCenterModal: selectReportCenter,
|
reportCenterModal: selectReportCenter,
|
||||||
|
bodyshop: selectBodyshop,
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||||
});
|
});
|
||||||
export default connect(
|
export default connect(
|
||||||
mapStateToProps,
|
mapStateToProps,
|
||||||
mapDispatchToProps
|
mapDispatchToProps
|
||||||
)(ReportCenterModalComponent);
|
)(ReportCenterModalComponent);
|
||||||
|
|
||||||
export function ReportCenterModalComponent({ reportCenterModal }) {
|
export function ReportCenterModalComponent({reportCenterModal, bodyshop}) {
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
const [search, setSearch] = useState("");
|
const [search, setSearch] = useState("");
|
||||||
|
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const { t } = useTranslation();
|
const {t} = useTranslation();
|
||||||
const Templates = TemplateList("report_center");
|
const Templates = TemplateList("report_center");
|
||||||
const ReportsList = Object.keys(Templates).map((key) => {
|
const ReportsList = Object.keys(Templates).map((key) => {
|
||||||
return Templates[key];
|
return Templates[key];
|
||||||
});
|
});
|
||||||
const { visible } = reportCenterModal;
|
const {open} = reportCenterModal;
|
||||||
|
|
||||||
const [callVendorQuery, { data: vendorData, called: vendorCalled }] =
|
const [callVendorQuery, {data: vendorData, called: vendorCalled}] =
|
||||||
useLazyQuery(QUERY_ALL_VENDORS, {
|
useLazyQuery(QUERY_ALL_VENDORS, {
|
||||||
skip: !(
|
skip: !(
|
||||||
visible &&
|
open &&
|
||||||
Templates[form.getFieldValue("key")] &&
|
Templates[form.getFieldValue("key")] &&
|
||||||
Templates[form.getFieldValue("key")].idtype
|
Templates[form.getFieldValue("key")].idtype
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
const [callEmployeeQuery, { data: employeeData, called: employeeCalled }] =
|
const [callEmployeeQuery, {data: employeeData, called: employeeCalled}] =
|
||||||
useLazyQuery(QUERY_ACTIVE_EMPLOYEES, {
|
useLazyQuery(QUERY_ACTIVE_EMPLOYEES, {
|
||||||
skip: !(
|
skip: !(
|
||||||
visible &&
|
open &&
|
||||||
Templates[form.getFieldValue("key")] &&
|
Templates[form.getFieldValue("key")] &&
|
||||||
Templates[form.getFieldValue("key")].idtype
|
Templates[form.getFieldValue("key")].idtype
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleFinish = async (values) => {
|
const handleFinish = async (values) => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
const start = values.dates[0];
|
const start = values.dates ? values.dates[0] : null;
|
||||||
const end = values.dates[1];
|
const end = values.dates ? values.dates[1] : null;
|
||||||
const { id } = values;
|
const { id } = values;
|
||||||
|
|
||||||
await GenerateDocument(
|
const templateConfig = {
|
||||||
{
|
name: values.key,
|
||||||
name: values.key,
|
variables: {
|
||||||
variables: {
|
...(start
|
||||||
...(start
|
? {start: moment(start).startOf("day").format("YYYY-MM-DD")}
|
||||||
? { start: moment(start).startOf("day").format("YYYY-MM-DD") }
|
: {}),
|
||||||
: {}),
|
...(end ? {end: moment(end).endOf("day").format("YYYY-MM-DD")} : {}),
|
||||||
...(end
|
...(start ? {starttz: moment(start).startOf("day")} : {}),
|
||||||
? { end: moment(end).endOf("day").format("YYYY-MM-DD") }
|
...(end ? {endtz: moment(end).endOf("day")} : {}),
|
||||||
: {}),
|
|
||||||
...(start ? { starttz: moment(start).startOf("day") } : {}),
|
|
||||||
...(end ? { endtz: moment(end).endOf("day") } : {}),
|
|
||||||
|
|
||||||
...(id ? { id: id } : {}),
|
...(id ? {id: id} : {}),
|
||||||
|
},
|
||||||
|
filters: values.filters,
|
||||||
|
sorters: values.sorters,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (_.isString(values.defaultSorters) && !_.isEmpty(values.defaultSorters)) {
|
||||||
|
templateConfig.defaultSorters = JSON.parse(values.defaultSorters);
|
||||||
|
}
|
||||||
|
|
||||||
|
await GenerateDocument(
|
||||||
|
templateConfig,
|
||||||
|
{
|
||||||
|
to: values.to,
|
||||||
|
subject: Templates[values.key]?.subject,
|
||||||
},
|
},
|
||||||
},
|
values.sendbyexcel === "excel"
|
||||||
{
|
? "x"
|
||||||
to: values.to,
|
: values.sendby === "email"
|
||||||
subject: Templates[values.key]?.subject,
|
? "e"
|
||||||
},
|
: "p",
|
||||||
values.sendbyexcel === "excel"
|
id
|
||||||
? "x"
|
|
||||||
: values.sendby === "email"
|
|
||||||
? "e"
|
|
||||||
: "p",
|
|
||||||
id
|
|
||||||
);
|
);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const FilteredReportsList =
|
const FilteredReportsList =
|
||||||
search !== ""
|
search !== ""
|
||||||
? ReportsList.filter((r) =>
|
? ReportsList.filter((r) =>
|
||||||
r.title.toLowerCase().includes(search.toLowerCase())
|
r.title.toLowerCase().includes(search.toLowerCase())
|
||||||
)
|
)
|
||||||
: ReportsList;
|
: ReportsList;
|
||||||
|
|
||||||
//Group it, create cards, and then filter out.
|
//Group it, create cards, and then filter out.
|
||||||
|
|
||||||
const grouped = _.groupBy(FilteredReportsList, "group");
|
const grouped = _.groupBy(FilteredReportsList, "group");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Form
|
<Form
|
||||||
onFinish={handleFinish}
|
onFinish={handleFinish}
|
||||||
autoComplete={"off"}
|
autoComplete={"off"}
|
||||||
layout="vertical"
|
layout="vertical"
|
||||||
form={form}
|
form={form}
|
||||||
>
|
|
||||||
<Input.Search
|
|
||||||
onChange={(e) => setSearch(e.target.value)}
|
|
||||||
value={search}
|
|
||||||
/>
|
|
||||||
<Form.Item
|
|
||||||
name="key"
|
|
||||||
label={t("reportcenter.labels.key")}
|
|
||||||
// className="radio-group-columns"
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
>
|
||||||
<Radio.Group>
|
<Input.Search
|
||||||
{/* {Object.keys(Templates).map((key) => (
|
onChange={(e) => setSearch(e.target.value)}
|
||||||
|
value={search}
|
||||||
|
/>
|
||||||
|
<Form.Item name="defaultSorters" hidden/>
|
||||||
|
<Form.Item
|
||||||
|
name="key"
|
||||||
|
label={t("reportcenter.labels.key")}
|
||||||
|
// className="radio-group-columns"
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Radio.Group>
|
||||||
|
{/* {Object.keys(Templates).map((key) => (
|
||||||
<Radio key={key} value={key}>
|
<Radio key={key} value={key}>
|
||||||
{Templates[key].title}
|
{Templates[key].title}
|
||||||
</Radio>
|
</Radio>
|
||||||
))} */}
|
))} */}
|
||||||
|
|
||||||
<Row gutter={[16, 16]}>
|
<Row gutter={[16, 16]}>
|
||||||
{Object.keys(grouped).map((key) => (
|
{Object.keys(grouped).map((key) => (
|
||||||
<Col md={8} sm={12} key={key}>
|
<Col md={8} sm={12} key={key}>
|
||||||
<Card.Grid
|
<Card.Grid
|
||||||
style={{
|
style={{
|
||||||
width: "100%",
|
width: "100%",
|
||||||
height: "100%",
|
height: "100%",
|
||||||
maxHeight: "33vh",
|
maxHeight: "33vh",
|
||||||
overflowY: "scroll",
|
overflowY: "scroll",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography.Title level={4}>
|
<Typography.Title level={4}>
|
||||||
{t(`reportcenter.labels.groups.${key}`)}
|
{t(`reportcenter.labels.groups.${key}`)}
|
||||||
</Typography.Title>
|
</Typography.Title>
|
||||||
<ul style={{ columns: "2 auto" }}>
|
<ul style={{ listStyleType: 'none', columns: "2 auto"}}>
|
||||||
{grouped[key].map((item) => (
|
{grouped[key].map((item) => (
|
||||||
<li key={item.key}>
|
<li key={item.key}>
|
||||||
<Radio key={item.key} value={item.key}>
|
<Radio key={item.key} value={item.key}>
|
||||||
{item.title}
|
{item.title}
|
||||||
</Radio>
|
</Radio>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
</Card.Grid>
|
</Card.Grid>
|
||||||
</Col>
|
</Col>
|
||||||
))}
|
))}
|
||||||
</Row>
|
</Row>
|
||||||
</Radio.Group>
|
</Radio.Group>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item style={{ margin: 0, padding: 0 }} dependencies={["key"]}>
|
<Form.Item style={{margin: 0, padding: 0}} dependencies={["key"]}>
|
||||||
{() => {
|
{() => {
|
||||||
const key = form.getFieldValue("key");
|
const key = form.getFieldValue("key");
|
||||||
if (!key) return null;
|
if (!key) return null;
|
||||||
//Kind of Id
|
//Kind of Id
|
||||||
const rangeFilter = Templates[key] && Templates[key].rangeFilter;
|
const rangeFilter = Templates[key] && Templates[key].rangeFilter;
|
||||||
if (!rangeFilter) return null;
|
if (!rangeFilter) return null;
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
{t("reportcenter.labels.filterson", {
|
|
||||||
object: rangeFilter.object,
|
|
||||||
field: rangeFilter.field,
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item style={{ margin: 0, padding: 0 }} dependencies={["key"]}>
|
|
||||||
{() => {
|
|
||||||
const key = form.getFieldValue("key");
|
|
||||||
const currentId = form.getFieldValue("id");
|
|
||||||
if (!key) return null;
|
|
||||||
//Kind of Id
|
|
||||||
const idtype = Templates[key] && Templates[key].idtype;
|
|
||||||
if (!idtype && currentId) {
|
|
||||||
form.setFieldsValue({ id: null });
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (!vendorCalled && idtype === "vendor") callVendorQuery();
|
|
||||||
if (!employeeCalled && idtype === "employee") callEmployeeQuery();
|
|
||||||
if (idtype === "vendor")
|
|
||||||
return (
|
return (
|
||||||
<Form.Item
|
<div>
|
||||||
name="id"
|
{t("reportcenter.labels.filterson", {
|
||||||
label={t("reportcenter.labels.vendor")}
|
object: rangeFilter.object,
|
||||||
rules={[
|
field: rangeFilter.field,
|
||||||
{
|
})}
|
||||||
required: true,
|
</div>
|
||||||
//message: t("general.validation.required"),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<VendorSearchSelect
|
|
||||||
options={vendorData ? vendorData.vendors : []}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
);
|
);
|
||||||
if (idtype === "employee")
|
}}
|
||||||
return (
|
</Form.Item>
|
||||||
<Form.Item
|
<ReportCenterModalFiltersSortersComponent form={form} bodyshop={bodyshop} />
|
||||||
name="id"
|
<Form.Item style={{margin: 0, padding: 0}} dependencies={["key"]}>
|
||||||
label={t("reportcenter.labels.employee")}
|
{() => {
|
||||||
rules={[
|
const key = form.getFieldValue("key");
|
||||||
{
|
const currentId = form.getFieldValue("id");
|
||||||
required: true,
|
if (!key) return null;
|
||||||
//message: t("general.validation.required"),
|
//Kind of Id
|
||||||
},
|
const idtype = Templates[key] && Templates[key].idtype;
|
||||||
]}
|
if (!idtype && currentId) {
|
||||||
>
|
form.setFieldsValue({id: null});
|
||||||
<EmployeeSearchSelect
|
return null;
|
||||||
options={employeeData ? employeeData.employees : []}
|
}
|
||||||
/>
|
if (!vendorCalled && idtype === "vendor") callVendorQuery();
|
||||||
</Form.Item>
|
if (!employeeCalled && idtype === "employee") callEmployeeQuery();
|
||||||
);
|
if (idtype === "vendor")
|
||||||
else return null;
|
return (
|
||||||
}}
|
<Form.Item
|
||||||
</Form.Item>
|
name="id"
|
||||||
<Form.Item
|
label={t("reportcenter.labels.vendor")}
|
||||||
name="dates"
|
rules={[
|
||||||
label={t("reportcenter.labels.dates")}
|
{
|
||||||
rules={[
|
required: true,
|
||||||
{
|
//message: t("general.validation.required"),
|
||||||
required: true,
|
},
|
||||||
//message: t("general.validation.required"),
|
]}
|
||||||
},
|
>
|
||||||
]}
|
<VendorSearchSelect
|
||||||
>
|
options={vendorData ? vendorData.vendors : []}
|
||||||
<DatePicker.RangePicker
|
/>
|
||||||
format="MM/DD/YYYY"
|
</Form.Item>
|
||||||
ranges={DatePIckerRanges}
|
);
|
||||||
/>
|
if (idtype === "employee")
|
||||||
</Form.Item>
|
return (
|
||||||
<Form.Item style={{ margin: 0, padding: 0 }} dependencies={["key"]}>
|
<Form.Item
|
||||||
{() => {
|
name="id"
|
||||||
const key = form.getFieldValue("key");
|
label={t("reportcenter.labels.employee")}
|
||||||
//Kind of Id
|
rules={[
|
||||||
const reporttype = Templates[key] && Templates[key].reporttype;
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<EmployeeSearchSelect
|
||||||
|
options={employeeData ? employeeData.employees : []}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
);
|
||||||
|
else return null;
|
||||||
|
}}
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item style={{margin: 0, padding: 0}} dependencies={["key"]}>
|
||||||
|
{() => {
|
||||||
|
const key = form.getFieldValue("key");
|
||||||
|
const datedisable = Templates[key] && Templates[key].datedisable;
|
||||||
|
|
||||||
if (reporttype === "excel")
|
// TODO: MERGE NOTE, Ranges turns to presets in DatePicker.RangePicker
|
||||||
return (
|
|
||||||
<Form.Item
|
|
||||||
label={t("general.labels.sendby")}
|
|
||||||
name="sendbyexcel"
|
|
||||||
initialValue="excel"
|
|
||||||
>
|
|
||||||
<Radio.Group>
|
|
||||||
<Radio value="excel">{t("general.labels.excel")}</Radio>
|
|
||||||
</Radio.Group>
|
|
||||||
</Form.Item>
|
|
||||||
);
|
|
||||||
if (reporttype !== "excel")
|
|
||||||
return (
|
|
||||||
<Form.Item
|
|
||||||
label={t("general.labels.sendby")}
|
|
||||||
name="sendby"
|
|
||||||
initialValue="print"
|
|
||||||
>
|
|
||||||
<Radio.Group>
|
|
||||||
<Radio value="email">{t("general.labels.email")}</Radio>
|
|
||||||
<Radio value="print">{t("general.labels.print")}</Radio>
|
|
||||||
</Radio.Group>
|
|
||||||
</Form.Item>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
</Form.Item>
|
|
||||||
|
|
||||||
<div
|
if (datedisable !== true) {
|
||||||
style={{
|
return (
|
||||||
display: "flex",
|
<Form.Item
|
||||||
justifyContent: "center",
|
name="dates"
|
||||||
marginTop: "1rem",
|
label={t("reportcenter.labels.dates")}
|
||||||
}}
|
rules={[
|
||||||
>
|
{
|
||||||
<Button onClick={() => form.submit()} style={{}} loading={loading}>
|
required: true,
|
||||||
{t("reportcenter.actions.generate")}
|
//message: t("general.validation.required"),
|
||||||
</Button>
|
},
|
||||||
</div>
|
]}
|
||||||
</Form>
|
>
|
||||||
</div>
|
<DatePicker.RangePicker
|
||||||
|
format="MM/DD/YYYY"
|
||||||
|
ranges={DatePickerRanges}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
);
|
||||||
|
} else return null;
|
||||||
|
}}
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item style={{margin: 0, padding: 0}} dependencies={["key"]}>
|
||||||
|
{() => {
|
||||||
|
const key = form.getFieldValue("key");
|
||||||
|
//Kind of Id
|
||||||
|
const reporttype = Templates[key] && Templates[key].reporttype;
|
||||||
|
|
||||||
|
if (reporttype === "excel")
|
||||||
|
return (
|
||||||
|
<Form.Item
|
||||||
|
label={t("general.labels.sendby")}
|
||||||
|
name="sendbyexcel"
|
||||||
|
initialValue="excel"
|
||||||
|
>
|
||||||
|
<Radio.Group>
|
||||||
|
<Radio value="excel">{t("general.labels.excel")}</Radio>
|
||||||
|
</Radio.Group>
|
||||||
|
</Form.Item>
|
||||||
|
);
|
||||||
|
if (reporttype !== "excel")
|
||||||
|
return (
|
||||||
|
<Form.Item
|
||||||
|
label={t("general.labels.sendby")}
|
||||||
|
name="sendby"
|
||||||
|
initialValue="print"
|
||||||
|
>
|
||||||
|
<Radio.Group>
|
||||||
|
<Radio value="email">{t("general.labels.email")}</Radio>
|
||||||
|
<Radio value="print">{t("general.labels.print")}</Radio>
|
||||||
|
</Radio.Group>
|
||||||
|
</Form.Item>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "center",
|
||||||
|
marginTop: "1rem",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button onClick={() => form.submit()} style={{}} loading={loading}>
|
||||||
|
{t("reportcenter.actions.generate")}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</Form>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { connect } from "react-redux";
|
|||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import { toggleModalVisible } from "../../redux/modals/modals.actions";
|
import { toggleModalVisible } from "../../redux/modals/modals.actions";
|
||||||
import { selectReportCenter } from "../../redux/modals/modals.selectors";
|
import { selectReportCenter } from "../../redux/modals/modals.selectors";
|
||||||
|
import RbacWrapperComponent from "../rbac-wrapper/rbac-wrapper.component";
|
||||||
import ReportCenterModalComponent from "./report-center-modal.component";
|
import ReportCenterModalComponent from "./report-center-modal.component";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
@@ -33,7 +34,9 @@ export function ReportCenterModalContainer({
|
|||||||
destroyOnClose
|
destroyOnClose
|
||||||
width="80%"
|
width="80%"
|
||||||
>
|
>
|
||||||
<ReportCenterModalComponent />
|
<RbacWrapperComponent action="shop:reportcenter">
|
||||||
|
<ReportCenterModalComponent />
|
||||||
|
</RbacWrapperComponent>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -216,6 +216,7 @@ export function ScheduleJobModalContainer({
|
|||||||
okButtonProps={{
|
okButtonProps={{
|
||||||
loading: loading,
|
loading: loading,
|
||||||
}}
|
}}
|
||||||
|
closable={false}
|
||||||
>
|
>
|
||||||
<Form
|
<Form
|
||||||
form={form}
|
form={form}
|
||||||
|
|||||||
@@ -1,5 +1,14 @@
|
|||||||
import { useMutation } from "@apollo/client";
|
import { useMutation } from "@apollo/client";
|
||||||
import { Button, Card, Dropdown, Form, InputNumber, notification } from "antd";
|
import {
|
||||||
|
Button,
|
||||||
|
Card,
|
||||||
|
Dropdown,
|
||||||
|
Form,
|
||||||
|
InputNumber,
|
||||||
|
notification,
|
||||||
|
Space,
|
||||||
|
} from "antd";
|
||||||
|
import moment from "moment";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { UPDATE_SCOREBOARD_ENTRY } from "../../graphql/scoreboard.queries";
|
import { UPDATE_SCOREBOARD_ENTRY } from "../../graphql/scoreboard.queries";
|
||||||
@@ -13,6 +22,7 @@ export default function ScoreboardEntryEdit({ entry }) {
|
|||||||
|
|
||||||
const handleFinish = async (values) => {
|
const handleFinish = async (values) => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
values.date = moment(values.date).format("YYYY-MM-DD");
|
||||||
const result = await updateScoreboardentry({
|
const result = await updateScoreboardentry({
|
||||||
variables: { sbId: entry.id, sbInput: values },
|
variables: { sbId: entry.id, sbInput: values },
|
||||||
});
|
});
|
||||||
@@ -77,13 +87,14 @@ export default function ScoreboardEntryEdit({ entry }) {
|
|||||||
>
|
>
|
||||||
<InputNumber precision={1} />
|
<InputNumber precision={1} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
<Space wrap>
|
||||||
<Button type="primary" loading={loading} htmlType="submit">
|
<Button type="primary" loading={loading} htmlType="submit">
|
||||||
{t("general.actions.save")}
|
{t("general.actions.save")}
|
||||||
</Button>
|
</Button>
|
||||||
<Button onClick={() => setVisible(false)}>
|
<Button onClick={() => setVisible(false)}>
|
||||||
{t("general.actions.cancel")}
|
{t("general.actions.cancel")}
|
||||||
</Button>
|
</Button>
|
||||||
|
</Space>
|
||||||
</Form>
|
</Form>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { SyncOutlined } from "@ant-design/icons";
|
||||||
import { useQuery } from "@apollo/client";
|
import { useQuery } from "@apollo/client";
|
||||||
import { Button, Card, Input, Modal, Space, Table, Typography } from "antd";
|
import { Button, Card, Input, Modal, Space, Table, Typography } from "antd";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
@@ -5,12 +6,14 @@ import { useTranslation } from "react-i18next";
|
|||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import { QUERY_SCOREBOARD_PAGINATED } from "../../graphql/scoreboard.queries";
|
import { QUERY_SCOREBOARD_PAGINATED } from "../../graphql/scoreboard.queries";
|
||||||
import { DateFormatter } from "../../utils/DateFormatter";
|
import { DateFormatter } from "../../utils/DateFormatter";
|
||||||
|
import { pageLimit } from "../../utils/config";
|
||||||
|
import { alphaSort, dateSort } from "../../utils/sorters";
|
||||||
import AlertComponent from "../alert/alert.component";
|
import AlertComponent from "../alert/alert.component";
|
||||||
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
import OwnerNameDisplay, {
|
||||||
|
OwnerNameDisplayFunction,
|
||||||
|
} from "../owner-name-display/owner-name-display.component";
|
||||||
import ScoreboardEntryEdit from "../scoreboard-entry-edit/scoreboard-entry-edit.component";
|
import ScoreboardEntryEdit from "../scoreboard-entry-edit/scoreboard-entry-edit.component";
|
||||||
import ScoreboardRemoveButton from "../scoreboard-remove-button/scorebard-remove-button.component";
|
import ScoreboardRemoveButton from "../scoreboard-remove-button/scorebard-remove-button.component";
|
||||||
import { SyncOutlined } from "@ant-design/icons";
|
|
||||||
import {pageLimit} from "../../utils/config";
|
|
||||||
export default function ScoreboardJobsList({ scoreBoardlist }) {
|
export default function ScoreboardJobsList({ scoreBoardlist }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [state, setState] = useState({
|
const [state, setState] = useState({
|
||||||
@@ -44,6 +47,7 @@ export default function ScoreboardJobsList({ scoreBoardlist }) {
|
|||||||
title: t("jobs.fields.ro_number"),
|
title: t("jobs.fields.ro_number"),
|
||||||
dataIndex: "ro_number",
|
dataIndex: "ro_number",
|
||||||
key: "ro_number",
|
key: "ro_number",
|
||||||
|
sorter: (a, b) => alphaSort(a.job.ro_number, b.job.ro_number),
|
||||||
render: (text, record) => (
|
render: (text, record) => (
|
||||||
<Link to={"/manage/jobs/" + record.job.id}>
|
<Link to={"/manage/jobs/" + record.job.id}>
|
||||||
{record.job.ro_number || t("general.labels.na")}
|
{record.job.ro_number || t("general.labels.na")}
|
||||||
@@ -55,7 +59,11 @@ export default function ScoreboardJobsList({ scoreBoardlist }) {
|
|||||||
dataIndex: "owner",
|
dataIndex: "owner",
|
||||||
key: "owner",
|
key: "owner",
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
|
sorter: (a, b) =>
|
||||||
|
alphaSort(
|
||||||
|
OwnerNameDisplayFunction(a.job),
|
||||||
|
OwnerNameDisplayFunction(b.job)
|
||||||
|
),
|
||||||
render: (text, record) => <OwnerNameDisplay ownerObject={record.job} />,
|
render: (text, record) => <OwnerNameDisplay ownerObject={record.job} />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -63,6 +71,15 @@ export default function ScoreboardJobsList({ scoreBoardlist }) {
|
|||||||
dataIndex: "vehicle",
|
dataIndex: "vehicle",
|
||||||
key: "vehicle",
|
key: "vehicle",
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
|
sorter: (a, b) =>
|
||||||
|
alphaSort(
|
||||||
|
`${a.job.v_model_yr || ""} ${a.job.v_make_desc || ""} ${
|
||||||
|
a.job.v_model_desc || ""
|
||||||
|
}`,
|
||||||
|
`${b.job.v_model_yr || ""} ${b.job.v_make_desc || ""} ${
|
||||||
|
b.job.v_model_desc || ""
|
||||||
|
}`
|
||||||
|
),
|
||||||
render: (text, record) => (
|
render: (text, record) => (
|
||||||
<span>{`${record.job.v_model_yr || ""} ${
|
<span>{`${record.job.v_model_yr || ""} ${
|
||||||
record.job.v_make_desc || ""
|
record.job.v_make_desc || ""
|
||||||
@@ -73,17 +90,20 @@ export default function ScoreboardJobsList({ scoreBoardlist }) {
|
|||||||
title: t("scoreboard.fields.date"),
|
title: t("scoreboard.fields.date"),
|
||||||
dataIndex: "date",
|
dataIndex: "date",
|
||||||
key: "date",
|
key: "date",
|
||||||
|
sorter: (a, b) => dateSort(a.date, b.date),
|
||||||
render: (text, record) => <DateFormatter>{record.date}</DateFormatter>,
|
render: (text, record) => <DateFormatter>{record.date}</DateFormatter>,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
title: t("scoreboard.fields.painthrs"),
|
|
||||||
dataIndex: "painthrs",
|
|
||||||
key: "painthrs",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
title: t("scoreboard.fields.bodyhrs"),
|
title: t("scoreboard.fields.bodyhrs"),
|
||||||
dataIndex: "bodyhrs",
|
dataIndex: "bodyhrs",
|
||||||
key: "bodyhrs",
|
key: "bodyhrs",
|
||||||
|
sorter: (a, b) => Number(a.bodyhrs) - Number(b.bodyhrs),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("scoreboard.fields.painthrs"),
|
||||||
|
dataIndex: "painthrs",
|
||||||
|
key: "painthrs",
|
||||||
|
sorter: (a, b) => Number(a.painthrs) - Number(b.painthrs),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("general.labels.actions"),
|
title: t("general.labels.actions"),
|
||||||
@@ -104,8 +124,9 @@ export default function ScoreboardJobsList({ scoreBoardlist }) {
|
|||||||
visible={state.visible}
|
visible={state.visible}
|
||||||
destroyOnClose
|
destroyOnClose
|
||||||
width="80%"
|
width="80%"
|
||||||
|
closable={false}
|
||||||
cancelButtonProps={{ style: { display: "none" } }}
|
cancelButtonProps={{ style: { display: "none" } }}
|
||||||
onCancel={() =>
|
onOk={() =>
|
||||||
setState((state) => ({
|
setState((state) => ({
|
||||||
...state,
|
...state,
|
||||||
visible: false,
|
visible: false,
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export default connect(
|
|||||||
|
|
||||||
export function ScoreboardTimeTicketsStats({ bodyshop }) {
|
export function ScoreboardTimeTicketsStats({ bodyshop }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const startDate = moment().startOf("month")
|
const startDate = moment().startOf("month");
|
||||||
const endDate = moment().endOf("month");
|
const endDate = moment().endOf("month");
|
||||||
|
|
||||||
const fixedPeriods = useMemo(() => {
|
const fixedPeriods = useMemo(() => {
|
||||||
@@ -84,6 +84,8 @@ export function ScoreboardTimeTicketsStats({ bodyshop }) {
|
|||||||
end: endDate.format("YYYY-MM-DD"),
|
end: endDate.format("YYYY-MM-DD"),
|
||||||
fixedStart: fixedPeriods.start.format("YYYY-MM-DD"),
|
fixedStart: fixedPeriods.start.format("YYYY-MM-DD"),
|
||||||
fixedEnd: fixedPeriods.end.format("YYYY-MM-DD"),
|
fixedEnd: fixedPeriods.end.format("YYYY-MM-DD"),
|
||||||
|
jobStart: startDate,
|
||||||
|
jobEnd: endDate,
|
||||||
},
|
},
|
||||||
fetchPolicy: "network-only",
|
fetchPolicy: "network-only",
|
||||||
nextFetchPolicy: "network-only",
|
nextFetchPolicy: "network-only",
|
||||||
@@ -340,11 +342,21 @@ export function ScoreboardTimeTicketsStats({ bodyshop }) {
|
|||||||
larData.push({ ...r, ...lar });
|
larData.push({ ...r, ...lar });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const jobData = {};
|
||||||
|
data.jobs.forEach((job) => {
|
||||||
|
job.tthrs = job.joblines.reduce((acc, val) => acc + val.mod_lb_hrs, 0);
|
||||||
|
});
|
||||||
|
jobData.tthrs = data.jobs
|
||||||
|
.reduce((acc, val) => acc + val.tthrs, 0)
|
||||||
|
.toFixed(1);
|
||||||
|
jobData.count = data.jobs.length.toFixed(0);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
fixed: ret,
|
fixed: ret,
|
||||||
combinedData: combinedData,
|
combinedData: combinedData,
|
||||||
labData: labData,
|
labData: labData,
|
||||||
larData: larData,
|
larData: larData,
|
||||||
|
jobData: jobData,
|
||||||
};
|
};
|
||||||
}, [fixedPeriods, data, bodyshop]);
|
}, [fixedPeriods, data, bodyshop]);
|
||||||
|
|
||||||
@@ -356,7 +368,10 @@ export function ScoreboardTimeTicketsStats({ bodyshop }) {
|
|||||||
<ScoreboardTimeticketsTargetsTable />
|
<ScoreboardTimeticketsTargetsTable />
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={24}>
|
<Col span={24}>
|
||||||
<ScoreboardTicketsStats data={calculatedData.fixed} />
|
<ScoreboardTicketsStats
|
||||||
|
data={calculatedData.fixed}
|
||||||
|
jobData={calculatedData.jobData}
|
||||||
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={24}>
|
<Col span={24}>
|
||||||
<ScoreboardTimeTicketsChart
|
<ScoreboardTimeTicketsChart
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ function useLocalStorage(key, initialValue) {
|
|||||||
return [storedValue, setStoredValue];
|
return [storedValue, setStoredValue];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ScoreboardTicketsStats({ data, bodyshop }) {
|
export function ScoreboardTicketsStats({ data, jobData, bodyshop }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [isLarge, setIsLarge] = useLocalStorage("isLargeStatistic", false);
|
const [isLarge, setIsLarge] = useLocalStorage("isLargeStatistic", false);
|
||||||
|
|
||||||
@@ -408,7 +408,7 @@ export function ScoreboardTicketsStats({ data, bodyshop }) {
|
|||||||
{/* Monthly Stats */}
|
{/* Monthly Stats */}
|
||||||
<Row gutter={[16, 16]}>
|
<Row gutter={[16, 16]}>
|
||||||
{/* This Month */}
|
{/* This Month */}
|
||||||
<Col span={8} align="center">
|
<Col span={7} align="center">
|
||||||
<Card size="small" title={t("scoreboard.labels.thismonth")}>
|
<Card size="small" title={t("scoreboard.labels.thismonth")}>
|
||||||
<Row gutter={[16, 16]}>
|
<Row gutter={[16, 16]}>
|
||||||
<Col span={24}>
|
<Col span={24}>
|
||||||
@@ -482,7 +482,7 @@ export function ScoreboardTicketsStats({ data, bodyshop }) {
|
|||||||
</Card>
|
</Card>
|
||||||
</Col>
|
</Col>
|
||||||
{/* Last Month */}
|
{/* Last Month */}
|
||||||
<Col span={8} align="center">
|
<Col span={7} align="center">
|
||||||
<Card size="small" title={t("scoreboard.labels.lastmonth")}>
|
<Card size="small" title={t("scoreboard.labels.lastmonth")}>
|
||||||
<Row gutter={[16, 16]}>
|
<Row gutter={[16, 16]}>
|
||||||
<Col span={24}>
|
<Col span={24}>
|
||||||
@@ -556,7 +556,7 @@ export function ScoreboardTicketsStats({ data, bodyshop }) {
|
|||||||
</Card>
|
</Card>
|
||||||
</Col>
|
</Col>
|
||||||
{/* Efficiency Over Period */}
|
{/* Efficiency Over Period */}
|
||||||
<Col span={8} align="center">
|
<Col span={7} align="center">
|
||||||
<Card
|
<Card
|
||||||
size="small"
|
size="small"
|
||||||
title={t("scoreboard.labels.efficiencyoverperiod")}
|
title={t("scoreboard.labels.efficiencyoverperiod")}
|
||||||
@@ -604,6 +604,40 @@ export function ScoreboardTicketsStats({ data, bodyshop }) {
|
|||||||
</Row>
|
</Row>
|
||||||
</Card>
|
</Card>
|
||||||
</Col>
|
</Col>
|
||||||
|
<Col span={3} align="center">
|
||||||
|
<Card
|
||||||
|
size="small"
|
||||||
|
title={t("scoreboard.labels.jobscompletednotinvoiced")}
|
||||||
|
>
|
||||||
|
<Row gutter={[16, 16]}>
|
||||||
|
<Col span={24}>
|
||||||
|
<Statistic
|
||||||
|
value={jobData.count}
|
||||||
|
valueStyle={{
|
||||||
|
fontSize: statisticSize,
|
||||||
|
fontWeight: statisticWeight,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row gutter={[16, 16]}>
|
||||||
|
<Col span={24}>
|
||||||
|
<Statistic
|
||||||
|
title={
|
||||||
|
<Typography.Text strong>
|
||||||
|
{t("scoreboard.labels.totalhrs")}
|
||||||
|
</Typography.Text>
|
||||||
|
}
|
||||||
|
value={jobData.tthrs}
|
||||||
|
valueStyle={{
|
||||||
|
fontSize: statisticSize,
|
||||||
|
fontWeight: statisticWeight,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</Card>
|
||||||
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
</Space>
|
</Space>
|
||||||
{/* Disclaimer */}
|
{/* Disclaimer */}
|
||||||
|
|||||||
@@ -65,6 +65,8 @@ export default function ScoreboardTimeTickets() {
|
|||||||
end: endDate.format("YYYY-MM-DD"),
|
end: endDate.format("YYYY-MM-DD"),
|
||||||
fixedStart: fixedPeriods.start.format("YYYY-MM-DD"),
|
fixedStart: fixedPeriods.start.format("YYYY-MM-DD"),
|
||||||
fixedEnd: fixedPeriods.end.format("YYYY-MM-DD"),
|
fixedEnd: fixedPeriods.end.format("YYYY-MM-DD"),
|
||||||
|
jobStart: startDate,
|
||||||
|
jobEnd: endDate,
|
||||||
},
|
},
|
||||||
fetchPolicy: "network-only",
|
fetchPolicy: "network-only",
|
||||||
nextFetchPolicy: "network-only",
|
nextFetchPolicy: "network-only",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React from "react";
|
|
||||||
import { Form } from "antd";
|
import { Form } from "antd";
|
||||||
|
import React from "react";
|
||||||
import ConfigFormComponents from "../config-form-components/config-form-components.component";
|
import ConfigFormComponents from "../config-form-components/config-form-components.component";
|
||||||
|
|
||||||
export default function ShopCsiConfigForm({ selectedCsi }) {
|
export default function ShopCsiConfigForm({ selectedCsi }) {
|
||||||
@@ -9,7 +9,7 @@ export default function ShopCsiConfigForm({ selectedCsi }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
The Config Form {readOnly}
|
{readOnly}
|
||||||
{selectedCsi && (
|
{selectedCsi && (
|
||||||
<Form form={form} onFinish={handleFinish}>
|
<Form form={form} onFinish={handleFinish}>
|
||||||
<ConfigFormComponents
|
<ConfigFormComponents
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { CheckCircleFilled } from "@ant-design/icons";
|
import { CheckCircleFilled } from "@ant-design/icons";
|
||||||
|
import { useQuery } from "@apollo/client";
|
||||||
import { Button, Col, List, Row } from "antd";
|
import { Button, Col, List, Row } from "antd";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { useQuery } from "@apollo/client";
|
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { GET_ALL_QUESTION_SETS } from "../../graphql/csi.queries";
|
import { GET_ALL_QUESTION_SETS } from "../../graphql/csi.queries";
|
||||||
import { DateFormatter } from "../../utils/DateFormatter";
|
import { DateFormatter } from "../../utils/DateFormatter";
|
||||||
@@ -21,7 +21,6 @@ export default function ShopCsiConfig() {
|
|||||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
The Config Form
|
|
||||||
<Row>
|
<Row>
|
||||||
<Col span={3}>
|
<Col span={3}>
|
||||||
<List
|
<List
|
||||||
@@ -42,7 +41,8 @@ export default function ShopCsiConfig() {
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={21}>
|
<Col span={1} />
|
||||||
|
<Col span={20}>
|
||||||
<ShopCsiConfigForm selectedCsi={selectedCsi} />
|
<ShopCsiConfigForm selectedCsi={selectedCsi} />
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
|||||||
@@ -1,14 +1,20 @@
|
|||||||
import { Button, Table } from "antd";
|
import { Button, Table } from "antd";
|
||||||
import queryString from "query-string";
|
import queryString from "query-string";
|
||||||
import React from "react";
|
import React, { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useHistory, useLocation } from "react-router-dom";
|
import { useHistory, useLocation } from "react-router-dom";
|
||||||
|
import { alphaSort } from "../../utils/sorters";
|
||||||
|
|
||||||
export default function ShopEmployeesListComponent({ loading, employees }) {
|
export default function ShopEmployeesListComponent({ loading, employees }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const search = queryString.parse(useLocation().search);
|
const search = queryString.parse(useLocation().search);
|
||||||
|
|
||||||
|
const [state, setState] = useState({
|
||||||
|
sortedInfo: {},
|
||||||
|
filteredInfo: { text: "" },
|
||||||
|
});
|
||||||
|
|
||||||
const handleOnRowClick = (record) => {
|
const handleOnRowClick = (record) => {
|
||||||
if (record) {
|
if (record) {
|
||||||
search.employeeId = record.id;
|
search.employeeId = record.id;
|
||||||
@@ -18,32 +24,82 @@ export default function ShopEmployeesListComponent({ loading, employees }) {
|
|||||||
history.push({ search: queryString.stringify(search) });
|
history.push({ search: queryString.stringify(search) });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleTableChange = (pagination, filters, sorter) => {
|
||||||
|
setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
|
||||||
|
};
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
title: t("employees.fields.employee_number"),
|
title: t("employees.fields.employee_number"),
|
||||||
dataIndex: "employee_number",
|
dataIndex: "employee_number",
|
||||||
key: "employee_number",
|
key: "employee_number",
|
||||||
|
sorter: (a, b) => alphaSort(a.employee_number, b.employee_number),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "employee_number" &&
|
||||||
|
state.sortedInfo.order,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("employees.fields.first_name"),
|
title: t("employees.labels.name"),
|
||||||
dataIndex: "first_name",
|
dataIndex: "employee_name",
|
||||||
key: "first_name",
|
key: "employee_name",
|
||||||
|
sorter: (a, b) =>
|
||||||
|
alphaSort(
|
||||||
|
`${a.first_name || ""} ${a.last_name || ""}`.trim(),
|
||||||
|
`${b.first_name || ""} ${b.last_name || ""}`.trim()
|
||||||
|
),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "employee_name" &&
|
||||||
|
state.sortedInfo.order,
|
||||||
|
render: (text, record) =>
|
||||||
|
`${record.first_name || ""} ${record.last_name || ""}`.trim(),
|
||||||
},
|
},
|
||||||
{
|
|
||||||
title: t("employees.fields.last_name"),
|
|
||||||
dataIndex: "last_name",
|
|
||||||
key: "last_name",
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
{
|
||||||
title: t("employees.labels.rate_type"),
|
title: t("employees.labels.rate_type"),
|
||||||
dataIndex: "rate_type",
|
dataIndex: "rate_type",
|
||||||
key: "rate_type",
|
key: "rate_type",
|
||||||
|
sorter: (a, b) => Number(a.flat_rate) - Number(b.flat_rate),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "rate_type" && state.sortedInfo.order,
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
text: t("employees.labels.flat_rate"),
|
||||||
|
value: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: t("employees.labels.straight_time"),
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
onFilter: (value, record) => value === record.flate_rate,
|
||||||
render: (text, record) =>
|
render: (text, record) =>
|
||||||
record.flat_rate
|
record.flat_rate
|
||||||
? t("employees.labels.flat_rate")
|
? t("employees.labels.flat_rate")
|
||||||
: t("employees.labels.straight_time"),
|
: t("employees.labels.straight_time"),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: t("employees.labels.status"),
|
||||||
|
dataIndex: "active",
|
||||||
|
key: "active",
|
||||||
|
sorter: (a, b) => Number(a.active) - Number(b.active),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "active" && state.sortedInfo.order,
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
text: t("employees.labels.active"),
|
||||||
|
value: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: t("employees.labels.inactive"),
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
onFilter: (value, record) => value === record.active,
|
||||||
|
render: (text, record) =>
|
||||||
|
record.active
|
||||||
|
? t("employees.labels.active")
|
||||||
|
: t("employees.labels.inactive"),
|
||||||
|
},
|
||||||
];
|
];
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@@ -74,6 +130,7 @@ export default function ShopEmployeesListComponent({ loading, employees }) {
|
|||||||
type: "radio",
|
type: "radio",
|
||||||
selectedRowKeys: [search.employeeId],
|
selectedRowKeys: [search.employeeId],
|
||||||
}}
|
}}
|
||||||
|
onChange={handleTableChange}
|
||||||
onRow={(record, rowIndex) => {
|
onRow={(record, rowIndex) => {
|
||||||
return {
|
return {
|
||||||
onClick: (event) => {
|
onClick: (event) => {
|
||||||
|
|||||||
@@ -28,18 +28,6 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
|
|||||||
return (
|
return (
|
||||||
<RbacWrapper action="shop:rbac">
|
<RbacWrapper action="shop:rbac">
|
||||||
<LayoutFormRow>
|
<LayoutFormRow>
|
||||||
<Form.Item
|
|
||||||
label={t("bodyshop.fields.rbac.accounting.payables")}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
name={["md_rbac", "accounting:payables"]}
|
|
||||||
>
|
|
||||||
<InputNumber />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("bodyshop.fields.rbac.accounting.exportlog")}
|
label={t("bodyshop.fields.rbac.accounting.exportlog")}
|
||||||
rules={[
|
rules={[
|
||||||
@@ -52,6 +40,18 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
|
|||||||
>
|
>
|
||||||
<InputNumber />
|
<InputNumber />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.rbac.accounting.payables")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
name={["md_rbac", "accounting:payables"]}
|
||||||
|
>
|
||||||
|
<InputNumber />
|
||||||
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("bodyshop.fields.rbac.accounting.payments")}
|
label={t("bodyshop.fields.rbac.accounting.payments")}
|
||||||
rules={[
|
rules={[
|
||||||
@@ -77,26 +77,62 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
|
|||||||
<InputNumber />
|
<InputNumber />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("bodyshop.fields.rbac.csi.page")}
|
label={t("bodyshop.fields.rbac.bills.delete")}
|
||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
//message: t("general.validation.required"),
|
//message: t("general.validation.required"),
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
name={["md_rbac", "csi:page"]}
|
name={["md_rbac", "bills:delete"]}
|
||||||
>
|
>
|
||||||
<InputNumber />
|
<InputNumber />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("bodyshop.fields.rbac.csi.export")}
|
label={t("bodyshop.fields.rbac.bills.enter")}
|
||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
//message: t("general.validation.required"),
|
//message: t("general.validation.required"),
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
name={["md_rbac", "csi:export"]}
|
name={["md_rbac", "bills:enter"]}
|
||||||
|
>
|
||||||
|
<InputNumber />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.rbac.bills.list")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
name={["md_rbac", "bills:list"]}
|
||||||
|
>
|
||||||
|
<InputNumber />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.rbac.bills.reexport")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
name={["md_rbac", "bills:reexport"]}
|
||||||
|
>
|
||||||
|
<InputNumber />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.rbac.bills.view")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
name={["md_rbac", "bills:view"]}
|
||||||
>
|
>
|
||||||
<InputNumber />
|
<InputNumber />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
@@ -173,26 +209,38 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
|
|||||||
<InputNumber />
|
<InputNumber />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("bodyshop.fields.rbac.jobs.list-active")}
|
label={t("bodyshop.fields.rbac.csi.export")}
|
||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
//message: t("general.validation.required"),
|
//message: t("general.validation.required"),
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
name={["md_rbac", "jobs:list-active"]}
|
name={["md_rbac", "csi:export"]}
|
||||||
>
|
>
|
||||||
<InputNumber />
|
<InputNumber />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("bodyshop.fields.rbac.jobs.list-ready")}
|
label={t("bodyshop.fields.rbac.csi.page")}
|
||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
//message: t("general.validation.required"),
|
//message: t("general.validation.required"),
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
name={["md_rbac", "jobs:list-ready"]}
|
name={["md_rbac", "csi:page"]}
|
||||||
|
>
|
||||||
|
<InputNumber />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.rbac.employees.page")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
name={["md_rbac", "employees:page"]}
|
||||||
>
|
>
|
||||||
<InputNumber />
|
<InputNumber />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
@@ -208,30 +256,6 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
|
|||||||
>
|
>
|
||||||
<InputNumber />
|
<InputNumber />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
|
||||||
label={t("bodyshop.fields.rbac.jobs.partsqueue")}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
name={["md_rbac", "jobs:partsqueue"]}
|
|
||||||
>
|
|
||||||
<InputNumber />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t("bodyshop.fields.rbac.jobs.list-all")}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
name={["md_rbac", "jobs:list-all"]}
|
|
||||||
>
|
|
||||||
<InputNumber />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("bodyshop.fields.rbac.jobs.available-list")}
|
label={t("bodyshop.fields.rbac.jobs.available-list")}
|
||||||
rules={[
|
rules={[
|
||||||
@@ -245,26 +269,14 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
|
|||||||
<InputNumber />
|
<InputNumber />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("bodyshop.fields.rbac.jobs.create")}
|
label={t("bodyshop.fields.rbac.jobs.checklist-view")}
|
||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
//message: t("general.validation.required"),
|
//message: t("general.validation.required"),
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
name={["md_rbac", "jobs:create"]}
|
name={["md_rbac", "jobs:checklist-view"]}
|
||||||
>
|
|
||||||
<InputNumber />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t("bodyshop.fields.rbac.jobs.intake")}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
name={["md_rbac", "jobs:intake"]}
|
|
||||||
>
|
>
|
||||||
<InputNumber />
|
<InputNumber />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
@@ -280,6 +292,18 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
|
|||||||
>
|
>
|
||||||
<InputNumber />
|
<InputNumber />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.rbac.jobs.create")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
name={["md_rbac", "jobs:create"]}
|
||||||
|
>
|
||||||
|
<InputNumber />
|
||||||
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("bodyshop.fields.rbac.jobs.deliver")}
|
label={t("bodyshop.fields.rbac.jobs.deliver")}
|
||||||
rules={[
|
rules={[
|
||||||
@@ -305,14 +329,62 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
|
|||||||
<InputNumber />
|
<InputNumber />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("bodyshop.fields.rbac.jobs.checklist-view")}
|
label={t("bodyshop.fields.rbac.jobs.intake")}
|
||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
//message: t("general.validation.required"),
|
//message: t("general.validation.required"),
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
name={["md_rbac", "jobs:checklist-view"]}
|
name={["md_rbac", "jobs:intake"]}
|
||||||
|
>
|
||||||
|
<InputNumber />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.rbac.jobs.list-active")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
name={["md_rbac", "jobs:list-active"]}
|
||||||
|
>
|
||||||
|
<InputNumber />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.rbac.jobs.list-all")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
name={["md_rbac", "jobs:list-all"]}
|
||||||
|
>
|
||||||
|
<InputNumber />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.rbac.jobs.list-ready")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
name={["md_rbac", "jobs:list-ready"]}
|
||||||
|
>
|
||||||
|
<InputNumber />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.rbac.jobs.partsqueue")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
name={["md_rbac", "jobs:partsqueue"]}
|
||||||
>
|
>
|
||||||
<InputNumber />
|
<InputNumber />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
@@ -329,74 +401,14 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
|
|||||||
<InputNumber />
|
<InputNumber />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("bodyshop.fields.rbac.bills.enter")}
|
label={t("bodyshop.fields.rbac.owners.detail")}
|
||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
//message: t("general.validation.required"),
|
//message: t("general.validation.required"),
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
name={["md_rbac", "bills:enter"]}
|
name={["md_rbac", "owners:detail"]}
|
||||||
>
|
|
||||||
<InputNumber />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t("bodyshop.fields.rbac.bills.delete")}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
name={["md_rbac", "bills:delete"]}
|
|
||||||
>
|
|
||||||
<InputNumber />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t("bodyshop.fields.rbac.bills.reexport")}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
name={["md_rbac", "bills:reexport"]}
|
|
||||||
>
|
|
||||||
<InputNumber />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t("bodyshop.fields.rbac.bills.view")}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
name={["md_rbac", "bills:view"]}
|
|
||||||
>
|
|
||||||
<InputNumber />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t("bodyshop.fields.rbac.bills.list")}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
name={["md_rbac", "bills:list"]}
|
|
||||||
>
|
|
||||||
<InputNumber />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t("bodyshop.fields.rbac.employees.page")}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
name={["md_rbac", "employees:page"]}
|
|
||||||
>
|
>
|
||||||
<InputNumber />
|
<InputNumber />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
@@ -412,18 +424,6 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
|
|||||||
>
|
>
|
||||||
<InputNumber />
|
<InputNumber />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
|
||||||
label={t("bodyshop.fields.rbac.owners.detail")}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
name={["md_rbac", "owners:detail"]}
|
|
||||||
>
|
|
||||||
<InputNumber />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("bodyshop.fields.rbac.payments.enter")}
|
label={t("bodyshop.fields.rbac.payments.enter")}
|
||||||
rules={[
|
rules={[
|
||||||
@@ -448,6 +448,30 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
|
|||||||
>
|
>
|
||||||
<InputNumber />
|
<InputNumber />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.rbac.phonebook.edit")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
name={["md_rbac", "phonebook:edit"]}
|
||||||
|
>
|
||||||
|
<InputNumber />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.rbac.phonebook.view")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
name={["md_rbac", "phonebook:view"]}
|
||||||
|
>
|
||||||
|
<InputNumber />
|
||||||
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("bodyshop.fields.rbac.production.board")}
|
label={t("bodyshop.fields.rbac.production.board")}
|
||||||
rules={[
|
rules={[
|
||||||
@@ -509,38 +533,14 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
|
|||||||
<InputNumber />
|
<InputNumber />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("bodyshop.fields.rbac.timetickets.edit")}
|
label={t("bodyshop.fields.rbac.shop.config")}
|
||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
//message: t("general.validation.required"),
|
//message: t("general.validation.required"),
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
name={["md_rbac", "timetickets:edit"]}
|
name={["md_rbac", "shop:config"]}
|
||||||
>
|
|
||||||
<InputNumber />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t("bodyshop.fields.rbac.timetickets.shiftedit")}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
name={["md_rbac", "timetickets:shiftedit"]}
|
|
||||||
>
|
|
||||||
<InputNumber />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t("bodyshop.fields.rbac.shop.vendors")}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
name={["md_rbac", "shop:vendors"]}
|
|
||||||
>
|
>
|
||||||
<InputNumber />
|
<InputNumber />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
@@ -556,18 +556,6 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
|
|||||||
>
|
>
|
||||||
<InputNumber />
|
<InputNumber />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
|
||||||
label={t("bodyshop.fields.rbac.shop.config")}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
name={["md_rbac", "shop:config"]}
|
|
||||||
>
|
|
||||||
<InputNumber />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("bodyshop.fields.rbac.shop.rbac")}
|
label={t("bodyshop.fields.rbac.shop.rbac")}
|
||||||
rules={[
|
rules={[
|
||||||
@@ -580,6 +568,18 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
|
|||||||
>
|
>
|
||||||
<InputNumber />
|
<InputNumber />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.rbac.shop.reportcenter")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
name={["md_rbac", "shop:reportcenter"]}
|
||||||
|
>
|
||||||
|
<InputNumber />
|
||||||
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("bodyshop.fields.rbac.shop.templates")}
|
label={t("bodyshop.fields.rbac.shop.templates")}
|
||||||
rules={[
|
rules={[
|
||||||
@@ -592,6 +592,42 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
|
|||||||
>
|
>
|
||||||
<InputNumber />
|
<InputNumber />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.rbac.shop.vendors")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
name={["md_rbac", "shop:vendors"]}
|
||||||
|
>
|
||||||
|
<InputNumber />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.rbac.temporarydocs.view")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
name={["md_rbac", "temporarydocs:view"]}
|
||||||
|
>
|
||||||
|
<InputNumber />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.rbac.timetickets.edit")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
name={["md_rbac", "timetickets:edit"]}
|
||||||
|
>
|
||||||
|
<InputNumber />
|
||||||
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("bodyshop.fields.rbac.timetickets.enter")}
|
label={t("bodyshop.fields.rbac.timetickets.enter")}
|
||||||
rules={[
|
rules={[
|
||||||
@@ -616,6 +652,18 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
|
|||||||
>
|
>
|
||||||
<InputNumber />
|
<InputNumber />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.rbac.timetickets.shiftedit")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
name={["md_rbac", "timetickets:shiftedit"]}
|
||||||
|
>
|
||||||
|
<InputNumber />
|
||||||
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("bodyshop.fields.rbac.users.editaccess")}
|
label={t("bodyshop.fields.rbac.users.editaccess")}
|
||||||
rules={[
|
rules={[
|
||||||
@@ -628,42 +676,6 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
|
|||||||
>
|
>
|
||||||
<InputNumber />
|
<InputNumber />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
|
||||||
label={t("bodyshop.fields.rbac.temporarydocs.view")}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
name={["md_rbac", "temporarydocs:view"]}
|
|
||||||
>
|
|
||||||
<InputNumber />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t("bodyshop.fields.rbac.phonebook.view")}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
name={["md_rbac", "phonebook:view"]}
|
|
||||||
>
|
|
||||||
<InputNumber />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t("bodyshop.fields.rbac.phonebook.edit")}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
name={["md_rbac", "phonebook:edit"]}
|
|
||||||
>
|
|
||||||
<InputNumber />
|
|
||||||
</Form.Item>
|
|
||||||
{Simple_Inventory.treatment === "on" && (
|
{Simple_Inventory.treatment === "on" && (
|
||||||
<>
|
<>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
|
|||||||
@@ -166,9 +166,6 @@ export function TechClockOffButton({
|
|||||||
},
|
},
|
||||||
({ getFieldValue }) => ({
|
({ getFieldValue }) => ({
|
||||||
validator(rule, value) {
|
validator(rule, value) {
|
||||||
console.log(
|
|
||||||
bodyshop.tt_enforce_hours_for_tech_console
|
|
||||||
);
|
|
||||||
if (!bodyshop.tt_enforce_hours_for_tech_console) {
|
if (!bodyshop.tt_enforce_hours_for_tech_console) {
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { Button, Form, Input } from "antd";
|
import { Button, Form, Input } from "antd";
|
||||||
import React from "react";
|
import React, { useEffect } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
|
import { Redirect } from "react-router-dom";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import { techLoginStart } from "../../redux/tech/tech.actions";
|
import { techLoginStart } from "../../redux/tech/tech.actions";
|
||||||
import {
|
import {
|
||||||
@@ -11,7 +12,6 @@ import {
|
|||||||
} from "../../redux/tech/tech.selectors";
|
} from "../../redux/tech/tech.selectors";
|
||||||
import AlertComponent from "../alert/alert.component";
|
import AlertComponent from "../alert/alert.component";
|
||||||
import "./tech-login.styles.scss";
|
import "./tech-login.styles.scss";
|
||||||
import { Redirect } from "react-router-dom";
|
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
technician: selectTechnician,
|
technician: selectTechnician,
|
||||||
@@ -35,6 +35,10 @@ export function TechLogin({
|
|||||||
techLoginStart(values);
|
techLoginStart(values);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
document.title = t("titles.techconsole");
|
||||||
|
}, [t]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="tech-login-container">
|
<div className="tech-login-container">
|
||||||
{technician ? <Redirect to={`/tech/joblookup`} /> : null}
|
{technician ? <Redirect to={`/tech/joblookup`} /> : null}
|
||||||
|
|||||||
@@ -1,17 +1,21 @@
|
|||||||
import { onError } from "@apollo/client/link/error";
|
import { onError } from "@apollo/client/link/error";
|
||||||
//https://stackoverflow.com/questions/57163454/refreshing-a-token-with-apollo-client-firebase-auth
|
//https://stackoverflow.com/questions/57163454/refreshing-a-token-with-apollo-client-firebase-auth
|
||||||
|
import * as Sentry from "@sentry/react";
|
||||||
|
|
||||||
const errorLink = onError(
|
const errorLink = onError(
|
||||||
({ graphQLErrors, networkError, operation, forward }) => {
|
({ graphQLErrors, networkError, operation, forward }) => {
|
||||||
if (graphQLErrors)
|
if (graphQLErrors) {
|
||||||
graphQLErrors.forEach(({ message, locations, path }) =>
|
graphQLErrors.forEach(({ message, locations, path }) => {
|
||||||
console.log(
|
console.log(
|
||||||
`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`
|
`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`
|
||||||
)
|
);
|
||||||
);
|
Sentry.captureException({ message, locations, path });
|
||||||
|
});
|
||||||
|
}
|
||||||
if (networkError)
|
if (networkError)
|
||||||
console.log(`[Network error]: ${JSON.stringify(networkError)}`);
|
console.log(`[Network error]: ${JSON.stringify(networkError)}`);
|
||||||
console.log(operation.getContext());
|
console.log(operation.getContext());
|
||||||
|
return forward(operation);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ export const QUERY_AVAILABLE_CC = gql`
|
|||||||
]
|
]
|
||||||
status: { _eq: "courtesycars.status.in" }
|
status: { _eq: "courtesycars.status.in" }
|
||||||
}
|
}
|
||||||
|
order_by: { fleetnumber: asc }
|
||||||
) {
|
) {
|
||||||
color
|
color
|
||||||
dailycost
|
dailycost
|
||||||
@@ -29,16 +30,17 @@ export const QUERY_AVAILABLE_CC = gql`
|
|||||||
fleetnumber
|
fleetnumber
|
||||||
fuel
|
fuel
|
||||||
id
|
id
|
||||||
|
insuranceexpires
|
||||||
make
|
make
|
||||||
model
|
|
||||||
plate
|
|
||||||
status
|
|
||||||
year
|
|
||||||
dailycost
|
|
||||||
mileage
|
mileage
|
||||||
|
model
|
||||||
notes
|
notes
|
||||||
nextservicekm
|
nextservicekm
|
||||||
nextservicedate
|
nextservicedate
|
||||||
|
plate
|
||||||
|
readiness
|
||||||
|
status
|
||||||
|
year
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
@@ -57,7 +59,7 @@ export const CHECK_CC_FLEET_NUMBER = gql`
|
|||||||
`;
|
`;
|
||||||
export const QUERY_ALL_CC = gql`
|
export const QUERY_ALL_CC = gql`
|
||||||
query QUERY_ALL_CC {
|
query QUERY_ALL_CC {
|
||||||
courtesycars {
|
courtesycars(order_by: { fleetnumber: asc }) {
|
||||||
color
|
color
|
||||||
created_at
|
created_at
|
||||||
dailycost
|
dailycost
|
||||||
@@ -68,19 +70,20 @@ export const QUERY_ALL_CC = gql`
|
|||||||
insuranceexpires
|
insuranceexpires
|
||||||
leaseenddate
|
leaseenddate
|
||||||
make
|
make
|
||||||
|
mileage
|
||||||
model
|
model
|
||||||
nextservicedate
|
nextservicedate
|
||||||
nextservicekm
|
nextservicekm
|
||||||
notes
|
notes
|
||||||
plate
|
plate
|
||||||
purchasedate
|
purchasedate
|
||||||
|
readiness
|
||||||
registrationexpires
|
registrationexpires
|
||||||
serviceenddate
|
serviceenddate
|
||||||
servicestartdate
|
servicestartdate
|
||||||
status
|
status
|
||||||
vin
|
vin
|
||||||
year
|
year
|
||||||
mileage
|
|
||||||
cccontracts(
|
cccontracts(
|
||||||
where: { status: { _eq: "contracts.status.out" } }
|
where: { status: { _eq: "contracts.status.out" } }
|
||||||
order_by: { contract_date: desc }
|
order_by: { contract_date: desc }
|
||||||
@@ -90,10 +93,10 @@ export const QUERY_ALL_CC = gql`
|
|||||||
scheduledreturn
|
scheduledreturn
|
||||||
job {
|
job {
|
||||||
id
|
id
|
||||||
ro_number
|
|
||||||
ownr_fn
|
ownr_fn
|
||||||
ownr_ln
|
ownr_ln
|
||||||
ownr_co_nm
|
ownr_co_nm
|
||||||
|
ro_number
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -119,19 +122,20 @@ export const QUERY_CC_BY_PK = gql`
|
|||||||
insuranceexpires
|
insuranceexpires
|
||||||
leaseenddate
|
leaseenddate
|
||||||
make
|
make
|
||||||
|
mileage
|
||||||
model
|
model
|
||||||
nextservicedate
|
nextservicedate
|
||||||
nextservicekm
|
nextservicekm
|
||||||
notes
|
notes
|
||||||
plate
|
plate
|
||||||
purchasedate
|
purchasedate
|
||||||
|
readiness
|
||||||
registrationexpires
|
registrationexpires
|
||||||
serviceenddate
|
serviceenddate
|
||||||
servicestartdate
|
servicestartdate
|
||||||
status
|
status
|
||||||
vin
|
vin
|
||||||
year
|
year
|
||||||
mileage
|
|
||||||
cccontracts_aggregate {
|
cccontracts_aggregate {
|
||||||
aggregate {
|
aggregate {
|
||||||
count(distinct: true)
|
count(distinct: true)
|
||||||
@@ -139,21 +143,20 @@ export const QUERY_CC_BY_PK = gql`
|
|||||||
}
|
}
|
||||||
cccontracts(offset: $offset, limit: $limit, order_by: $order) {
|
cccontracts(offset: $offset, limit: $limit, order_by: $order) {
|
||||||
agreementnumber
|
agreementnumber
|
||||||
|
driver_fn
|
||||||
|
driver_ln
|
||||||
id
|
id
|
||||||
status
|
|
||||||
start
|
|
||||||
scheduledreturn
|
|
||||||
kmstart
|
kmstart
|
||||||
kmend
|
kmend
|
||||||
driver_ln
|
scheduledreturn
|
||||||
driver_fn
|
start
|
||||||
|
status
|
||||||
job {
|
job {
|
||||||
ro_number
|
id
|
||||||
|
|
||||||
ownr_ln
|
ownr_ln
|
||||||
ownr_fn
|
ownr_fn
|
||||||
ownr_co_nm
|
ownr_co_nm
|
||||||
id
|
ro_number
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,19 +57,15 @@ export const INSERT_CSI = gql`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export const QUERY_CSI_RESPONSE_PAGINATED = gql`
|
export const QUERY_CSI_RESPONSE_PAGINATED = gql`
|
||||||
query QUERY_CSI_RESPONSE_PAGINATED(
|
query QUERY_CSI_RESPONSE_PAGINATED {
|
||||||
$offset: Int
|
csi(order_by: { completedon: desc_nulls_last }) {
|
||||||
$limit: Int
|
|
||||||
$order: [csi_order_by!]!
|
|
||||||
) {
|
|
||||||
csi(offset: $offset, limit: $limit, order_by: $order) {
|
|
||||||
id
|
id
|
||||||
completedon
|
completedon
|
||||||
job {
|
job {
|
||||||
ownr_fn
|
ownr_fn
|
||||||
ownr_ln
|
ownr_ln
|
||||||
|
ownerid
|
||||||
ro_number
|
ro_number
|
||||||
|
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -83,6 +79,7 @@ export const QUERY_CSI_RESPONSE_PAGINATED = gql`
|
|||||||
export const QUERY_CSI_RESPONSE_BY_PK = gql`
|
export const QUERY_CSI_RESPONSE_BY_PK = gql`
|
||||||
query QUERY_CSI_RESPONSE_BY_PK($id: uuid!) {
|
query QUERY_CSI_RESPONSE_BY_PK($id: uuid!) {
|
||||||
csi_by_pk(id: $id) {
|
csi_by_pk(id: $id) {
|
||||||
|
completedon
|
||||||
relateddata
|
relateddata
|
||||||
valid
|
valid
|
||||||
validuntil
|
validuntil
|
||||||
|
|||||||
@@ -3,11 +3,12 @@ import { gql } from "@apollo/client";
|
|||||||
export const QUERY_EMPLOYEES = gql`
|
export const QUERY_EMPLOYEES = gql`
|
||||||
query QUERY_EMPLOYEES {
|
query QUERY_EMPLOYEES {
|
||||||
employees(order_by: { employee_number: asc }) {
|
employees(order_by: { employee_number: asc }) {
|
||||||
last_name
|
active
|
||||||
id
|
employee_number
|
||||||
first_name
|
first_name
|
||||||
flat_rate
|
flat_rate
|
||||||
employee_number
|
id
|
||||||
|
last_name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ export const QUERY_ALL_ACTIVE_JOBS_PAGINATED = gql`
|
|||||||
$offset: Int
|
$offset: Int
|
||||||
$limit: Int
|
$limit: Int
|
||||||
$order: [jobs_order_by!]
|
$order: [jobs_order_by!]
|
||||||
$statuses: [String!]!,
|
$statuses: [String!]!
|
||||||
$isConverted: Boolean
|
$isConverted: Boolean
|
||||||
) {
|
) {
|
||||||
jobs(
|
jobs(
|
||||||
@@ -108,22 +108,19 @@ export const QUERY_ALL_ACTIVE_JOBS = gql`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export const QUERY_PARTS_QUEUE = gql`
|
export const QUERY_PARTS_QUEUE = gql`
|
||||||
query QUERY_PARTS_QUEUE(
|
query QUERY_PARTS_QUEUE($statuses: [String!]!, $offset: Int, $limit: Int) {
|
||||||
$statuses: [String!]!
|
|
||||||
$offset: Int
|
|
||||||
$limit: Int
|
|
||||||
$order: [jobs_order_by!]
|
|
||||||
) {
|
|
||||||
jobs_aggregate(where: { _and: [{ status: { _in: $statuses } }] }) {
|
jobs_aggregate(where: { _and: [{ status: { _in: $statuses } }] }) {
|
||||||
aggregate {
|
aggregate {
|
||||||
count(distinct: true)
|
count(distinct: true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
jobs(
|
jobs(
|
||||||
where: { _and: [{ status: { _in: $statuses }, converted: { _eq: true } }] }
|
where: {
|
||||||
|
_and: [{ status: { _in: $statuses }, converted: { _eq: true } }]
|
||||||
|
}
|
||||||
offset: $offset
|
offset: $offset
|
||||||
limit: $limit
|
limit: $limit
|
||||||
order_by: $order
|
order_by: { ro_number: desc }
|
||||||
) {
|
) {
|
||||||
ownr_fn
|
ownr_fn
|
||||||
ownr_ln
|
ownr_ln
|
||||||
@@ -140,7 +137,9 @@ export const QUERY_PARTS_QUEUE = gql`
|
|||||||
v_color
|
v_color
|
||||||
vehicleid
|
vehicleid
|
||||||
scheduled_in
|
scheduled_in
|
||||||
|
scheduled_completion
|
||||||
id
|
id
|
||||||
|
ins_co_nm
|
||||||
clm_no
|
clm_no
|
||||||
ro_number
|
ro_number
|
||||||
status
|
status
|
||||||
@@ -336,6 +335,7 @@ export const QUERY_JOBS_IN_PRODUCTION = gql`
|
|||||||
category
|
category
|
||||||
iouparent
|
iouparent
|
||||||
ro_number
|
ro_number
|
||||||
|
ownerid
|
||||||
ownr_fn
|
ownr_fn
|
||||||
ownr_ln
|
ownr_ln
|
||||||
ownr_co_nm
|
ownr_co_nm
|
||||||
@@ -364,6 +364,8 @@ export const QUERY_JOBS_IN_PRODUCTION = gql`
|
|||||||
employee_refinish
|
employee_refinish
|
||||||
employee_prep
|
employee_prep
|
||||||
employee_csr
|
employee_csr
|
||||||
|
est_ct_fn
|
||||||
|
est_ct_ln
|
||||||
suspended
|
suspended
|
||||||
date_repairstarted
|
date_repairstarted
|
||||||
joblines_status {
|
joblines_status {
|
||||||
@@ -540,147 +542,166 @@ export const QUERY_JOB_COSTING_DETAILS = gql`
|
|||||||
export const GET_JOB_BY_PK = gql`
|
export const GET_JOB_BY_PK = gql`
|
||||||
query GET_JOB_BY_PK($id: uuid!) {
|
query GET_JOB_BY_PK($id: uuid!) {
|
||||||
jobs_by_pk(id: $id) {
|
jobs_by_pk(id: $id) {
|
||||||
updated_at
|
actual_completion
|
||||||
|
actual_delivery
|
||||||
|
actual_in
|
||||||
|
adjustment_bottom_line
|
||||||
|
area_of_damage
|
||||||
|
auto_add_ats
|
||||||
|
available_jobs {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
alt_transport
|
||||||
|
ca_bc_pvrt
|
||||||
|
ca_customer_gst
|
||||||
|
ca_gst_registrant
|
||||||
|
category
|
||||||
|
cccontracts {
|
||||||
|
agreementnumber
|
||||||
|
courtesycar {
|
||||||
|
fleetnumber
|
||||||
|
id
|
||||||
|
make
|
||||||
|
model
|
||||||
|
plate
|
||||||
|
year
|
||||||
|
}
|
||||||
|
id
|
||||||
|
scheduledreturn
|
||||||
|
start
|
||||||
|
status
|
||||||
|
}
|
||||||
|
cieca_ttl
|
||||||
|
class
|
||||||
|
clm_no
|
||||||
|
clm_total
|
||||||
|
comment
|
||||||
|
converted
|
||||||
|
csiinvites {
|
||||||
|
completedon
|
||||||
|
id
|
||||||
|
}
|
||||||
|
date_estimated
|
||||||
|
date_exported
|
||||||
|
date_invoiced
|
||||||
|
date_last_contacted
|
||||||
|
date_lost_sale
|
||||||
|
date_next_contact
|
||||||
|
date_open
|
||||||
|
date_rentalresp
|
||||||
|
date_repairstarted
|
||||||
|
date_scheduled
|
||||||
|
date_towin
|
||||||
|
date_void
|
||||||
|
ded_amt
|
||||||
|
ded_note
|
||||||
|
ded_status
|
||||||
|
deliverchecklist
|
||||||
|
depreciation_taxes
|
||||||
|
driveable
|
||||||
|
employee_body
|
||||||
employee_body_rel {
|
employee_body_rel {
|
||||||
id
|
id
|
||||||
first_name
|
first_name
|
||||||
last_name
|
last_name
|
||||||
}
|
}
|
||||||
employee_refinish_rel {
|
employee_csr
|
||||||
id
|
|
||||||
first_name
|
|
||||||
last_name
|
|
||||||
}
|
|
||||||
employee_prep_rel {
|
|
||||||
id
|
|
||||||
first_name
|
|
||||||
last_name
|
|
||||||
}
|
|
||||||
employee_csr_rel {
|
employee_csr_rel {
|
||||||
id
|
id
|
||||||
first_name
|
first_name
|
||||||
last_name
|
last_name
|
||||||
}
|
}
|
||||||
employee_csr
|
|
||||||
employee_prep
|
employee_prep
|
||||||
|
employee_prep_rel {
|
||||||
|
id
|
||||||
|
first_name
|
||||||
|
last_name
|
||||||
|
}
|
||||||
employee_refinish
|
employee_refinish
|
||||||
employee_body
|
employee_refinish_rel {
|
||||||
alt_transport
|
id
|
||||||
intakechecklist
|
first_name
|
||||||
invoice_final_note
|
last_name
|
||||||
comment
|
}
|
||||||
loss_desc
|
est_co_nm
|
||||||
kmin
|
est_ct_fn
|
||||||
kmout
|
est_ct_ln
|
||||||
referral_source
|
est_ea
|
||||||
referral_source_extra
|
est_ph1
|
||||||
unit_number
|
federal_tax_rate
|
||||||
po_number
|
id
|
||||||
special_coverage_policy
|
|
||||||
scheduled_delivery
|
|
||||||
converted
|
|
||||||
lbr_adjustments
|
|
||||||
ro_number
|
|
||||||
po_number
|
|
||||||
clm_total
|
|
||||||
inproduction
|
inproduction
|
||||||
vehicleid
|
|
||||||
plate_no
|
|
||||||
plate_st
|
|
||||||
v_vin
|
|
||||||
v_model_yr
|
|
||||||
v_model_desc
|
|
||||||
v_make_desc
|
|
||||||
v_color
|
|
||||||
vehicleid
|
|
||||||
driveable
|
|
||||||
towin
|
|
||||||
loss_of_use
|
|
||||||
lost_sale_reason
|
|
||||||
vehicle {
|
|
||||||
id
|
|
||||||
plate_no
|
|
||||||
plate_st
|
|
||||||
v_vin
|
|
||||||
v_model_yr
|
|
||||||
v_model_desc
|
|
||||||
v_make_desc
|
|
||||||
v_color
|
|
||||||
notes
|
|
||||||
v_paint_codes
|
|
||||||
jobs {
|
|
||||||
id
|
|
||||||
ro_number
|
|
||||||
status
|
|
||||||
clm_no
|
|
||||||
}
|
|
||||||
}
|
|
||||||
available_jobs {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
ins_co_id
|
|
||||||
policy_no
|
|
||||||
loss_date
|
|
||||||
clm_no
|
|
||||||
area_of_damage
|
|
||||||
ins_co_nm
|
|
||||||
ins_addr1
|
ins_addr1
|
||||||
ins_city
|
ins_city
|
||||||
|
ins_co_id
|
||||||
|
ins_co_nm
|
||||||
ins_ct_ln
|
ins_ct_ln
|
||||||
ins_ct_fn
|
ins_ct_fn
|
||||||
ins_ea
|
ins_ea
|
||||||
ins_ph1
|
ins_ph1
|
||||||
est_co_nm
|
intakechecklist
|
||||||
est_ct_fn
|
invoice_final_note
|
||||||
est_ct_ln
|
|
||||||
est_ph1
|
|
||||||
est_ea
|
|
||||||
selling_dealer
|
|
||||||
servicing_dealer
|
|
||||||
selling_dealer_contact
|
|
||||||
servicing_dealer_contact
|
|
||||||
regie_number
|
|
||||||
scheduled_completion
|
|
||||||
id
|
|
||||||
ded_amt
|
|
||||||
ded_status
|
|
||||||
depreciation_taxes
|
|
||||||
other_amount_payable
|
|
||||||
towing_payable
|
|
||||||
storage_payable
|
|
||||||
adjustment_bottom_line
|
|
||||||
federal_tax_rate
|
|
||||||
state_tax_rate
|
|
||||||
local_tax_rate
|
|
||||||
tax_tow_rt
|
|
||||||
tax_str_rt
|
|
||||||
tax_paint_mat_rt
|
|
||||||
tax_shop_mat_rt
|
|
||||||
tax_sub_rt
|
|
||||||
tax_lbr_rt
|
|
||||||
tax_levies_rt
|
|
||||||
parts_tax_rates
|
|
||||||
job_totals
|
|
||||||
ownr_fn
|
|
||||||
ownr_ln
|
|
||||||
ownr_co_nm
|
|
||||||
ownr_ea
|
|
||||||
ownr_addr1
|
|
||||||
ownr_addr2
|
|
||||||
ownr_city
|
|
||||||
ownr_st
|
|
||||||
ownr_zip
|
|
||||||
ownr_ctry
|
|
||||||
ownr_ph1
|
|
||||||
ownr_ph2
|
|
||||||
production_vars
|
|
||||||
ca_gst_registrant
|
|
||||||
ownerid
|
|
||||||
ded_note
|
|
||||||
materials
|
|
||||||
auto_add_ats
|
|
||||||
rate_ats
|
|
||||||
iouparent
|
iouparent
|
||||||
|
job_totals
|
||||||
|
joblines(where: { removed: { _eq: false } }, order_by: { line_no: asc }) {
|
||||||
|
act_price
|
||||||
|
ah_detail_line
|
||||||
|
alt_partm
|
||||||
|
alt_partno
|
||||||
|
billlines(limit: 1, order_by: { bill: { date: desc } }) {
|
||||||
|
actual_cost
|
||||||
|
actual_price
|
||||||
|
bill {
|
||||||
|
id
|
||||||
|
invoice_number
|
||||||
|
vendor {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
joblineid
|
||||||
|
id
|
||||||
|
quantity
|
||||||
|
}
|
||||||
|
convertedtolbr
|
||||||
|
critical
|
||||||
|
db_hrs
|
||||||
|
db_price
|
||||||
|
db_ref
|
||||||
|
id
|
||||||
|
ioucreated
|
||||||
|
lbr_amt
|
||||||
|
lbr_op
|
||||||
|
line_desc
|
||||||
|
line_ind
|
||||||
|
line_no
|
||||||
|
line_ref
|
||||||
|
location
|
||||||
|
manual_line
|
||||||
|
mod_lb_hrs
|
||||||
|
mod_lbr_ty
|
||||||
|
notes
|
||||||
|
oem_partno
|
||||||
|
op_code_desc
|
||||||
|
part_qty
|
||||||
|
part_type
|
||||||
|
prt_dsmk_m
|
||||||
|
prt_dsmk_p
|
||||||
|
status
|
||||||
|
tax_part
|
||||||
|
unq_seq
|
||||||
|
}
|
||||||
|
kmin
|
||||||
|
kmout
|
||||||
|
labor_rate_desc
|
||||||
|
lbr_adjustments
|
||||||
|
local_tax_rate
|
||||||
|
loss_date
|
||||||
|
loss_desc
|
||||||
|
loss_of_use
|
||||||
|
lost_sale_reason
|
||||||
|
materials
|
||||||
|
other_amount_payable
|
||||||
owner {
|
owner {
|
||||||
id
|
id
|
||||||
ownr_fn
|
ownr_fn
|
||||||
@@ -697,7 +718,40 @@ export const GET_JOB_BY_PK = gql`
|
|||||||
ownr_ph2
|
ownr_ph2
|
||||||
tax_number
|
tax_number
|
||||||
}
|
}
|
||||||
labor_rate_desc
|
owner_owing
|
||||||
|
ownerid
|
||||||
|
ownr_addr1
|
||||||
|
ownr_addr2
|
||||||
|
ownr_ctry
|
||||||
|
ownr_city
|
||||||
|
ownr_co_nm
|
||||||
|
ownr_ea
|
||||||
|
ownr_fn
|
||||||
|
ownr_ln
|
||||||
|
ownr_ph1
|
||||||
|
ownr_ph2
|
||||||
|
ownr_st
|
||||||
|
ownr_zip
|
||||||
|
parts_tax_rates
|
||||||
|
payments {
|
||||||
|
amount
|
||||||
|
created_at
|
||||||
|
date
|
||||||
|
exportedat
|
||||||
|
id
|
||||||
|
jobid
|
||||||
|
memo
|
||||||
|
payer
|
||||||
|
paymentnum
|
||||||
|
transactionid
|
||||||
|
type
|
||||||
|
}
|
||||||
|
plate_no
|
||||||
|
plate_st
|
||||||
|
po_number
|
||||||
|
policy_no
|
||||||
|
production_vars
|
||||||
|
rate_ats
|
||||||
rate_la1
|
rate_la1
|
||||||
rate_la2
|
rate_la2
|
||||||
rate_la3
|
rate_la3
|
||||||
@@ -721,121 +775,64 @@ export const GET_JOB_BY_PK = gql`
|
|||||||
rate_mapa
|
rate_mapa
|
||||||
rate_mash
|
rate_mash
|
||||||
rate_matd
|
rate_matd
|
||||||
actual_in
|
regie_number
|
||||||
federal_tax_rate
|
referral_source
|
||||||
local_tax_rate
|
referral_source_extra
|
||||||
state_tax_rate
|
remove_from_ar
|
||||||
|
ro_number
|
||||||
scheduled_completion
|
scheduled_completion
|
||||||
scheduled_in
|
|
||||||
actual_completion
|
|
||||||
scheduled_delivery
|
scheduled_delivery
|
||||||
actual_delivery
|
scheduled_in
|
||||||
date_estimated
|
selling_dealer
|
||||||
date_open
|
servicing_dealer
|
||||||
date_scheduled
|
selling_dealer_contact
|
||||||
date_invoiced
|
servicing_dealer_contact
|
||||||
date_last_contacted
|
special_coverage_policy
|
||||||
date_lost_sale
|
state_tax_rate
|
||||||
date_next_contact
|
|
||||||
date_towin
|
|
||||||
date_rentalresp
|
|
||||||
date_exported
|
|
||||||
date_repairstarted
|
|
||||||
date_void
|
|
||||||
status
|
status
|
||||||
owner_owing
|
storage_payable
|
||||||
tax_registration_number
|
|
||||||
class
|
|
||||||
category
|
|
||||||
deliverchecklist
|
|
||||||
voided
|
|
||||||
ca_bc_pvrt
|
|
||||||
ca_customer_gst
|
|
||||||
suspended
|
suspended
|
||||||
joblines(where: { removed: { _eq: false } }, order_by: { line_no: asc }) {
|
tax_lbr_rt
|
||||||
|
tax_levies_rt
|
||||||
|
tax_paint_mat_rt
|
||||||
|
tax_registration_number
|
||||||
|
tax_shop_mat_rt
|
||||||
|
tax_str_rt
|
||||||
|
tax_sub_rt
|
||||||
|
tax_tow_rt
|
||||||
|
towin
|
||||||
|
towing_payable
|
||||||
|
unit_number
|
||||||
|
updated_at
|
||||||
|
v_vin
|
||||||
|
v_model_yr
|
||||||
|
v_model_desc
|
||||||
|
v_make_desc
|
||||||
|
v_color
|
||||||
|
vehicleid
|
||||||
|
vehicle {
|
||||||
id
|
id
|
||||||
alt_partm
|
jobs {
|
||||||
line_no
|
clm_no
|
||||||
unq_seq
|
id
|
||||||
line_ind
|
ro_number
|
||||||
line_desc
|
status
|
||||||
line_ref
|
}
|
||||||
part_type
|
|
||||||
oem_partno
|
|
||||||
alt_partno
|
|
||||||
db_price
|
|
||||||
act_price
|
|
||||||
part_qty
|
|
||||||
mod_lbr_ty
|
|
||||||
db_hrs
|
|
||||||
mod_lb_hrs
|
|
||||||
lbr_op
|
|
||||||
lbr_amt
|
|
||||||
op_code_desc
|
|
||||||
status
|
|
||||||
notes
|
notes
|
||||||
location
|
plate_no
|
||||||
tax_part
|
plate_st
|
||||||
db_ref
|
v_color
|
||||||
manual_line
|
v_make_desc
|
||||||
prt_dsmk_p
|
v_model_desc
|
||||||
prt_dsmk_m
|
v_model_yr
|
||||||
ioucreated
|
v_paint_codes
|
||||||
convertedtolbr
|
v_vin
|
||||||
ah_detail_line
|
|
||||||
critical
|
|
||||||
billlines(limit: 1, order_by: { bill: { date: desc } }) {
|
|
||||||
id
|
|
||||||
quantity
|
|
||||||
actual_cost
|
|
||||||
actual_price
|
|
||||||
joblineid
|
|
||||||
bill {
|
|
||||||
id
|
|
||||||
invoice_number
|
|
||||||
vendor {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
payments {
|
|
||||||
id
|
|
||||||
jobid
|
|
||||||
amount
|
|
||||||
payer
|
|
||||||
paymentnum
|
|
||||||
created_at
|
|
||||||
transactionid
|
|
||||||
memo
|
|
||||||
date
|
|
||||||
type
|
|
||||||
exportedat
|
|
||||||
}
|
|
||||||
cccontracts {
|
|
||||||
id
|
|
||||||
status
|
|
||||||
start
|
|
||||||
scheduledreturn
|
|
||||||
agreementnumber
|
|
||||||
courtesycar {
|
|
||||||
id
|
|
||||||
make
|
|
||||||
model
|
|
||||||
year
|
|
||||||
plate
|
|
||||||
fleetnumber
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cieca_ttl
|
|
||||||
csiinvites {
|
|
||||||
id
|
|
||||||
completedon
|
|
||||||
}
|
}
|
||||||
|
voided
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const GET_JOB_RECONCILIATION_BY_PK = gql`
|
export const GET_JOB_RECONCILIATION_BY_PK = gql`
|
||||||
query GET_JOB_RECONCILIATION_BY_PK($id: uuid!) {
|
query GET_JOB_RECONCILIATION_BY_PK($id: uuid!) {
|
||||||
bills(where: { jobid: { _eq: $id } }) {
|
bills(where: { jobid: { _eq: $id } }) {
|
||||||
@@ -900,6 +897,7 @@ export const GET_JOB_RECONCILIATION_BY_PK = gql`
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const QUERY_JOB_CARD_DETAILS = gql`
|
export const QUERY_JOB_CARD_DETAILS = gql`
|
||||||
query QUERY_JOB_CARD_DETAILS($id: uuid!) {
|
query QUERY_JOB_CARD_DETAILS($id: uuid!) {
|
||||||
jobs_by_pk(id: $id) {
|
jobs_by_pk(id: $id) {
|
||||||
@@ -2220,3 +2218,280 @@ export const GET_JOB_LINE_ORDERS = gql`
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const UPDATE_REMOVE_FROM_AR = gql`
|
||||||
|
mutation UPDATE_REMOVE_FROM_AR($jobId: uuid!, $remove_from_ar: Boolean!) {
|
||||||
|
update_jobs_by_pk(
|
||||||
|
pk_columns: { id: $jobId }
|
||||||
|
_set: { remove_from_ar: $remove_from_ar }
|
||||||
|
) {
|
||||||
|
id
|
||||||
|
remove_from_ar
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const UNVOID_JOB = gql`
|
||||||
|
mutation UNVOID_JOB(
|
||||||
|
$jobId: uuid!
|
||||||
|
$default_imported: String!
|
||||||
|
$currentUserEmail: String!
|
||||||
|
$text: String!
|
||||||
|
) {
|
||||||
|
update_jobs_by_pk(
|
||||||
|
pk_columns: { id: $jobId }
|
||||||
|
_set: { voided: false, status: $default_imported, date_void: null }
|
||||||
|
) {
|
||||||
|
id
|
||||||
|
date_void
|
||||||
|
voided
|
||||||
|
status
|
||||||
|
}
|
||||||
|
insert_notes(
|
||||||
|
objects: {
|
||||||
|
jobid: $jobId
|
||||||
|
audit: true
|
||||||
|
created_by: $currentUserEmail
|
||||||
|
text: $text
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
returning {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const DELETE_INTAKE_CHECKLIST = gql`
|
||||||
|
mutation DELETE_INTAKE($jobId: uuid!) {
|
||||||
|
update_jobs_by_pk(
|
||||||
|
pk_columns: { id: $jobId }
|
||||||
|
_set: { intakechecklist: null }
|
||||||
|
) {
|
||||||
|
id
|
||||||
|
intakechecklist
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const DELETE_DELIVERY_CHECKLIST = gql`
|
||||||
|
mutation DELETE_DELIVERY($jobId: uuid!) {
|
||||||
|
update_jobs_by_pk(
|
||||||
|
pk_columns: { id: $jobId }
|
||||||
|
_set: { deliverchecklist: null }
|
||||||
|
) {
|
||||||
|
id
|
||||||
|
deliverchecklist
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const MARK_JOB_FOR_REEXPORT = gql`
|
||||||
|
mutation MARK_JOB_FOR_REEXPORT($jobId: uuid!, $default_invoiced: String!) {
|
||||||
|
update_jobs_by_pk(
|
||||||
|
pk_columns: { id: $jobId }
|
||||||
|
_set: { date_exported: null, status: $default_invoiced }
|
||||||
|
) {
|
||||||
|
id
|
||||||
|
date_exported
|
||||||
|
status
|
||||||
|
date_invoiced
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const MARK_JOB_AS_EXPORTED = gql`
|
||||||
|
mutation MARK_JOB_AS_EXPORTED(
|
||||||
|
$jobId: uuid!
|
||||||
|
$date_exported: timestamptz!
|
||||||
|
$default_exported: String!
|
||||||
|
) {
|
||||||
|
update_jobs_by_pk(
|
||||||
|
pk_columns: { id: $jobId }
|
||||||
|
_set: { date_exported: $date_exported, status: $default_exported }
|
||||||
|
) {
|
||||||
|
id
|
||||||
|
date_exported
|
||||||
|
date_invoiced
|
||||||
|
status
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const MARK_JOB_AS_UNINVOICED = gql`
|
||||||
|
mutation MARK_JOB_AS_UNINVOICED($jobId: uuid!, $default_delivered: String!) {
|
||||||
|
update_jobs_by_pk(
|
||||||
|
pk_columns: { id: $jobId }
|
||||||
|
_set: {
|
||||||
|
date_exported: null
|
||||||
|
date_invoiced: null
|
||||||
|
status: $default_delivered
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
id
|
||||||
|
date_exported
|
||||||
|
date_invoiced
|
||||||
|
status
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const QUERY_PARTS_QUEUE_CARD_DETAILS = gql`
|
||||||
|
query QUERY_JOB_CARD_DETAILS($id: uuid!) {
|
||||||
|
jobs_by_pk(id: $id) {
|
||||||
|
actual_completion
|
||||||
|
actual_delivery
|
||||||
|
actual_in
|
||||||
|
alt_transport
|
||||||
|
available_jobs {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
area_of_damage
|
||||||
|
ca_gst_registrant
|
||||||
|
cccontracts {
|
||||||
|
agreementnumber
|
||||||
|
courtesycar {
|
||||||
|
id
|
||||||
|
make
|
||||||
|
model
|
||||||
|
year
|
||||||
|
plate
|
||||||
|
fleetnumber
|
||||||
|
}
|
||||||
|
id
|
||||||
|
scheduledreturn
|
||||||
|
start
|
||||||
|
status
|
||||||
|
}
|
||||||
|
clm_no
|
||||||
|
clm_total
|
||||||
|
comment
|
||||||
|
date_estimated
|
||||||
|
date_exported
|
||||||
|
date_invoiced
|
||||||
|
date_last_contacted
|
||||||
|
date_next_contact
|
||||||
|
date_open
|
||||||
|
date_repairstarted
|
||||||
|
date_scheduled
|
||||||
|
ded_amt
|
||||||
|
employee_body
|
||||||
|
employee_body_rel {
|
||||||
|
id
|
||||||
|
first_name
|
||||||
|
last_name
|
||||||
|
}
|
||||||
|
employee_csr
|
||||||
|
employee_csr_rel {
|
||||||
|
id
|
||||||
|
first_name
|
||||||
|
last_name
|
||||||
|
}
|
||||||
|
employee_prep
|
||||||
|
employee_prep_rel {
|
||||||
|
id
|
||||||
|
first_name
|
||||||
|
last_name
|
||||||
|
}
|
||||||
|
employee_refinish
|
||||||
|
employee_refinish_rel {
|
||||||
|
id
|
||||||
|
first_name
|
||||||
|
last_name
|
||||||
|
}
|
||||||
|
est_co_nm
|
||||||
|
est_ct_fn
|
||||||
|
est_ct_ln
|
||||||
|
est_ea
|
||||||
|
est_ph1
|
||||||
|
id
|
||||||
|
ins_co_nm
|
||||||
|
ins_ct_fn
|
||||||
|
ins_ct_ln
|
||||||
|
ins_ea
|
||||||
|
ins_ph1
|
||||||
|
inproduction
|
||||||
|
job_totals
|
||||||
|
joblines(
|
||||||
|
order_by: { line_no: asc }
|
||||||
|
where: {
|
||||||
|
part_type: {
|
||||||
|
_in: [
|
||||||
|
"PAN"
|
||||||
|
"PAC"
|
||||||
|
"PAR"
|
||||||
|
"PAL"
|
||||||
|
"PAA"
|
||||||
|
"PAM"
|
||||||
|
"PAP"
|
||||||
|
"PAG"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
removed: { _eq: false }
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
act_price
|
||||||
|
alt_partno
|
||||||
|
db_ref
|
||||||
|
id
|
||||||
|
line_desc
|
||||||
|
line_no
|
||||||
|
location
|
||||||
|
mod_lbr_ty
|
||||||
|
mod_lb_hrs
|
||||||
|
oem_partno
|
||||||
|
part_qty
|
||||||
|
part_type
|
||||||
|
prt_dsmk_m
|
||||||
|
status
|
||||||
|
}
|
||||||
|
lbr_adjustments
|
||||||
|
ownr_co_nm
|
||||||
|
ownr_ea
|
||||||
|
ownr_fn
|
||||||
|
ownr_ln
|
||||||
|
ownr_ph1
|
||||||
|
ownr_ph2
|
||||||
|
owner {
|
||||||
|
id
|
||||||
|
allow_text_message
|
||||||
|
preferred_contact
|
||||||
|
tax_number
|
||||||
|
}
|
||||||
|
owner_owing
|
||||||
|
plate_no
|
||||||
|
plate_st
|
||||||
|
po_number
|
||||||
|
production_vars
|
||||||
|
ro_number
|
||||||
|
scheduled_completion
|
||||||
|
scheduled_delivery
|
||||||
|
scheduled_in
|
||||||
|
special_coverage_policy
|
||||||
|
status
|
||||||
|
suspended
|
||||||
|
updated_at
|
||||||
|
vehicle {
|
||||||
|
id
|
||||||
|
jobs {
|
||||||
|
id
|
||||||
|
clm_no
|
||||||
|
ro_number
|
||||||
|
}
|
||||||
|
notes
|
||||||
|
plate_no
|
||||||
|
v_color
|
||||||
|
v_make_desc
|
||||||
|
v_model_desc
|
||||||
|
v_model_yr
|
||||||
|
}
|
||||||
|
vehicleid
|
||||||
|
v_color
|
||||||
|
v_make_desc
|
||||||
|
v_model_desc
|
||||||
|
v_model_yr
|
||||||
|
v_vin
|
||||||
|
voided
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|||||||
@@ -143,9 +143,14 @@ export const QUERY_TIME_TICKETS_IN_RANGE_SB = gql`
|
|||||||
$end: date!
|
$end: date!
|
||||||
$fixedStart: date!
|
$fixedStart: date!
|
||||||
$fixedEnd: date!
|
$fixedEnd: date!
|
||||||
|
$jobStart: timestamptz!
|
||||||
|
$jobEnd: timestamptz!
|
||||||
) {
|
) {
|
||||||
timetickets(
|
timetickets(
|
||||||
where: { date: { _gte: $start, _lte: $end }, cost_center: {_neq: "timetickets.labels.shift"} }
|
where: {
|
||||||
|
date: { _gte: $start, _lte: $end }
|
||||||
|
cost_center: { _neq: "timetickets.labels.shift" }
|
||||||
|
}
|
||||||
order_by: { date: desc_nulls_first }
|
order_by: { date: desc_nulls_first }
|
||||||
) {
|
) {
|
||||||
actualhrs
|
actualhrs
|
||||||
@@ -176,7 +181,10 @@ export const QUERY_TIME_TICKETS_IN_RANGE_SB = gql`
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
fixedperiod: timetickets(
|
fixedperiod: timetickets(
|
||||||
where: { date: { _gte: $fixedStart, _lte: $fixedEnd }, cost_center: {_neq: "timetickets.labels.shift"} }
|
where: {
|
||||||
|
date: { _gte: $fixedStart, _lte: $fixedEnd }
|
||||||
|
cost_center: { _neq: "timetickets.labels.shift" }
|
||||||
|
}
|
||||||
order_by: { date: desc_nulls_first }
|
order_by: { date: desc_nulls_first }
|
||||||
) {
|
) {
|
||||||
actualhrs
|
actualhrs
|
||||||
@@ -205,6 +213,25 @@ export const QUERY_TIME_TICKETS_IN_RANGE_SB = gql`
|
|||||||
last_name
|
last_name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
jobs(
|
||||||
|
where: {
|
||||||
|
date_invoiced: { _is_null: true }
|
||||||
|
ro_number: { _is_null: false }
|
||||||
|
voided: { _eq: false }
|
||||||
|
_or: [
|
||||||
|
{ actual_completion: { _gte: $jobStart, _lte: $jobEnd } }
|
||||||
|
{ actual_delivery: { _gte: $jobStart, _lte: $jobEnd } }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
id
|
||||||
|
joblines(order_by: { line_no: asc }, where: { removed: { _eq: false } }) {
|
||||||
|
convertedtolbr
|
||||||
|
convertedtolbr_data
|
||||||
|
mod_lb_hrs
|
||||||
|
mod_lbr_ty
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|||||||
@@ -14,38 +14,38 @@ import { persistor, store } from "./redux/store";
|
|||||||
import reportWebVitals from "./reportWebVitals";
|
import reportWebVitals from "./reportWebVitals";
|
||||||
import "./translations/i18n";
|
import "./translations/i18n";
|
||||||
import "./utils/CleanAxios";
|
import "./utils/CleanAxios";
|
||||||
|
|
||||||
//import { BrowserTracing } from "@sentry/tracing";
|
//import { BrowserTracing } from "@sentry/tracing";
|
||||||
|
|
||||||
// Dinero.defaultCurrency = "CAD";
|
// Dinero.defaultCurrency = "CAD";
|
||||||
// Dinero.globalLocale = "en-CA";
|
// Dinero.globalLocale = "en-CA";
|
||||||
Dinero.globalRoundingMode = "HALF_EVEN";
|
Dinero.globalRoundingMode = "HALF_EVEN";
|
||||||
|
|
||||||
if (process.env.NODE_ENV !== "development") {
|
//if (process.env.NODE_ENV !== "development") {
|
||||||
Sentry.init({
|
Sentry.init({
|
||||||
dsn: "https://fd7e89369b6b4bdc9c6c4c9f22fa4ee4@o492140.ingest.sentry.io/5651027",
|
dsn: "https://fd7e89369b6b4bdc9c6c4c9f22fa4ee4@o492140.ingest.sentry.io/5651027",
|
||||||
ignoreErrors: [
|
ignoreErrors: [
|
||||||
"ResizeObserver loop",
|
"ResizeObserver loop",
|
||||||
"Module specifier, 'fs' does not start",
|
"Module specifier, 'fs' does not start",
|
||||||
"Module specifier, 'zlib' does not start with",
|
"Module specifier, 'zlib' does not start with",
|
||||||
],
|
],
|
||||||
integrations: [
|
integrations: [
|
||||||
// new BrowserTracing(),
|
Sentry.replayIntegration({
|
||||||
// new Sentry.Integrations.Breadcrumbs({ console: true }),
|
maskAllText: false,
|
||||||
// new Sentry.Replay(),
|
blockAllMedia: true,
|
||||||
],
|
}),
|
||||||
// This sets the sample rate to be 10%. You may want this to be 100% while
|
new Sentry.BrowserTracing({}),
|
||||||
// in development and sample at a lower rate in production
|
],
|
||||||
// replaysSessionSampleRate: 0.1,
|
tracePropagationTargets: [
|
||||||
// // If the entire session is not sampled, use the below sample rate to sample
|
"api.imex.online",
|
||||||
// // sessions when an error occurs.
|
"api.test.imex.online",
|
||||||
// replaysOnErrorSampleRate: 1.0,
|
"db.imex.online",
|
||||||
environment: process.env.NODE_ENV,
|
],
|
||||||
// tracesSampleRate: 0.2,
|
tracesSampleRate: 1.0,
|
||||||
// We recommend adjusting this value in production, or using tracesSampler
|
replaysOnErrorSampleRate: 1.0,
|
||||||
// for finer control
|
environment: process.env.NODE_ENV,
|
||||||
// tracesSampleRate: 0.5,
|
});
|
||||||
});
|
//}
|
||||||
}
|
|
||||||
|
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
import { useMutation, useQuery } from "@apollo/client";
|
import { useMutation, useQuery } from "@apollo/client";
|
||||||
import { Form, notification } from "antd";
|
import { Form, notification } from "antd";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
|
import queryString from "query-string";
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { useParams } from "react-router-dom";
|
import { useLocation, useParams } from "react-router-dom";
|
||||||
import AlertComponent from "../../components/alert/alert.component";
|
import AlertComponent from "../../components/alert/alert.component";
|
||||||
|
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
|
||||||
|
import NotFound from "../../components/not-found/not-found.component";
|
||||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||||
import { QUERY_CC_BY_PK, UPDATE_CC } from "../../graphql/courtesy-car.queries";
|
import { QUERY_CC_BY_PK, UPDATE_CC } from "../../graphql/courtesy-car.queries";
|
||||||
import {
|
import {
|
||||||
@@ -13,13 +16,10 @@ import {
|
|||||||
setBreadcrumbs,
|
setBreadcrumbs,
|
||||||
setSelectedHeader,
|
setSelectedHeader,
|
||||||
} from "../../redux/application/application.actions";
|
} from "../../redux/application/application.actions";
|
||||||
|
import { pageLimit } from "../../utils/config";
|
||||||
import { CreateRecentItem } from "../../utils/create-recent-item";
|
import { CreateRecentItem } from "../../utils/create-recent-item";
|
||||||
|
import UndefinedToNull from "./../../utils/undefinedtonull";
|
||||||
import CourtesyCarDetailPageComponent from "./courtesy-car-detail.page.component";
|
import CourtesyCarDetailPageComponent from "./courtesy-car-detail.page.component";
|
||||||
import NotFound from "../../components/not-found/not-found.component";
|
|
||||||
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
|
|
||||||
import queryString from "query-string";
|
|
||||||
import { useLocation } from "react-router-dom";
|
|
||||||
import {pageLimit} from "../../utils/config";
|
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||||
@@ -112,7 +112,10 @@ export function CourtesyCarDetailPageContainer({
|
|||||||
setSaveLoading(true);
|
setSaveLoading(true);
|
||||||
|
|
||||||
const result = await updateCourtesyCar({
|
const result = await updateCourtesyCar({
|
||||||
variables: { cc: { ...values }, ccId: ccId },
|
variables: {
|
||||||
|
cc: { ...UndefinedToNull(values, ["readiness"]) },
|
||||||
|
ccId: ccId,
|
||||||
|
},
|
||||||
refetchQueries: ["QUERY_CC_BY_PK"],
|
refetchQueries: ["QUERY_CC_BY_PK"],
|
||||||
awaitRefetchQueries: true,
|
awaitRefetchQueries: true,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,88 +1,67 @@
|
|||||||
import { useQuery, useMutation } from "@apollo/client";
|
// import { useMutation, useQuery } from "@apollo/client";
|
||||||
import { Form, Layout, Typography, Button, Result } from "antd";
|
import { Button, Form, Layout, Result, Typography } from "antd";
|
||||||
import React, { useState } from "react";
|
import axios from "axios";
|
||||||
|
import React, { useCallback, useEffect, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { connect } from "react-redux";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
import AlertComponent from "../../components/alert/alert.component";
|
import AlertComponent from "../../components/alert/alert.component";
|
||||||
import ConfigFormComponents from "../../components/config-form-components/config-form-components.component";
|
import ConfigFormComponents from "../../components/config-form-components/config-form-components.component";
|
||||||
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
|
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
|
||||||
import { QUERY_SURVEY, COMPLETE_SURVEY } from "../../graphql/csi.queries";
|
|
||||||
import { connect } from "react-redux";
|
|
||||||
import { createStructuredSelector } from "reselect";
|
|
||||||
import { selectCurrentUser } from "../../redux/user/user.selectors";
|
import { selectCurrentUser } from "../../redux/user/user.selectors";
|
||||||
|
import { DateTimeFormat } from "./../../utils/DateFormatter";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
currentUser: selectCurrentUser,
|
currentUser: selectCurrentUser,
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({});
|
||||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
|
||||||
});
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(CsiContainerPage);
|
export default connect(mapStateToProps, mapDispatchToProps)(CsiContainerPage);
|
||||||
|
|
||||||
export function CsiContainerPage({ currentUser }) {
|
export function CsiContainerPage({ currentUser }) {
|
||||||
const { surveyId } = useParams();
|
const { surveyId } = useParams();
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
|
const [axiosResponse, setAxiosResponse] = useState(null);
|
||||||
const [submitting, setSubmitting] = useState({
|
const [submitting, setSubmitting] = useState({
|
||||||
loading: false,
|
loading: false,
|
||||||
submitted: false,
|
submitted: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { loading, error, data } = useQuery(QUERY_SURVEY, {
|
|
||||||
variables: { surveyId },
|
|
||||||
fetchPolicy: "network-only",
|
|
||||||
nextFetchPolicy: "network-only",
|
|
||||||
});
|
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [completeSurvey] = useMutation(COMPLETE_SURVEY);
|
|
||||||
if (loading) return <LoadingSpinner />;
|
|
||||||
|
|
||||||
if (error || !!!data.csi_by_pk)
|
const getAxiosData = useCallback(async () => {
|
||||||
return (
|
try {
|
||||||
<div>
|
try {
|
||||||
<Result
|
window.$crisp.push(["do", "chat:hide"]);
|
||||||
status="error"
|
} catch {
|
||||||
title={t("csi.errors.notfoundtitle")}
|
console.log("Unable to attach to crisp instance. ");
|
||||||
subTitle={t("csi.errors.notfoundsubtitle")}
|
}
|
||||||
>
|
setSubmitting((prevSubmitting) => ({ ...prevSubmitting, loading: true }));
|
||||||
{error ? (
|
const response = await axios.post("/csi/lookup", { surveyId });
|
||||||
<div>ERROR: {error.graphQLErrors.map((e) => e.message)}</div>
|
setSubmitting((prevSubmitting) => ({
|
||||||
) : null}
|
...prevSubmitting,
|
||||||
</Result>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleFinish = async (values) => {
|
|
||||||
setSubmitting({ ...submitting, loading: true });
|
|
||||||
|
|
||||||
const result = await completeSurvey({
|
|
||||||
variables: {
|
|
||||||
surveyId,
|
|
||||||
survey: {
|
|
||||||
response: values,
|
|
||||||
valid: false,
|
|
||||||
completedon: new Date(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!!!result.errors) {
|
|
||||||
setSubmitting({ ...submitting, loading: false, submitted: true });
|
|
||||||
} else {
|
|
||||||
setSubmitting({
|
|
||||||
...submitting,
|
|
||||||
loading: false,
|
loading: false,
|
||||||
error: JSON.stringify(result.errors),
|
}));
|
||||||
|
setAxiosResponse(response.data);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Something went wrong...: ${error.message}`);
|
||||||
|
console.dir({
|
||||||
|
stack: error?.stack,
|
||||||
|
message: error?.message,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
}, [setAxiosResponse, surveyId]);
|
||||||
|
|
||||||
const {
|
useEffect(() => {
|
||||||
relateddata: { bodyshop, job },
|
getAxiosData().catch((err) =>
|
||||||
csiquestion: { config: csiquestions },
|
console.error(
|
||||||
} = data.csi_by_pk;
|
`Something went wrong fetching axios data: ${err.message || ""}`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}, [getAxiosData]);
|
||||||
|
|
||||||
if (currentUser && currentUser.authorized)
|
// Return if authorized
|
||||||
|
if (currentUser && currentUser.authorized) {
|
||||||
return (
|
return (
|
||||||
<Layout
|
<Layout
|
||||||
style={{ height: "100vh", display: "flex", flexDirection: "column" }}
|
style={{ height: "100vh", display: "flex", flexDirection: "column" }}
|
||||||
@@ -94,85 +73,176 @@ export function CsiContainerPage({ currentUser }) {
|
|||||||
/>
|
/>
|
||||||
</Layout>
|
</Layout>
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
if (submitting.loading) return <LoadingSpinner />;
|
||||||
<Layout
|
|
||||||
style={{ height: "100vh", display: "flex", flexDirection: "column" }}
|
const handleFinish = async (values) => {
|
||||||
>
|
try {
|
||||||
<div
|
setSubmitting({ ...submitting, loading: true, submitting: true });
|
||||||
style={{
|
const result = await axios.post("/csi/submit", { surveyId, values });
|
||||||
display: "flex",
|
console.log("result", result);
|
||||||
flexDirection: "column",
|
if (!!!result.errors && result.data.update_csi.affected_rows > 0) {
|
||||||
alignItems: "center",
|
setSubmitting({ ...submitting, loading: false, submitted: true });
|
||||||
}}
|
}
|
||||||
>
|
} catch (error) {
|
||||||
<div style={{ display: "flex", alignItems: "center", margin: "2em" }}>
|
console.error(`Something went wrong...: ${error.message}`);
|
||||||
{bodyshop.logo_img_path && bodyshop.logo_img_path.src ? (
|
console.dir({
|
||||||
<img src={bodyshop.logo_img_path.src} alt="Logo" />
|
stack: error?.stack,
|
||||||
) : null}
|
message: error?.message,
|
||||||
<div style={{ margin: "2em" }}>
|
});
|
||||||
<strong>{bodyshop.shopname || ""}</strong>
|
}
|
||||||
<div>{`${bodyshop.address1 || ""}`}</div>
|
};
|
||||||
<div>{`${bodyshop.address2 || ""}`}</div>
|
|
||||||
<div>{`${bodyshop.city || ""} ${bodyshop.state || ""} ${
|
if (!axiosResponse || axiosResponse.csi_by_pk === null) {
|
||||||
bodyshop.zip_post || ""
|
// Do something here , this is where you would return a loading box or something
|
||||||
}`}</div>
|
return (
|
||||||
|
<>
|
||||||
|
<Layout style={{ display: "flex", flexDirection: "column" }}>
|
||||||
|
<Layout.Content
|
||||||
|
style={{
|
||||||
|
backgroundColor: "#fff",
|
||||||
|
margin: "2em 4em",
|
||||||
|
padding: "2em",
|
||||||
|
overflowY: "auto",
|
||||||
|
textAlign: "center",
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Form>
|
||||||
|
<Result
|
||||||
|
status="error"
|
||||||
|
title={t("csi.errors.notfoundtitle")}
|
||||||
|
subTitle={t("csi.errors.notfoundsubtitle")}
|
||||||
|
/>
|
||||||
|
</Form>
|
||||||
|
</Layout.Content>
|
||||||
|
<Layout.Footer>
|
||||||
|
{t("csi.labels.copyright")}{" "}
|
||||||
|
{t("csi.fields.surveyid", { surveyId: surveyId })}
|
||||||
|
</Layout.Footer>
|
||||||
|
</Layout>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
const {
|
||||||
|
relateddata: { bodyshop, job },
|
||||||
|
csiquestion: { config: csiquestions },
|
||||||
|
} = axiosResponse.csi_by_pk;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Layout style={{ display: "flex", flexDirection: "column" }}>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
alignItems: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div style={{ display: "flex", alignItems: "center", margin: "2em" }}>
|
||||||
|
{bodyshop.logo_img_path && bodyshop.logo_img_path.src ? (
|
||||||
|
<img
|
||||||
|
src={bodyshop.logo_img_path.src}
|
||||||
|
alt={bodyshop.shopname.concat(" Logo")}
|
||||||
|
height={bodyshop.logo_img_path.height}
|
||||||
|
width={bodyshop.logo_img_path.width}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
<div style={{ margin: "2em", verticalAlign: "middle" }}>
|
||||||
|
<Typography.Title level={4} style={{ margin: 0 }}>
|
||||||
|
{bodyshop.shopname || ""}
|
||||||
|
</Typography.Title>
|
||||||
|
<Typography.Paragraph style={{ margin: 0 }}>
|
||||||
|
{`${bodyshop.address1 || ""}${bodyshop.address2 ? ", " : ""}${
|
||||||
|
bodyshop.address2 || ""
|
||||||
|
}`.trim()}
|
||||||
|
</Typography.Paragraph>
|
||||||
|
<Typography.Paragraph style={{ margin: 0 }}>
|
||||||
|
{`${bodyshop.city || ""}${
|
||||||
|
bodyshop.city && bodyshop.state ? ", " : ""
|
||||||
|
}${bodyshop.state || ""} ${bodyshop.zip_post || ""}`.trim()}
|
||||||
|
</Typography.Paragraph>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<Typography.Title>{t("csi.labels.title")}</Typography.Title>
|
||||||
|
<strong>
|
||||||
|
{t("csi.labels.greeting", {
|
||||||
|
name: job.ownr_co_nm || job.ownr_fn || "",
|
||||||
|
})}
|
||||||
|
</strong>
|
||||||
|
<Typography.Paragraph>
|
||||||
|
{t("csi.labels.intro", { shopname: bodyshop.shopname || "" })}
|
||||||
|
</Typography.Paragraph>
|
||||||
</div>
|
</div>
|
||||||
<Typography.Title>{t("csi.labels.title")}</Typography.Title>
|
|
||||||
<strong>{`Hi ${job.ownr_co_nm || job.ownr_fn || ""}!`}</strong>
|
|
||||||
<Typography.Paragraph>
|
|
||||||
{`At ${
|
|
||||||
bodyshop.shopname || ""
|
|
||||||
}, we value your feedback. We would love to
|
|
||||||
hear what you have to say. Please fill out the form below.`}
|
|
||||||
</Typography.Paragraph>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{submitting.error ? (
|
{submitting.error ? (
|
||||||
<AlertComponent message={submitting.error} type="error" />
|
<AlertComponent message={submitting.error} type="error" />
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{submitting.submitted ? (
|
{submitting.submitted ? (
|
||||||
<Layout.Content
|
<Layout.Content
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: "#fff",
|
backgroundColor: "#fff",
|
||||||
margin: "2em 4em",
|
margin: "2em 4em",
|
||||||
padding: "2em",
|
padding: "2em",
|
||||||
overflowY: "auto",
|
overflowY: "auto",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Result
|
<Result
|
||||||
status="success"
|
status="success"
|
||||||
title={t("csi.successes.submitted")}
|
title={t("csi.successes.submitted")}
|
||||||
subTitle={t("csi.successes.submittedsub")}
|
subTitle={t("csi.successes.submittedsub")}
|
||||||
/>
|
/>
|
||||||
</Layout.Content>
|
</Layout.Content>
|
||||||
) : (
|
) : (
|
||||||
<Layout.Content
|
<Layout.Content
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: "#fff",
|
backgroundColor: "#fff",
|
||||||
margin: "2em 4em",
|
margin: "2em 4em",
|
||||||
padding: "2em",
|
padding: "2em",
|
||||||
overflowY: "auto",
|
overflowY: "auto",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Form form={form} onFinish={handleFinish}>
|
<Form form={form} onFinish={handleFinish}>
|
||||||
<ConfigFormComponents componentList={csiquestions} />
|
{axiosResponse.csi_by_pk.valid ? (
|
||||||
<Button
|
<>
|
||||||
loading={submitting.loading}
|
<ConfigFormComponents componentList={csiquestions} />
|
||||||
type="primary"
|
<Button
|
||||||
htmlType="submit"
|
loading={submitting.loading}
|
||||||
>
|
type="primary"
|
||||||
{t("general.actions.submit")}
|
htmlType="submit"
|
||||||
</Button>
|
style={{ float: "right" }}
|
||||||
</Form>
|
>
|
||||||
</Layout.Content>
|
{t("general.actions.submit")}
|
||||||
)}
|
</Button>
|
||||||
|
</>
|
||||||
<Layout.Footer>
|
) : (
|
||||||
{`Copyright ImEX.Online. Survey ID: ${surveyId}`}
|
<>
|
||||||
</Layout.Footer>
|
<Result
|
||||||
</Layout>
|
title={t("csi.errors.surveycompletetitle")}
|
||||||
);
|
status="warning"
|
||||||
|
subTitle={t("csi.errors.surveycompletesubtitle", {
|
||||||
|
date: DateTimeFormat(axiosResponse.csi_by_pk.completedon),
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
<Typography.Paragraph
|
||||||
|
type="secondary"
|
||||||
|
style={{ textAlign: "center" }}
|
||||||
|
>
|
||||||
|
{t("csi.successes.submittedsub")}
|
||||||
|
</Typography.Paragraph>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Form>
|
||||||
|
</Layout.Content>
|
||||||
|
)}
|
||||||
|
<Layout.Footer>
|
||||||
|
{t("csi.labels.copyright")}{" "}
|
||||||
|
{t("csi.fields.surveyid", { surveyId: surveyId })}
|
||||||
|
</Layout.Footer>
|
||||||
|
</Layout>
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,16 +7,16 @@ import { useParams } from "react-router-dom";
|
|||||||
import AlertComponent from "../../components/alert/alert.component";
|
import AlertComponent from "../../components/alert/alert.component";
|
||||||
import JobCalculateTotals from "../../components/job-calculate-totals/job-calculate-totals.component";
|
import JobCalculateTotals from "../../components/job-calculate-totals/job-calculate-totals.component";
|
||||||
import ScoreboardAddButton from "../../components/job-scoreboard-add-button/job-scoreboard-add-button.component";
|
import ScoreboardAddButton from "../../components/job-scoreboard-add-button/job-scoreboard-add-button.component";
|
||||||
|
import JobsAdminStatus from "../../components/jobs-admin-change-status/jobs-admin-change.status.component";
|
||||||
import JobsAdminClass from "../../components/jobs-admin-class/jobs-admin-class.component";
|
import JobsAdminClass from "../../components/jobs-admin-class/jobs-admin-class.component";
|
||||||
import JobsAdminDatesChange from "../../components/jobs-admin-dates/jobs-admin-dates.component";
|
import JobsAdminDatesChange from "../../components/jobs-admin-dates/jobs-admin-dates.component";
|
||||||
import JobsAdminDeleteIntake from "../../components/jobs-admin-delete-intake/jobs-admin-delete-intake.component";
|
import JobsAdminDeleteIntake from "../../components/jobs-admin-delete-intake/jobs-admin-delete-intake.component";
|
||||||
import JobsAdminMarkReexport from "../../components/jobs-admin-mark-reexport/jobs-admin-mark-reexport.component";
|
import JobsAdminMarkReexport from "../../components/jobs-admin-mark-reexport/jobs-admin-mark-reexport.component";
|
||||||
import JobAdminOwnerReassociate from "../../components/jobs-admin-owner-reassociate/jobs-admin-owner-reassociate.component";
|
import JobAdminOwnerReassociate from "../../components/jobs-admin-owner-reassociate/jobs-admin-owner-reassociate.component";
|
||||||
|
import JobsAdminRemoveAR from "../../components/jobs-admin-remove-ar/jobs-admin-remove-ar.component";
|
||||||
import JobsAdminUnvoid from "../../components/jobs-admin-unvoid/jobs-admin-unvoid.component";
|
import JobsAdminUnvoid from "../../components/jobs-admin-unvoid/jobs-admin-unvoid.component";
|
||||||
import JobAdminVehicleReassociate from "../../components/jobs-admin-vehicle-reassociate/jobs-admin-vehicle-reassociate.component";
|
import JobAdminVehicleReassociate from "../../components/jobs-admin-vehicle-reassociate/jobs-admin-vehicle-reassociate.component";
|
||||||
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
|
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
|
||||||
import JobsAdminStatus from "../../components/jobs-admin-change-status/jobs-admin-change.status.component";
|
|
||||||
|
|
||||||
import NotFound from "../../components/not-found/not-found.component";
|
import NotFound from "../../components/not-found/not-found.component";
|
||||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||||
import { GET_JOB_BY_PK } from "../../graphql/jobs.queries";
|
import { GET_JOB_BY_PK } from "../../graphql/jobs.queries";
|
||||||
@@ -104,6 +104,7 @@ export function JobsCloseContainer({ setBreadcrumbs, setSelectedHeader }) {
|
|||||||
<JobsAdminMarkReexport job={data ? data.jobs_by_pk : {}} />
|
<JobsAdminMarkReexport job={data ? data.jobs_by_pk : {}} />
|
||||||
<JobsAdminUnvoid job={data ? data.jobs_by_pk : {}} />
|
<JobsAdminUnvoid job={data ? data.jobs_by_pk : {}} />
|
||||||
<JobsAdminStatus job={data ? data.jobs_by_pk : {}} />
|
<JobsAdminStatus job={data ? data.jobs_by_pk : {}} />
|
||||||
|
<JobsAdminRemoveAR job={data ? data.jobs_by_pk : {}} />
|
||||||
</Space>
|
</Space>
|
||||||
</Card>
|
</Card>
|
||||||
</Col>
|
</Col>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import _ from "lodash";
|
|
||||||
import { useLazyQuery, useMutation } from "@apollo/client";
|
import { useLazyQuery, useMutation } from "@apollo/client";
|
||||||
import { Form, notification } from "antd";
|
import { Form, notification } from "antd";
|
||||||
|
import _ from "lodash";
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
@@ -90,6 +90,7 @@ function JobsCreateContainer({ bodyshop, setBreadcrumbs, setSelectedHeader }) {
|
|||||||
{},
|
{},
|
||||||
values,
|
values,
|
||||||
{ date_open: new Date() },
|
{ date_open: new Date() },
|
||||||
|
{ date_estimated: new Date() },
|
||||||
{
|
{
|
||||||
vehicle:
|
vehicle:
|
||||||
state.vehicle.selectedid || state.vehicle.none
|
state.vehicle.selectedid || state.vehicle.none
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ import { selectBodyshop } from "../../redux/user/user.selectors";
|
|||||||
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
||||||
import UndefinedToNull from "../../utils/undefinedtonull";
|
import UndefinedToNull from "../../utils/undefinedtonull";
|
||||||
import { DateTimeFormat } from "./../../utils/DateFormatter";
|
import { DateTimeFormat } from "./../../utils/DateFormatter";
|
||||||
|
import JobLifecycleComponent from "../../components/job-lifecycle/job-lifecycle.component";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
@@ -333,7 +334,15 @@ export function JobsDetailPage({
|
|||||||
>
|
>
|
||||||
<JobsDetailLaborContainer job={job} jobId={job.id} />
|
<JobsDetailLaborContainer job={job} jobId={job.id} />
|
||||||
</Tabs.TabPane>
|
</Tabs.TabPane>
|
||||||
<Tabs.TabPane
|
<Tabs.TabPane
|
||||||
|
forceRender
|
||||||
|
tab={<span><BarsOutlined />{t('menus.jobsdetail.lifecycle')}</span>}
|
||||||
|
key="lifecycle"
|
||||||
|
>
|
||||||
|
<JobLifecycleComponent job={job} statuses={bodyshop.md_ro_statuses}/>
|
||||||
|
</Tabs.TabPane>
|
||||||
|
|
||||||
|
<Tabs.TabPane
|
||||||
forceRender
|
forceRender
|
||||||
tab={
|
tab={
|
||||||
<span>
|
<span>
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
import React, { useEffect } from "react";
|
import React, { useEffect } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
|
import PartsQueueDetailCard from "../../components/parts-queue-card/parts-queue-card.component";
|
||||||
|
import PartsQueueList from "../../components/parts-queue-list/parts-queue.list.component";
|
||||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||||
import {
|
import {
|
||||||
setBreadcrumbs,
|
setBreadcrumbs,
|
||||||
setSelectedHeader,
|
setSelectedHeader,
|
||||||
} from "../../redux/application/application.actions";
|
} from "../../redux/application/application.actions";
|
||||||
import PartsQueuePage from "./parts-queue.page.component";
|
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||||
@@ -26,7 +27,8 @@ export function PartsQueuePageContainer({ setBreadcrumbs, setSelectedHeader }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<RbacWrapper action="jobs:partsqueue">
|
<RbacWrapper action="jobs:partsqueue">
|
||||||
<PartsQueuePage />
|
<PartsQueueList />
|
||||||
|
<PartsQueueDetailCard />
|
||||||
</RbacWrapper>
|
</RbacWrapper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,20 @@
|
|||||||
import { Row, Col } from "antd";
|
|
||||||
import { useQuery } from "@apollo/client";
|
import { useQuery } from "@apollo/client";
|
||||||
import queryString from "query-string";
|
import { Col, Row } from "antd";
|
||||||
import React, { useEffect } from "react";
|
import React, { useEffect } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { useLocation } from "react-router-dom";
|
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import AlertComponent from "../../components/alert/alert.component";
|
import AlertComponent from "../../components/alert/alert.component";
|
||||||
import CsiResponseFormContainer from "../../components/csi-response-form/csi-response-form.container";
|
import CsiResponseFormContainer from "../../components/csi-response-form/csi-response-form.container";
|
||||||
import CsiResponseListPaginated from "../../components/csi-response-list-paginated/csi-response-list-paginated.component";
|
import CsiResponseListPaginated from "../../components/csi-response-list-paginated/csi-response-list-paginated.component";
|
||||||
|
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||||
import { QUERY_CSI_RESPONSE_PAGINATED } from "../../graphql/csi.queries";
|
import { QUERY_CSI_RESPONSE_PAGINATED } from "../../graphql/csi.queries";
|
||||||
import {
|
import {
|
||||||
setBreadcrumbs,
|
setBreadcrumbs,
|
||||||
setSelectedHeader,
|
setSelectedHeader,
|
||||||
} from "../../redux/application/application.actions";
|
} from "../../redux/application/application.actions";
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
|
||||||
import {pageLimit} from "../../utils/config";
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
});
|
});
|
||||||
@@ -33,28 +31,11 @@ export function ShopCsiContainer({
|
|||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const searchParams = queryString.parse(useLocation().search);
|
|
||||||
const { page, sortcolumn, sortorder } = searchParams;
|
|
||||||
|
|
||||||
const { loading, error, data, refetch } = useQuery(
|
const { loading, error, data, refetch } = useQuery(
|
||||||
QUERY_CSI_RESPONSE_PAGINATED,
|
QUERY_CSI_RESPONSE_PAGINATED,
|
||||||
{
|
{
|
||||||
fetchPolicy: "network-only",
|
fetchPolicy: "network-only",
|
||||||
nextFetchPolicy: "network-only",
|
nextFetchPolicy: "network-only",
|
||||||
variables: {
|
|
||||||
//search: search || "",
|
|
||||||
offset: page ? (page - 1) * pageLimit : 0,
|
|
||||||
limit: pageLimit,
|
|
||||||
order: [
|
|
||||||
{
|
|
||||||
[sortcolumn || "completedon"]: sortorder
|
|
||||||
? sortorder === "descend"
|
|
||||||
? "desc_nulls_last"
|
|
||||||
: "asc"
|
|
||||||
: "desc_nulls_last",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -73,12 +54,7 @@ export function ShopCsiContainer({
|
|||||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RbacWrapper
|
<RbacWrapper action="csi:page">
|
||||||
action="csi:page"
|
|
||||||
// noauth={
|
|
||||||
// <AlertComponent message="You don't have acess to see this screen." />
|
|
||||||
// }
|
|
||||||
>
|
|
||||||
<Row gutter={16}>
|
<Row gutter={16}>
|
||||||
<Col span={10}>
|
<Col span={10}>
|
||||||
<CsiResponseListPaginated
|
<CsiResponseListPaginated
|
||||||
|
|||||||
@@ -1,10 +1,17 @@
|
|||||||
import { Divider } from "antd";
|
import { Divider } from "antd";
|
||||||
import React from "react";
|
import React, { useEffect } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import TechClockInFormContainer from "../../components/tech-job-clock-in-form/tech-job-clock-in-form.container";
|
import TechClockInFormContainer from "../../components/tech-job-clock-in-form/tech-job-clock-in-form.container";
|
||||||
import TechClockedInList from "../../components/tech-job-clocked-in-list/tech-job-clocked-in-list.component";
|
import TechClockedInList from "../../components/tech-job-clocked-in-list/tech-job-clocked-in-list.component";
|
||||||
import TechJobStatistics from "../../components/tech-job-statistics/tech-job-statistics.component";
|
import TechJobStatistics from "../../components/tech-job-statistics/tech-job-statistics.component";
|
||||||
|
|
||||||
export default function TechClockComponent() {
|
export default function TechClockComponent() {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
document.title = t("titles.techjobclock");
|
||||||
|
}, [t]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<TechJobStatistics />
|
<TechJobStatistics />
|
||||||
|
|||||||
@@ -1,9 +1,16 @@
|
|||||||
import React from "react";
|
import React, { useEffect } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import RbacWrapperComponent from "../../components/rbac-wrapper/rbac-wrapper.component";
|
import RbacWrapperComponent from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||||
import TechLookupJobsDrawer from "../../components/tech-lookup-jobs-drawer/tech-lookup-jobs-drawer.component";
|
import TechLookupJobsDrawer from "../../components/tech-lookup-jobs-drawer/tech-lookup-jobs-drawer.component";
|
||||||
import TechLookupJobsList from "../../components/tech-lookup-jobs-list/tech-lookup-jobs-list.component";
|
import TechLookupJobsList from "../../components/tech-lookup-jobs-list/tech-lookup-jobs-list.component";
|
||||||
|
|
||||||
export default function TechLookupContainer() {
|
export default function TechLookupContainer() {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
document.title = t("titles.techjoblookup");
|
||||||
|
}, [t]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<RbacWrapperComponent action="jobs:list-active">
|
<RbacWrapperComponent action="jobs:list-active">
|
||||||
|
|||||||
@@ -1,7 +1,14 @@
|
|||||||
import React from "react";
|
import React, { useEffect } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import TimeTicketShift from "../../components/time-ticket-shift/time-ticket-shift.container";
|
import TimeTicketShift from "../../components/time-ticket-shift/time-ticket-shift.container";
|
||||||
|
|
||||||
export default function TechShiftClock() {
|
export default function TechShiftClock() {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
document.title = t("titles.techshiftclock");
|
||||||
|
}, [t]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<TimeTicketShift isTechConsole />
|
<TimeTicketShift isTechConsole />
|
||||||
|
|||||||
@@ -99,6 +99,7 @@
|
|||||||
},
|
},
|
||||||
"audit_trail": {
|
"audit_trail": {
|
||||||
"messages": {
|
"messages": {
|
||||||
|
"admin_job_remove_from_ar": "ADMIN: Remove from AR updated to: {{status}}",
|
||||||
"admin_jobmarkexported": "ADMIN: Job marked as exported.",
|
"admin_jobmarkexported": "ADMIN: Job marked as exported.",
|
||||||
"admin_jobmarkforreexport": "ADMIN: Job marked for re-export.",
|
"admin_jobmarkforreexport": "ADMIN: Job marked for re-export.",
|
||||||
"admin_jobuninvoice": "ADMIN: Job has been uninvoiced.",
|
"admin_jobuninvoice": "ADMIN: Job has been uninvoiced.",
|
||||||
@@ -112,11 +113,11 @@
|
|||||||
"jobassignmentchange": "Employee {{name}} assigned to {{operation}}",
|
"jobassignmentchange": "Employee {{name}} assigned to {{operation}}",
|
||||||
"jobassignmentremoved": "Employee assignment removed for {{operation}}",
|
"jobassignmentremoved": "Employee assignment removed for {{operation}}",
|
||||||
"jobchecklist": "Checklist type \"{{type}}\" completed. In production set to {{inproduction}}. Status set to {{status}}.",
|
"jobchecklist": "Checklist type \"{{type}}\" completed. In production set to {{inproduction}}. Status set to {{status}}.",
|
||||||
"jobinvoiced": "Job has been invoiced.",
|
|
||||||
"jobconverted": "Job converted and assigned number {{ro_number}}.",
|
"jobconverted": "Job converted and assigned number {{ro_number}}.",
|
||||||
"jobfieldchanged": "Job field $t(jobs.fields.{{field}}) changed to {{value}}.",
|
"jobfieldchanged": "Job field $t(jobs.fields.{{field}}) changed to {{value}}.",
|
||||||
"jobimported": "Job imported.",
|
"jobimported": "Job imported.",
|
||||||
"jobinproductionchange": "Job production status set to {{inproduction}}",
|
"jobinproductionchange": "Job production status set to {{inproduction}}",
|
||||||
|
"jobinvoiced": "Job has been invoiced.",
|
||||||
"jobioucreated": "IOU Created.",
|
"jobioucreated": "IOU Created.",
|
||||||
"jobmodifylbradj": "Labor adjustments modified {{mod_lbr_ty}} / {{hours}}.",
|
"jobmodifylbradj": "Labor adjustments modified {{mod_lbr_ty}} / {{hours}}.",
|
||||||
"jobnoteadded": "Note added to Job.",
|
"jobnoteadded": "Note added to Job.",
|
||||||
@@ -205,6 +206,7 @@
|
|||||||
"entered_total": "Total of Entered Lines",
|
"entered_total": "Total of Entered Lines",
|
||||||
"enteringcreditmemo": "You are entering a credit memo. Please ensure you are also entering positive values.",
|
"enteringcreditmemo": "You are entering a credit memo. Please ensure you are also entering positive values.",
|
||||||
"federal_tax": "Federal Tax",
|
"federal_tax": "Federal Tax",
|
||||||
|
"federal_tax_exempt": "Federal Tax Exempt?",
|
||||||
"generatepartslabel": "Generate Parts Labels after Saving?",
|
"generatepartslabel": "Generate Parts Labels after Saving?",
|
||||||
"iouexists": "An IOU exists that is associated to this RO.",
|
"iouexists": "An IOU exists that is associated to this RO.",
|
||||||
"local_tax": "Local Tax",
|
"local_tax": "Local Tax",
|
||||||
@@ -213,6 +215,7 @@
|
|||||||
"new": "New Bill",
|
"new": "New Bill",
|
||||||
"noneselected": "No bill selected.",
|
"noneselected": "No bill selected.",
|
||||||
"onlycmforinvoiced": "Only credit memos can be entered for any Job that has been invoiced, exported, or voided.",
|
"onlycmforinvoiced": "Only credit memos can be entered for any Job that has been invoiced, exported, or voided.",
|
||||||
|
"printlabels": "Print Labels",
|
||||||
"retailtotal": "Bills Retail Total",
|
"retailtotal": "Bills Retail Total",
|
||||||
"savewithdiscrepancy": "You are about to save this bill with a discrepancy. The system will continue to use the calculated amount using the bill lines. Press cancel to return to the bill.",
|
"savewithdiscrepancy": "You are about to save this bill with a discrepancy. The system will continue to use the calculated amount using the bill lines. Press cancel to return to the bill.",
|
||||||
"state_tax": "Provincial/State Tax",
|
"state_tax": "Provincial/State Tax",
|
||||||
@@ -252,7 +255,6 @@
|
|||||||
"saving": "Error encountered while saving. {{message}}"
|
"saving": "Error encountered while saving. {{message}}"
|
||||||
},
|
},
|
||||||
"fields": {
|
"fields": {
|
||||||
"ReceivableCustomField": "QBO Receivable Custom Field {{number}}",
|
|
||||||
"address1": "Address 1",
|
"address1": "Address 1",
|
||||||
"address2": "Address 2",
|
"address2": "Address 2",
|
||||||
"appt_alt_transport": "Appointment Alternative Transportation Options",
|
"appt_alt_transport": "Appointment Alternative Transportation Options",
|
||||||
@@ -329,6 +331,9 @@
|
|||||||
"md_ded_notes": "Deductible Notes",
|
"md_ded_notes": "Deductible Notes",
|
||||||
"md_email_cc": "Auto Email CC: $t(printcenter.subjects.jobs.{{template}})",
|
"md_email_cc": "Auto Email CC: $t(printcenter.subjects.jobs.{{template}})",
|
||||||
"md_from_emails": "Additional From Emails",
|
"md_from_emails": "Additional From Emails",
|
||||||
|
"md_functionality_toggles": {
|
||||||
|
"parts_queue_toggle": "Auto Add Imported/Supplemented Jobs to Parts Queue"
|
||||||
|
},
|
||||||
"md_hour_split": {
|
"md_hour_split": {
|
||||||
"paint": "Paint Hour Split",
|
"paint": "Paint Hour Split",
|
||||||
"prep": "Prep Hour Split"
|
"prep": "Prep Hour Split"
|
||||||
@@ -351,9 +356,6 @@
|
|||||||
},
|
},
|
||||||
"md_payment_types": "Payment Types",
|
"md_payment_types": "Payment Types",
|
||||||
"md_referral_sources": "Referral Sources",
|
"md_referral_sources": "Referral Sources",
|
||||||
"md_functionality_toggles": {
|
|
||||||
"parts_queue_toggle": "Auto Add Imported/Supplemented Jobs to Parts Queue"
|
|
||||||
},
|
|
||||||
"md_tasks_presets": {
|
"md_tasks_presets": {
|
||||||
"hourstype": "",
|
"hourstype": "",
|
||||||
"memo": "",
|
"memo": "",
|
||||||
@@ -449,6 +451,7 @@
|
|||||||
"config": "Shop -> Config",
|
"config": "Shop -> Config",
|
||||||
"dashboard": "Shop -> Dashboard",
|
"dashboard": "Shop -> Dashboard",
|
||||||
"rbac": "Shop -> RBAC",
|
"rbac": "Shop -> RBAC",
|
||||||
|
"reportcenter": "Shop -> Report Center",
|
||||||
"templates": "Shop -> Templates",
|
"templates": "Shop -> Templates",
|
||||||
"vendors": "Shop -> Vendors"
|
"vendors": "Shop -> Vendors"
|
||||||
},
|
},
|
||||||
@@ -470,6 +473,7 @@
|
|||||||
"editaccess": "Users -> Edit access"
|
"editaccess": "Users -> Edit access"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"ReceivableCustomField": "QBO Receivable Custom Field {{number}}",
|
||||||
"responsibilitycenter": "Responsibility Center",
|
"responsibilitycenter": "Responsibility Center",
|
||||||
"responsibilitycenter_accountdesc": "Account Description",
|
"responsibilitycenter_accountdesc": "Account Description",
|
||||||
"responsibilitycenter_accountitem": "Item",
|
"responsibilitycenter_accountitem": "Item",
|
||||||
@@ -743,6 +747,7 @@
|
|||||||
"driverinformation": "Driver's Information",
|
"driverinformation": "Driver's Information",
|
||||||
"findcontract": "Find Contract",
|
"findcontract": "Find Contract",
|
||||||
"findermodal": "Contract Finder",
|
"findermodal": "Contract Finder",
|
||||||
|
"insuranceexpired": "The courtesy car insurance expires before the car is expected to return.",
|
||||||
"noteconvertedfrom": "R.O. created from converted Courtesy Car Contract {{agreementnumber}}.",
|
"noteconvertedfrom": "R.O. created from converted Courtesy Car Contract {{agreementnumber}}.",
|
||||||
"populatefromjob": "Populate from Job",
|
"populatefromjob": "Populate from Job",
|
||||||
"rates": "Contract Rates",
|
"rates": "Contract Rates",
|
||||||
@@ -783,6 +788,7 @@
|
|||||||
"notes": "Notes",
|
"notes": "Notes",
|
||||||
"plate": "Plate Number",
|
"plate": "Plate Number",
|
||||||
"purchasedate": "Purchase Date",
|
"purchasedate": "Purchase Date",
|
||||||
|
"readiness": "Readiness",
|
||||||
"registrationexpires": "Registration Expires On",
|
"registrationexpires": "Registration Expires On",
|
||||||
"serviceenddate": "Usage End Date",
|
"serviceenddate": "Usage End Date",
|
||||||
"servicestartdate": "Usage Start Date",
|
"servicestartdate": "Usage Start Date",
|
||||||
@@ -810,6 +816,10 @@
|
|||||||
"usage": "Usage",
|
"usage": "Usage",
|
||||||
"vehicle": "Vehicle Description"
|
"vehicle": "Vehicle Description"
|
||||||
},
|
},
|
||||||
|
"readiness": {
|
||||||
|
"notready": "Not Ready",
|
||||||
|
"ready": "Ready"
|
||||||
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"in": "Available",
|
"in": "Available",
|
||||||
"inservice": "In Service",
|
"inservice": "In Service",
|
||||||
@@ -829,20 +839,27 @@
|
|||||||
"creating": "Error creating survey {{message}}",
|
"creating": "Error creating survey {{message}}",
|
||||||
"notconfigured": "You do not have any current CSI Question Sets configured.",
|
"notconfigured": "You do not have any current CSI Question Sets configured.",
|
||||||
"notfoundsubtitle": "We were unable to find a survey using the link you provided. Please ensure the URL is correct or reach out to your shop for more help.",
|
"notfoundsubtitle": "We were unable to find a survey using the link you provided. Please ensure the URL is correct or reach out to your shop for more help.",
|
||||||
"notfoundtitle": "No survey found."
|
"notfoundtitle": "No survey found.",
|
||||||
|
"surveycompletetitle": "Survey previously completed",
|
||||||
|
"surveycompletesubtitle": "This survey was already completed on {{date}}."
|
||||||
},
|
},
|
||||||
"fields": {
|
"fields": {
|
||||||
"completedon": "Completed On",
|
"completedon": "Completed On",
|
||||||
"created_at": "Created At"
|
"created_at": "Created At",
|
||||||
|
"surveyid": "Survey ID {{surveyId}}",
|
||||||
|
"validuntil": "Valid Until"
|
||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
"nologgedinuser": "Please log out of ImEX Online",
|
"nologgedinuser": "Please log out of $t(titles.app)",
|
||||||
"nologgedinuser_sub": "Users of ImEX Online cannot complete CSI surveys while logged in. Please log out and try again.",
|
"nologgedinuser_sub": "Users of $t(titles.app) cannot complete CSI surveys while logged in. Please log out and try again.",
|
||||||
"noneselected": "No response selected.",
|
"noneselected": "No response selected.",
|
||||||
"title": "Customer Satisfaction Survey"
|
"title": "Customer Satisfaction Survey",
|
||||||
|
"greeting": "Hi {{name}}!",
|
||||||
|
"intro": "At {{shopname}}, we value your feedback. We would love to hear what you have to say. Please fill out the form below.",
|
||||||
|
"copyright": "Copyright © $t(titles.app). All Rights Reserved."
|
||||||
},
|
},
|
||||||
"successes": {
|
"successes": {
|
||||||
"created": "CSI created successfully. ",
|
"created": "CSI created successfully.",
|
||||||
"submitted": "Your responses have been submitted successfully.",
|
"submitted": "Your responses have been submitted successfully.",
|
||||||
"submittedsub": "Your input is highly appreciated."
|
"submittedsub": "Your input is highly appreciated."
|
||||||
}
|
}
|
||||||
@@ -858,6 +875,7 @@
|
|||||||
"labels": {
|
"labels": {
|
||||||
"bodyhrs": "Body Hrs",
|
"bodyhrs": "Body Hrs",
|
||||||
"dollarsinproduction": "Dollars in Production",
|
"dollarsinproduction": "Dollars in Production",
|
||||||
|
"phone": "Phone",
|
||||||
"prodhrs": "Production Hrs",
|
"prodhrs": "Production Hrs",
|
||||||
"refhrs": "Refinish Hrs"
|
"refhrs": "Refinish Hrs"
|
||||||
},
|
},
|
||||||
@@ -873,8 +891,10 @@
|
|||||||
"productiondollars": "Total Dollars in Production",
|
"productiondollars": "Total Dollars in Production",
|
||||||
"productionhours": "Total Hours in Production",
|
"productionhours": "Total Hours in Production",
|
||||||
"projectedmonthlysales": "Projected Monthly Sales",
|
"projectedmonthlysales": "Projected Monthly Sales",
|
||||||
"scheduledintoday": "Sheduled In Today: {{date}}",
|
"scheduledindate": "Sheduled In Today: {{date}}",
|
||||||
"scheduledouttoday": "Sheduled Out Today: {{date}}"
|
"scheduledintoday": "Sheduled In Today",
|
||||||
|
"scheduledoutdate": "Sheduled Out Today: {{date}}",
|
||||||
|
"scheduledouttoday": "Sheduled Out Today"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dms": {
|
"dms": {
|
||||||
@@ -993,10 +1013,13 @@
|
|||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
"actions": "Actions",
|
"actions": "Actions",
|
||||||
|
"active": "Active",
|
||||||
"endmustbeafterstart": "End date must be after start date.",
|
"endmustbeafterstart": "End date must be after start date.",
|
||||||
"flat_rate": "Flat Rate",
|
"flat_rate": "Flat Rate",
|
||||||
|
"inactive": "Inactive",
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
"rate_type": "Rate Type",
|
"rate_type": "Rate Type",
|
||||||
|
"status": "Status",
|
||||||
"straight_time": "Straight Time"
|
"straight_time": "Straight Time"
|
||||||
},
|
},
|
||||||
"successes": {
|
"successes": {
|
||||||
@@ -1202,6 +1225,31 @@
|
|||||||
"updated": "Inventory line updated."
|
"updated": "Inventory line updated."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"job_lifecycle": {
|
||||||
|
"columns": {
|
||||||
|
"duration": "Duration",
|
||||||
|
"end": "End",
|
||||||
|
"relative_end": "Relative End",
|
||||||
|
"relative_start": "Relative Start",
|
||||||
|
"start": "Start",
|
||||||
|
"value": "Value"
|
||||||
|
},
|
||||||
|
"content": {
|
||||||
|
"current_status_accumulated_time": "Current Status Accumulated Time",
|
||||||
|
"data_unavailable": " There is currently no Lifecycle data for this Job.",
|
||||||
|
"legend_title": "Legend",
|
||||||
|
"loading": "Loading Job Timelines....",
|
||||||
|
"not_available": "N/A",
|
||||||
|
"previous_status_accumulated_time": "Previous Status Accumulated Time",
|
||||||
|
"title": "Job Lifecycle Component",
|
||||||
|
"title_durations": "Historical Status Durations",
|
||||||
|
"title_loading": "Loading",
|
||||||
|
"title_transitions": "Transitions"
|
||||||
|
},
|
||||||
|
"errors": {
|
||||||
|
"fetch": "Error getting Job Lifecycle Data"
|
||||||
|
}
|
||||||
|
},
|
||||||
"job_payments": {
|
"job_payments": {
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"goback": "Go Back",
|
"goback": "Go Back",
|
||||||
@@ -1688,6 +1736,7 @@
|
|||||||
"ca_gst_all_if_null": "If the Job is marked as a \"GST Registrant\" and this value is set to $0, the customer will be responsible for paying all of the GST by default. ",
|
"ca_gst_all_if_null": "If the Job is marked as a \"GST Registrant\" and this value is set to $0, the customer will be responsible for paying all of the GST by default. ",
|
||||||
"calc_repair_days": "Calculated Repair Days",
|
"calc_repair_days": "Calculated Repair Days",
|
||||||
"calc_repair_days_tt": "This is the approximate number of days required to complete the repair according to the target touch time in your shop configuration (current set to {{target_touchtime}}).",
|
"calc_repair_days_tt": "This is the approximate number of days required to complete the repair according to the target touch time in your shop configuration (current set to {{target_touchtime}}).",
|
||||||
|
"calc_scheuled_completion": "Calculate Scheduled Completion",
|
||||||
"cards": {
|
"cards": {
|
||||||
"customer": "Customer Information",
|
"customer": "Customer Information",
|
||||||
"damage": "Area of Damage",
|
"damage": "Area of Damage",
|
||||||
@@ -1696,6 +1745,7 @@
|
|||||||
"estimator": "Estimator",
|
"estimator": "Estimator",
|
||||||
"filehandler": "File Handler",
|
"filehandler": "File Handler",
|
||||||
"insurance": "Insurance Details",
|
"insurance": "Insurance Details",
|
||||||
|
"more": "More",
|
||||||
"notes": "Notes",
|
"notes": "Notes",
|
||||||
"parts": "Parts",
|
"parts": "Parts",
|
||||||
"totals": "Totals",
|
"totals": "Totals",
|
||||||
@@ -1784,6 +1834,7 @@
|
|||||||
"override_header": "Override estimate header on import?",
|
"override_header": "Override estimate header on import?",
|
||||||
"ownerassociation": "Owner Association",
|
"ownerassociation": "Owner Association",
|
||||||
"parts": "Parts",
|
"parts": "Parts",
|
||||||
|
"parts_lines": "Parts Lines",
|
||||||
"parts_received": "Parts Rec.",
|
"parts_received": "Parts Rec.",
|
||||||
"parts_tax_rates": "Parts Tax rates",
|
"parts_tax_rates": "Parts Tax rates",
|
||||||
"partsfilter": "Parts Only",
|
"partsfilter": "Parts Only",
|
||||||
@@ -1821,6 +1872,7 @@
|
|||||||
},
|
},
|
||||||
"reconciliationheader": "Parts & Sublet Reconciliation",
|
"reconciliationheader": "Parts & Sublet Reconciliation",
|
||||||
"relatedros": "Related ROs",
|
"relatedros": "Related ROs",
|
||||||
|
"remove_from_ar": "Remove from AR",
|
||||||
"returntotals": "Return Totals",
|
"returntotals": "Return Totals",
|
||||||
"rosaletotal": "RO Parts Total",
|
"rosaletotal": "RO Parts Total",
|
||||||
"sale_additional": "Sales - Additional",
|
"sale_additional": "Sales - Additional",
|
||||||
@@ -1844,6 +1896,7 @@
|
|||||||
"total_sales": "Total Sales",
|
"total_sales": "Total Sales",
|
||||||
"totals": "Totals",
|
"totals": "Totals",
|
||||||
"unvoidnote": "This Job was unvoided.",
|
"unvoidnote": "This Job was unvoided.",
|
||||||
|
"update_scheduled_completion": "Update Scheduled Completion?",
|
||||||
"vehicle_info": "Vehicle",
|
"vehicle_info": "Vehicle",
|
||||||
"vehicleassociation": "Vehicle Association",
|
"vehicleassociation": "Vehicle Association",
|
||||||
"viewallocations": "View Allocations",
|
"viewallocations": "View Allocations",
|
||||||
@@ -2000,6 +2053,7 @@
|
|||||||
"general": "General",
|
"general": "General",
|
||||||
"insurance": "Insurance Information",
|
"insurance": "Insurance Information",
|
||||||
"labor": "Labor",
|
"labor": "Labor",
|
||||||
|
"lifecycle": "Lifecycle",
|
||||||
"partssublet": "Parts & Bills",
|
"partssublet": "Parts & Bills",
|
||||||
"rates": "Rates",
|
"rates": "Rates",
|
||||||
"repairdata": "Repair Data",
|
"repairdata": "Repair Data",
|
||||||
@@ -2016,7 +2070,7 @@
|
|||||||
"joblookup": "Job Lookup",
|
"joblookup": "Job Lookup",
|
||||||
"login": "Login",
|
"login": "Login",
|
||||||
"logout": "Logout",
|
"logout": "Logout",
|
||||||
"productionboard": "Production Board - Visual",
|
"productionboard": "Production Visual",
|
||||||
"productionlist": "Production List",
|
"productionlist": "Production List",
|
||||||
"shiftclockin": "Shift Clock"
|
"shiftclockin": "Shift Clock"
|
||||||
}
|
}
|
||||||
@@ -2046,6 +2100,9 @@
|
|||||||
"sentby": "Sent by {{by}} at {{time}}",
|
"sentby": "Sent by {{by}} at {{time}}",
|
||||||
"typeamessage": "Send a message...",
|
"typeamessage": "Send a message...",
|
||||||
"unarchive": "Unarchive"
|
"unarchive": "Unarchive"
|
||||||
|
},
|
||||||
|
"render": {
|
||||||
|
"conversation_list": "Conversation List"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"notes": {
|
"notes": {
|
||||||
@@ -2521,6 +2578,16 @@
|
|||||||
"generate": "Generate"
|
"generate": "Generate"
|
||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
|
"advanced_filters": "Advanced Filters and Sorters",
|
||||||
|
"advanced_filters_show": "Show",
|
||||||
|
"advanced_filters_hide": "Hide",
|
||||||
|
"advanced_filters_filters": "Filters",
|
||||||
|
"advanced_filters_sorters": "Sorters",
|
||||||
|
"advanced_filters_filter_field": "Field",
|
||||||
|
"advanced_filters_sorter_field": "Field",
|
||||||
|
"advanced_filters_sorter_direction": "Direction",
|
||||||
|
"advanced_filters_filter_operator": "Operator",
|
||||||
|
"advanced_filters_filter_value": "Value",
|
||||||
"dates": "Dates",
|
"dates": "Dates",
|
||||||
"employee": "Employee",
|
"employee": "Employee",
|
||||||
"filterson": "Filters on {{object}}: {{field}}",
|
"filterson": "Filters on {{object}}: {{field}}",
|
||||||
@@ -2548,6 +2615,7 @@
|
|||||||
},
|
},
|
||||||
"templates": {
|
"templates": {
|
||||||
"anticipated_revenue": "Anticipated Revenue",
|
"anticipated_revenue": "Anticipated Revenue",
|
||||||
|
"ar_aging": "AR Aging",
|
||||||
"attendance_detail": "Attendance (All Employees)",
|
"attendance_detail": "Attendance (All Employees)",
|
||||||
"attendance_employee": "Employee Attendance",
|
"attendance_employee": "Employee Attendance",
|
||||||
"attendance_summary": "Attendance Summary (All Employees)",
|
"attendance_summary": "Attendance Summary (All Employees)",
|
||||||
@@ -2610,6 +2678,7 @@
|
|||||||
"open_orders": "Open Orders by Date",
|
"open_orders": "Open Orders by Date",
|
||||||
"open_orders_csr": "Open Orders by CSR",
|
"open_orders_csr": "Open Orders by CSR",
|
||||||
"open_orders_estimator": "Open Orders by Estimator",
|
"open_orders_estimator": "Open Orders by Estimator",
|
||||||
|
"open_orders_excel": "Open Orders - Excel",
|
||||||
"open_orders_ins_co": "Open Orders by Insurance Company",
|
"open_orders_ins_co": "Open Orders by Insurance Company",
|
||||||
"open_orders_referral": "Open Orders by Referral Source",
|
"open_orders_referral": "Open Orders by Referral Source",
|
||||||
"open_orders_specific_csr": "Open Orders filtered by CSR",
|
"open_orders_specific_csr": "Open Orders filtered by CSR",
|
||||||
@@ -2697,6 +2766,7 @@
|
|||||||
"efficiencyoverperiod": "Efficiency over Selected Dates",
|
"efficiencyoverperiod": "Efficiency over Selected Dates",
|
||||||
"entries": "Scoreboard Entries",
|
"entries": "Scoreboard Entries",
|
||||||
"jobs": "Jobs",
|
"jobs": "Jobs",
|
||||||
|
"jobscompletednotinvoiced": "Completed Not Invoiced",
|
||||||
"lastmonth": "Last Month",
|
"lastmonth": "Last Month",
|
||||||
"lastweek": "Last Week",
|
"lastweek": "Last Week",
|
||||||
"monthlytarget": "Monthly",
|
"monthlytarget": "Monthly",
|
||||||
@@ -2711,6 +2781,7 @@
|
|||||||
"timetickets": "Time Tickets",
|
"timetickets": "Time Tickets",
|
||||||
"timeticketsemployee": "Time Tickets by Employee",
|
"timeticketsemployee": "Time Tickets by Employee",
|
||||||
"todateactual": "Actual (MTD)",
|
"todateactual": "Actual (MTD)",
|
||||||
|
"totalhrs": "Total Hours",
|
||||||
"totaloverperiod": "Total over Selected Dates",
|
"totaloverperiod": "Total over Selected Dates",
|
||||||
"weeklyactual": "Actual (W)",
|
"weeklyactual": "Actual (W)",
|
||||||
"weeklytarget": "Weekly",
|
"weeklytarget": "Weekly",
|
||||||
@@ -2888,7 +2959,7 @@
|
|||||||
"parts-queue": "Parts Queue | $t(titles.app)",
|
"parts-queue": "Parts Queue | $t(titles.app)",
|
||||||
"payments-all": "Payments | $t(titles.app)",
|
"payments-all": "Payments | $t(titles.app)",
|
||||||
"phonebook": "Phonebook | $t(titles.app)",
|
"phonebook": "Phonebook | $t(titles.app)",
|
||||||
"productionboard": "Production - Board",
|
"productionboard": "Production Board - Visual | $t(titles.app)",
|
||||||
"productionlist": "Production Board - List | $t(titles.app)",
|
"productionlist": "Production Board - List | $t(titles.app)",
|
||||||
"profile": "My Profile | $t(titles.app)",
|
"profile": "My Profile | $t(titles.app)",
|
||||||
"readyjobs": "Ready Jobs | $t(titles.app)",
|
"readyjobs": "Ready Jobs | $t(titles.app)",
|
||||||
@@ -2900,6 +2971,10 @@
|
|||||||
"shop-csi": "CSI Responses | $t(titles.app)",
|
"shop-csi": "CSI Responses | $t(titles.app)",
|
||||||
"shop-templates": "Shop Templates | $t(titles.app)",
|
"shop-templates": "Shop Templates | $t(titles.app)",
|
||||||
"shop_vendors": "Vendors | $t(titles.app)",
|
"shop_vendors": "Vendors | $t(titles.app)",
|
||||||
|
"techconsole": "Technician Console | $t(titles.app)",
|
||||||
|
"techjobclock": "Technician Job Clock | $t(titles.app)",
|
||||||
|
"techjoblookup": "Technician Job Lookup | $t(titles.app)",
|
||||||
|
"techshiftclock": "Technician Shift Clock | $t(titles.app)",
|
||||||
"temporarydocs": "Temporary Documents | $t(titles.app)",
|
"temporarydocs": "Temporary Documents | $t(titles.app)",
|
||||||
"timetickets": "Time Tickets | $t(titles.app)",
|
"timetickets": "Time Tickets | $t(titles.app)",
|
||||||
"ttapprovals": "",
|
"ttapprovals": "",
|
||||||
|
|||||||
@@ -99,6 +99,7 @@
|
|||||||
},
|
},
|
||||||
"audit_trail": {
|
"audit_trail": {
|
||||||
"messages": {
|
"messages": {
|
||||||
|
"admin_job_remove_from_ar": "",
|
||||||
"admin_jobmarkexported": "",
|
"admin_jobmarkexported": "",
|
||||||
"admin_jobmarkforreexport": "",
|
"admin_jobmarkforreexport": "",
|
||||||
"admin_jobuninvoice": "",
|
"admin_jobuninvoice": "",
|
||||||
@@ -112,11 +113,11 @@
|
|||||||
"jobassignmentchange": "",
|
"jobassignmentchange": "",
|
||||||
"jobassignmentremoved": "",
|
"jobassignmentremoved": "",
|
||||||
"jobchecklist": "",
|
"jobchecklist": "",
|
||||||
"jobinvoiced": "",
|
|
||||||
"jobconverted": "",
|
"jobconverted": "",
|
||||||
"jobfieldchanged": "",
|
"jobfieldchanged": "",
|
||||||
"jobimported": "",
|
"jobimported": "",
|
||||||
"jobinproductionchange": "",
|
"jobinproductionchange": "",
|
||||||
|
"jobinvoiced": "",
|
||||||
"jobioucreated": "",
|
"jobioucreated": "",
|
||||||
"jobmodifylbradj": "",
|
"jobmodifylbradj": "",
|
||||||
"jobnoteadded": "",
|
"jobnoteadded": "",
|
||||||
@@ -205,6 +206,7 @@
|
|||||||
"entered_total": "",
|
"entered_total": "",
|
||||||
"enteringcreditmemo": "",
|
"enteringcreditmemo": "",
|
||||||
"federal_tax": "",
|
"federal_tax": "",
|
||||||
|
"federal_tax_exempt": "",
|
||||||
"generatepartslabel": "",
|
"generatepartslabel": "",
|
||||||
"iouexists": "",
|
"iouexists": "",
|
||||||
"local_tax": "",
|
"local_tax": "",
|
||||||
@@ -213,6 +215,7 @@
|
|||||||
"new": "",
|
"new": "",
|
||||||
"noneselected": "",
|
"noneselected": "",
|
||||||
"onlycmforinvoiced": "",
|
"onlycmforinvoiced": "",
|
||||||
|
"printlabels": "",
|
||||||
"retailtotal": "",
|
"retailtotal": "",
|
||||||
"savewithdiscrepancy": "",
|
"savewithdiscrepancy": "",
|
||||||
"state_tax": "",
|
"state_tax": "",
|
||||||
@@ -252,13 +255,9 @@
|
|||||||
"saving": ""
|
"saving": ""
|
||||||
},
|
},
|
||||||
"fields": {
|
"fields": {
|
||||||
"ReceivableCustomField": "",
|
|
||||||
"address1": "",
|
"address1": "",
|
||||||
"address2": "",
|
"address2": "",
|
||||||
"appt_alt_transport": "",
|
"appt_alt_transport": "",
|
||||||
"md_functionality_toggles": {
|
|
||||||
"parts_queue_toggle": ""
|
|
||||||
},
|
|
||||||
"appt_colors": {
|
"appt_colors": {
|
||||||
"color": "",
|
"color": "",
|
||||||
"label": ""
|
"label": ""
|
||||||
@@ -332,6 +331,9 @@
|
|||||||
"md_ded_notes": "",
|
"md_ded_notes": "",
|
||||||
"md_email_cc": "",
|
"md_email_cc": "",
|
||||||
"md_from_emails": "",
|
"md_from_emails": "",
|
||||||
|
"md_functionality_toggles": {
|
||||||
|
"parts_queue_toggle": ""
|
||||||
|
},
|
||||||
"md_hour_split": {
|
"md_hour_split": {
|
||||||
"paint": "",
|
"paint": "",
|
||||||
"prep": ""
|
"prep": ""
|
||||||
@@ -449,6 +451,7 @@
|
|||||||
"config": "",
|
"config": "",
|
||||||
"dashboard": "",
|
"dashboard": "",
|
||||||
"rbac": "",
|
"rbac": "",
|
||||||
|
"reportcenter": "",
|
||||||
"templates": "",
|
"templates": "",
|
||||||
"vendors": ""
|
"vendors": ""
|
||||||
},
|
},
|
||||||
@@ -470,6 +473,7 @@
|
|||||||
"editaccess": ""
|
"editaccess": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"ReceivableCustomField": "",
|
||||||
"responsibilitycenter": "",
|
"responsibilitycenter": "",
|
||||||
"responsibilitycenter_accountdesc": "",
|
"responsibilitycenter_accountdesc": "",
|
||||||
"responsibilitycenter_accountitem": "",
|
"responsibilitycenter_accountitem": "",
|
||||||
@@ -743,6 +747,7 @@
|
|||||||
"driverinformation": "",
|
"driverinformation": "",
|
||||||
"findcontract": "",
|
"findcontract": "",
|
||||||
"findermodal": "",
|
"findermodal": "",
|
||||||
|
"insuranceexpired": "",
|
||||||
"noteconvertedfrom": "",
|
"noteconvertedfrom": "",
|
||||||
"populatefromjob": "",
|
"populatefromjob": "",
|
||||||
"rates": "",
|
"rates": "",
|
||||||
@@ -783,6 +788,7 @@
|
|||||||
"notes": "",
|
"notes": "",
|
||||||
"plate": "",
|
"plate": "",
|
||||||
"purchasedate": "",
|
"purchasedate": "",
|
||||||
|
"readiness": "",
|
||||||
"registrationexpires": "",
|
"registrationexpires": "",
|
||||||
"serviceenddate": "",
|
"serviceenddate": "",
|
||||||
"servicestartdate": "",
|
"servicestartdate": "",
|
||||||
@@ -810,6 +816,10 @@
|
|||||||
"usage": "",
|
"usage": "",
|
||||||
"vehicle": ""
|
"vehicle": ""
|
||||||
},
|
},
|
||||||
|
"readiness": {
|
||||||
|
"notready": "",
|
||||||
|
"ready": ""
|
||||||
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"in": "",
|
"in": "",
|
||||||
"inservice": "",
|
"inservice": "",
|
||||||
@@ -829,17 +839,24 @@
|
|||||||
"creating": "",
|
"creating": "",
|
||||||
"notconfigured": "",
|
"notconfigured": "",
|
||||||
"notfoundsubtitle": "",
|
"notfoundsubtitle": "",
|
||||||
"notfoundtitle": ""
|
"notfoundtitle": "",
|
||||||
|
"surveycompletetitle": "",
|
||||||
|
"surveycompletesubtitle": ""
|
||||||
},
|
},
|
||||||
"fields": {
|
"fields": {
|
||||||
"completedon": "",
|
"completedon": "",
|
||||||
"created_at": ""
|
"created_at": "",
|
||||||
|
"surveyid": "",
|
||||||
|
"validuntil": ""
|
||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
"nologgedinuser": "",
|
"nologgedinuser": "",
|
||||||
"nologgedinuser_sub": "",
|
"nologgedinuser_sub": "",
|
||||||
"noneselected": "",
|
"noneselected": "",
|
||||||
"title": ""
|
"title": "",
|
||||||
|
"greeting": "",
|
||||||
|
"intro": "",
|
||||||
|
"copyright": ""
|
||||||
},
|
},
|
||||||
"successes": {
|
"successes": {
|
||||||
"created": "",
|
"created": "",
|
||||||
@@ -858,6 +875,7 @@
|
|||||||
"labels": {
|
"labels": {
|
||||||
"bodyhrs": "",
|
"bodyhrs": "",
|
||||||
"dollarsinproduction": "",
|
"dollarsinproduction": "",
|
||||||
|
"phone": "",
|
||||||
"prodhrs": "",
|
"prodhrs": "",
|
||||||
"refhrs": ""
|
"refhrs": ""
|
||||||
},
|
},
|
||||||
@@ -873,7 +891,9 @@
|
|||||||
"productiondollars": "",
|
"productiondollars": "",
|
||||||
"productionhours": "",
|
"productionhours": "",
|
||||||
"projectedmonthlysales": "",
|
"projectedmonthlysales": "",
|
||||||
|
"scheduledindate": "",
|
||||||
"scheduledintoday": "",
|
"scheduledintoday": "",
|
||||||
|
"scheduledoutdate": "",
|
||||||
"scheduledouttoday": ""
|
"scheduledouttoday": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -993,10 +1013,13 @@
|
|||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
"actions": "",
|
"actions": "",
|
||||||
|
"active": "",
|
||||||
"endmustbeafterstart": "",
|
"endmustbeafterstart": "",
|
||||||
"flat_rate": "",
|
"flat_rate": "",
|
||||||
|
"inactive": "",
|
||||||
"name": "",
|
"name": "",
|
||||||
"rate_type": "",
|
"rate_type": "",
|
||||||
|
"status": "",
|
||||||
"straight_time": ""
|
"straight_time": ""
|
||||||
},
|
},
|
||||||
"successes": {
|
"successes": {
|
||||||
@@ -1202,6 +1225,31 @@
|
|||||||
"updated": ""
|
"updated": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"job_lifecycle": {
|
||||||
|
"columns": {
|
||||||
|
"duration": "",
|
||||||
|
"end": "",
|
||||||
|
"relative_end": "",
|
||||||
|
"relative_start": "",
|
||||||
|
"start": "",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"content": {
|
||||||
|
"current_status_accumulated_time": "",
|
||||||
|
"data_unavailable": "",
|
||||||
|
"legend_title": "",
|
||||||
|
"loading": "",
|
||||||
|
"not_available": "",
|
||||||
|
"previous_status_accumulated_time": "",
|
||||||
|
"title": "",
|
||||||
|
"title_durations": "",
|
||||||
|
"title_loading": "",
|
||||||
|
"title_transitions": ""
|
||||||
|
},
|
||||||
|
"errors": {
|
||||||
|
"fetch": "Error al obtener los datos del ciclo de vida del trabajo"
|
||||||
|
}
|
||||||
|
},
|
||||||
"job_payments": {
|
"job_payments": {
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"goback": "",
|
"goback": "",
|
||||||
@@ -1688,6 +1736,7 @@
|
|||||||
"ca_gst_all_if_null": "",
|
"ca_gst_all_if_null": "",
|
||||||
"calc_repair_days": "",
|
"calc_repair_days": "",
|
||||||
"calc_repair_days_tt": "",
|
"calc_repair_days_tt": "",
|
||||||
|
"calc_scheuled_completion": "",
|
||||||
"cards": {
|
"cards": {
|
||||||
"customer": "Información al cliente",
|
"customer": "Información al cliente",
|
||||||
"damage": "Área de Daño",
|
"damage": "Área de Daño",
|
||||||
@@ -1696,6 +1745,7 @@
|
|||||||
"estimator": "Estimador",
|
"estimator": "Estimador",
|
||||||
"filehandler": "File Handler",
|
"filehandler": "File Handler",
|
||||||
"insurance": "detalles del seguro",
|
"insurance": "detalles del seguro",
|
||||||
|
"more": "Más",
|
||||||
"notes": "Notas",
|
"notes": "Notas",
|
||||||
"parts": "Partes",
|
"parts": "Partes",
|
||||||
"totals": "Totales",
|
"totals": "Totales",
|
||||||
@@ -1784,6 +1834,7 @@
|
|||||||
"override_header": "¿Anular encabezado estimado al importar?",
|
"override_header": "¿Anular encabezado estimado al importar?",
|
||||||
"ownerassociation": "",
|
"ownerassociation": "",
|
||||||
"parts": "Partes",
|
"parts": "Partes",
|
||||||
|
"parts_lines": "",
|
||||||
"parts_received": "",
|
"parts_received": "",
|
||||||
"parts_tax_rates": "",
|
"parts_tax_rates": "",
|
||||||
"partsfilter": "",
|
"partsfilter": "",
|
||||||
@@ -1821,6 +1872,7 @@
|
|||||||
},
|
},
|
||||||
"reconciliationheader": "",
|
"reconciliationheader": "",
|
||||||
"relatedros": "",
|
"relatedros": "",
|
||||||
|
"remove_from_ar": "",
|
||||||
"returntotals": "",
|
"returntotals": "",
|
||||||
"rosaletotal": "",
|
"rosaletotal": "",
|
||||||
"sale_additional": "",
|
"sale_additional": "",
|
||||||
@@ -1844,6 +1896,7 @@
|
|||||||
"total_sales": "",
|
"total_sales": "",
|
||||||
"totals": "",
|
"totals": "",
|
||||||
"unvoidnote": "",
|
"unvoidnote": "",
|
||||||
|
"update_scheduled_completion": "",
|
||||||
"vehicle_info": "Vehículo",
|
"vehicle_info": "Vehículo",
|
||||||
"vehicleassociation": "",
|
"vehicleassociation": "",
|
||||||
"viewallocations": "",
|
"viewallocations": "",
|
||||||
@@ -2000,6 +2053,7 @@
|
|||||||
"general": "",
|
"general": "",
|
||||||
"insurance": "",
|
"insurance": "",
|
||||||
"labor": "Labor",
|
"labor": "Labor",
|
||||||
|
"lifecycle": "",
|
||||||
"partssublet": "Piezas / Subarrendamiento",
|
"partssublet": "Piezas / Subarrendamiento",
|
||||||
"rates": "",
|
"rates": "",
|
||||||
"repairdata": "Datos de reparación",
|
"repairdata": "Datos de reparación",
|
||||||
@@ -2046,6 +2100,9 @@
|
|||||||
"sentby": "",
|
"sentby": "",
|
||||||
"typeamessage": "Enviar un mensaje...",
|
"typeamessage": "Enviar un mensaje...",
|
||||||
"unarchive": ""
|
"unarchive": ""
|
||||||
|
},
|
||||||
|
"render": {
|
||||||
|
"conversation_list": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"notes": {
|
"notes": {
|
||||||
@@ -2521,6 +2578,16 @@
|
|||||||
"generate": ""
|
"generate": ""
|
||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
|
"advanced_filters": "",
|
||||||
|
"advanced_filters_show": "",
|
||||||
|
"advanced_filters_hide": "",
|
||||||
|
"advanced_filters_filters": "",
|
||||||
|
"advanced_filters_sorters": "",
|
||||||
|
"advanced_filters_filter_field": "",
|
||||||
|
"advanced_filters_sorter_field": "",
|
||||||
|
"advanced_filters_sorter_direction": "",
|
||||||
|
"advanced_filters_filter_operator": "",
|
||||||
|
"advanced_filters_filter_value": "",
|
||||||
"dates": "",
|
"dates": "",
|
||||||
"employee": "",
|
"employee": "",
|
||||||
"filterson": "",
|
"filterson": "",
|
||||||
@@ -2548,6 +2615,7 @@
|
|||||||
},
|
},
|
||||||
"templates": {
|
"templates": {
|
||||||
"anticipated_revenue": "",
|
"anticipated_revenue": "",
|
||||||
|
"ar_aging": "",
|
||||||
"attendance_detail": "",
|
"attendance_detail": "",
|
||||||
"attendance_employee": "",
|
"attendance_employee": "",
|
||||||
"attendance_summary": "",
|
"attendance_summary": "",
|
||||||
@@ -2610,6 +2678,7 @@
|
|||||||
"open_orders": "",
|
"open_orders": "",
|
||||||
"open_orders_csr": "",
|
"open_orders_csr": "",
|
||||||
"open_orders_estimator": "",
|
"open_orders_estimator": "",
|
||||||
|
"open_orders_excel": "",
|
||||||
"open_orders_ins_co": "",
|
"open_orders_ins_co": "",
|
||||||
"open_orders_referral": "",
|
"open_orders_referral": "",
|
||||||
"open_orders_specific_csr": "",
|
"open_orders_specific_csr": "",
|
||||||
@@ -2697,6 +2766,7 @@
|
|||||||
"efficiencyoverperiod": "",
|
"efficiencyoverperiod": "",
|
||||||
"entries": "",
|
"entries": "",
|
||||||
"jobs": "",
|
"jobs": "",
|
||||||
|
"jobscompletednotinvoiced": "",
|
||||||
"lastmonth": "",
|
"lastmonth": "",
|
||||||
"lastweek": "",
|
"lastweek": "",
|
||||||
"monthlytarget": "",
|
"monthlytarget": "",
|
||||||
@@ -2711,6 +2781,7 @@
|
|||||||
"timetickets": "",
|
"timetickets": "",
|
||||||
"timeticketsemployee": "",
|
"timeticketsemployee": "",
|
||||||
"todateactual": "",
|
"todateactual": "",
|
||||||
|
"totalhrs": "",
|
||||||
"totaloverperiod": "",
|
"totaloverperiod": "",
|
||||||
"weeklyactual": "",
|
"weeklyactual": "",
|
||||||
"weeklytarget": "",
|
"weeklytarget": "",
|
||||||
@@ -2881,7 +2952,7 @@
|
|||||||
"jobs-intake": "",
|
"jobs-intake": "",
|
||||||
"jobsavailable": "Empleos disponibles | $t(titles.app)",
|
"jobsavailable": "Empleos disponibles | $t(titles.app)",
|
||||||
"jobsdetail": "Trabajo {{ro_number}} | $t(titles.app)",
|
"jobsdetail": "Trabajo {{ro_number}} | $t(titles.app)",
|
||||||
"jobsdocuments": "Documentos de trabajo {{ro_number}} | $ t (títulos.app)",
|
"jobsdocuments": "Documentos de trabajo {{ro_number}} | $t(titles.app)",
|
||||||
"manageroot": "Casa | $t(titles.app)",
|
"manageroot": "Casa | $t(titles.app)",
|
||||||
"owners": "Todos los propietarios | $t(titles.app)",
|
"owners": "Todos los propietarios | $t(titles.app)",
|
||||||
"owners-detail": "",
|
"owners-detail": "",
|
||||||
@@ -2900,6 +2971,10 @@
|
|||||||
"shop-csi": "",
|
"shop-csi": "",
|
||||||
"shop-templates": "",
|
"shop-templates": "",
|
||||||
"shop_vendors": "Vendedores | $t(titles.app)",
|
"shop_vendors": "Vendedores | $t(titles.app)",
|
||||||
|
"techconsole": "$t(titles.app)",
|
||||||
|
"techjobclock": "$t(titles.app)",
|
||||||
|
"techjoblookup": "$t(titles.app)",
|
||||||
|
"techshiftclock": "$t(titles.app)",
|
||||||
"temporarydocs": "",
|
"temporarydocs": "",
|
||||||
"timetickets": "",
|
"timetickets": "",
|
||||||
"ttapprovals": "",
|
"ttapprovals": "",
|
||||||
|
|||||||
@@ -99,6 +99,7 @@
|
|||||||
},
|
},
|
||||||
"audit_trail": {
|
"audit_trail": {
|
||||||
"messages": {
|
"messages": {
|
||||||
|
"admin_job_remove_from_ar": "",
|
||||||
"admin_jobmarkexported": "",
|
"admin_jobmarkexported": "",
|
||||||
"admin_jobmarkforreexport": "",
|
"admin_jobmarkforreexport": "",
|
||||||
"admin_jobuninvoice": "",
|
"admin_jobuninvoice": "",
|
||||||
@@ -112,11 +113,11 @@
|
|||||||
"jobassignmentchange": "",
|
"jobassignmentchange": "",
|
||||||
"jobassignmentremoved": "",
|
"jobassignmentremoved": "",
|
||||||
"jobchecklist": "",
|
"jobchecklist": "",
|
||||||
"jobinvoiced": "",
|
|
||||||
"jobconverted": "",
|
"jobconverted": "",
|
||||||
"jobfieldchanged": "",
|
"jobfieldchanged": "",
|
||||||
"jobimported": "",
|
"jobimported": "",
|
||||||
"jobinproductionchange": "",
|
"jobinproductionchange": "",
|
||||||
|
"jobinvoiced": "",
|
||||||
"jobioucreated": "",
|
"jobioucreated": "",
|
||||||
"jobmodifylbradj": "",
|
"jobmodifylbradj": "",
|
||||||
"jobnoteadded": "",
|
"jobnoteadded": "",
|
||||||
@@ -205,6 +206,7 @@
|
|||||||
"entered_total": "",
|
"entered_total": "",
|
||||||
"enteringcreditmemo": "",
|
"enteringcreditmemo": "",
|
||||||
"federal_tax": "",
|
"federal_tax": "",
|
||||||
|
"federal_tax_exempt": "",
|
||||||
"generatepartslabel": "",
|
"generatepartslabel": "",
|
||||||
"iouexists": "",
|
"iouexists": "",
|
||||||
"local_tax": "",
|
"local_tax": "",
|
||||||
@@ -213,6 +215,7 @@
|
|||||||
"new": "",
|
"new": "",
|
||||||
"noneselected": "",
|
"noneselected": "",
|
||||||
"onlycmforinvoiced": "",
|
"onlycmforinvoiced": "",
|
||||||
|
"printlabels": "",
|
||||||
"retailtotal": "",
|
"retailtotal": "",
|
||||||
"savewithdiscrepancy": "",
|
"savewithdiscrepancy": "",
|
||||||
"state_tax": "",
|
"state_tax": "",
|
||||||
@@ -252,7 +255,6 @@
|
|||||||
"saving": ""
|
"saving": ""
|
||||||
},
|
},
|
||||||
"fields": {
|
"fields": {
|
||||||
"ReceivableCustomField": "",
|
|
||||||
"address1": "",
|
"address1": "",
|
||||||
"address2": "",
|
"address2": "",
|
||||||
"appt_alt_transport": "",
|
"appt_alt_transport": "",
|
||||||
@@ -329,13 +331,13 @@
|
|||||||
"md_ded_notes": "",
|
"md_ded_notes": "",
|
||||||
"md_email_cc": "",
|
"md_email_cc": "",
|
||||||
"md_from_emails": "",
|
"md_from_emails": "",
|
||||||
|
"md_functionality_toggles": {
|
||||||
|
"parts_queue_toggle": ""
|
||||||
|
},
|
||||||
"md_hour_split": {
|
"md_hour_split": {
|
||||||
"paint": "",
|
"paint": "",
|
||||||
"prep": ""
|
"prep": ""
|
||||||
},
|
},
|
||||||
"md_functionality_toggles": {
|
|
||||||
"parts_queue_toggle": ""
|
|
||||||
},
|
|
||||||
"md_ins_co": {
|
"md_ins_co": {
|
||||||
"city": "",
|
"city": "",
|
||||||
"name": "",
|
"name": "",
|
||||||
@@ -449,6 +451,7 @@
|
|||||||
"config": "",
|
"config": "",
|
||||||
"dashboard": "",
|
"dashboard": "",
|
||||||
"rbac": "",
|
"rbac": "",
|
||||||
|
"reportcenter": "",
|
||||||
"templates": "",
|
"templates": "",
|
||||||
"vendors": ""
|
"vendors": ""
|
||||||
},
|
},
|
||||||
@@ -470,6 +473,7 @@
|
|||||||
"editaccess": ""
|
"editaccess": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"ReceivableCustomField": "",
|
||||||
"responsibilitycenter": "",
|
"responsibilitycenter": "",
|
||||||
"responsibilitycenter_accountdesc": "",
|
"responsibilitycenter_accountdesc": "",
|
||||||
"responsibilitycenter_accountitem": "",
|
"responsibilitycenter_accountitem": "",
|
||||||
@@ -743,6 +747,7 @@
|
|||||||
"driverinformation": "",
|
"driverinformation": "",
|
||||||
"findcontract": "",
|
"findcontract": "",
|
||||||
"findermodal": "",
|
"findermodal": "",
|
||||||
|
"insuranceexpired": "",
|
||||||
"noteconvertedfrom": "",
|
"noteconvertedfrom": "",
|
||||||
"populatefromjob": "",
|
"populatefromjob": "",
|
||||||
"rates": "",
|
"rates": "",
|
||||||
@@ -783,6 +788,7 @@
|
|||||||
"notes": "",
|
"notes": "",
|
||||||
"plate": "",
|
"plate": "",
|
||||||
"purchasedate": "",
|
"purchasedate": "",
|
||||||
|
"readiness": "",
|
||||||
"registrationexpires": "",
|
"registrationexpires": "",
|
||||||
"serviceenddate": "",
|
"serviceenddate": "",
|
||||||
"servicestartdate": "",
|
"servicestartdate": "",
|
||||||
@@ -810,6 +816,10 @@
|
|||||||
"usage": "",
|
"usage": "",
|
||||||
"vehicle": ""
|
"vehicle": ""
|
||||||
},
|
},
|
||||||
|
"readiness": {
|
||||||
|
"notready": "",
|
||||||
|
"ready": ""
|
||||||
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"in": "",
|
"in": "",
|
||||||
"inservice": "",
|
"inservice": "",
|
||||||
@@ -829,17 +839,24 @@
|
|||||||
"creating": "",
|
"creating": "",
|
||||||
"notconfigured": "",
|
"notconfigured": "",
|
||||||
"notfoundsubtitle": "",
|
"notfoundsubtitle": "",
|
||||||
"notfoundtitle": ""
|
"notfoundtitle": "",
|
||||||
|
"surveycompletetitle": "",
|
||||||
|
"surveycompletesubtitle": ""
|
||||||
},
|
},
|
||||||
"fields": {
|
"fields": {
|
||||||
"completedon": "",
|
"completedon": "",
|
||||||
"created_at": ""
|
"created_at": "",
|
||||||
|
"surveyid": "",
|
||||||
|
"validuntil": ""
|
||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
"nologgedinuser": "",
|
"nologgedinuser": "",
|
||||||
"nologgedinuser_sub": "",
|
"nologgedinuser_sub": "",
|
||||||
"noneselected": "",
|
"noneselected": "",
|
||||||
"title": ""
|
"title": "",
|
||||||
|
"greeting": "",
|
||||||
|
"intro": "",
|
||||||
|
"copyright": ""
|
||||||
},
|
},
|
||||||
"successes": {
|
"successes": {
|
||||||
"created": "",
|
"created": "",
|
||||||
@@ -858,6 +875,7 @@
|
|||||||
"labels": {
|
"labels": {
|
||||||
"bodyhrs": "",
|
"bodyhrs": "",
|
||||||
"dollarsinproduction": "",
|
"dollarsinproduction": "",
|
||||||
|
"phone": "",
|
||||||
"prodhrs": "",
|
"prodhrs": "",
|
||||||
"refhrs": ""
|
"refhrs": ""
|
||||||
},
|
},
|
||||||
@@ -873,7 +891,9 @@
|
|||||||
"productiondollars": "",
|
"productiondollars": "",
|
||||||
"productionhours": "",
|
"productionhours": "",
|
||||||
"projectedmonthlysales": "",
|
"projectedmonthlysales": "",
|
||||||
|
"scheduledindate": "",
|
||||||
"scheduledintoday": "",
|
"scheduledintoday": "",
|
||||||
|
"scheduledoutdate": "",
|
||||||
"scheduledouttoday": ""
|
"scheduledouttoday": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -993,10 +1013,13 @@
|
|||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
"actions": "",
|
"actions": "",
|
||||||
|
"active": "",
|
||||||
"endmustbeafterstart": "",
|
"endmustbeafterstart": "",
|
||||||
"flat_rate": "",
|
"flat_rate": "",
|
||||||
|
"inactive": "",
|
||||||
"name": "",
|
"name": "",
|
||||||
"rate_type": "",
|
"rate_type": "",
|
||||||
|
"status": "",
|
||||||
"straight_time": ""
|
"straight_time": ""
|
||||||
},
|
},
|
||||||
"successes": {
|
"successes": {
|
||||||
@@ -1202,6 +1225,31 @@
|
|||||||
"updated": ""
|
"updated": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"job_lifecycle": {
|
||||||
|
"columns": {
|
||||||
|
"duration": "",
|
||||||
|
"end": "",
|
||||||
|
"relative_end": "",
|
||||||
|
"relative_start": "",
|
||||||
|
"start": "",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"content": {
|
||||||
|
"current_status_accumulated_time": "",
|
||||||
|
"data_unavailable": "",
|
||||||
|
"legend_title": "",
|
||||||
|
"loading": "",
|
||||||
|
"not_available": "",
|
||||||
|
"previous_status_accumulated_time": "",
|
||||||
|
"title": "",
|
||||||
|
"title_durations": "",
|
||||||
|
"title_loading": "",
|
||||||
|
"title_transitions": ""
|
||||||
|
},
|
||||||
|
"errors": {
|
||||||
|
"fetch": "Erreur lors de l'obtention des données du cycle de vie des tâches"
|
||||||
|
}
|
||||||
|
},
|
||||||
"job_payments": {
|
"job_payments": {
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"goback": "",
|
"goback": "",
|
||||||
@@ -1688,6 +1736,7 @@
|
|||||||
"ca_gst_all_if_null": "",
|
"ca_gst_all_if_null": "",
|
||||||
"calc_repair_days": "",
|
"calc_repair_days": "",
|
||||||
"calc_repair_days_tt": "",
|
"calc_repair_days_tt": "",
|
||||||
|
"calc_scheuled_completion": "",
|
||||||
"cards": {
|
"cards": {
|
||||||
"customer": "Informations client",
|
"customer": "Informations client",
|
||||||
"damage": "Zone de dommages",
|
"damage": "Zone de dommages",
|
||||||
@@ -1696,6 +1745,7 @@
|
|||||||
"estimator": "Estimateur",
|
"estimator": "Estimateur",
|
||||||
"filehandler": "Gestionnaire de fichiers",
|
"filehandler": "Gestionnaire de fichiers",
|
||||||
"insurance": "Détails de l'assurance",
|
"insurance": "Détails de l'assurance",
|
||||||
|
"more": "Plus",
|
||||||
"notes": "Remarques",
|
"notes": "Remarques",
|
||||||
"parts": "les pièces",
|
"parts": "les pièces",
|
||||||
"totals": "Totaux",
|
"totals": "Totaux",
|
||||||
@@ -1784,6 +1834,7 @@
|
|||||||
"override_header": "Remplacer l'en-tête d'estimation à l'importation?",
|
"override_header": "Remplacer l'en-tête d'estimation à l'importation?",
|
||||||
"ownerassociation": "",
|
"ownerassociation": "",
|
||||||
"parts": "les pièces",
|
"parts": "les pièces",
|
||||||
|
"parts_lines": "",
|
||||||
"parts_received": "",
|
"parts_received": "",
|
||||||
"parts_tax_rates": "",
|
"parts_tax_rates": "",
|
||||||
"partsfilter": "",
|
"partsfilter": "",
|
||||||
@@ -1821,6 +1872,7 @@
|
|||||||
},
|
},
|
||||||
"reconciliationheader": "",
|
"reconciliationheader": "",
|
||||||
"relatedros": "",
|
"relatedros": "",
|
||||||
|
"remove_from_ar": "",
|
||||||
"returntotals": "",
|
"returntotals": "",
|
||||||
"rosaletotal": "",
|
"rosaletotal": "",
|
||||||
"sale_additional": "",
|
"sale_additional": "",
|
||||||
@@ -1844,6 +1896,7 @@
|
|||||||
"total_sales": "",
|
"total_sales": "",
|
||||||
"totals": "",
|
"totals": "",
|
||||||
"unvoidnote": "",
|
"unvoidnote": "",
|
||||||
|
"update_scheduled_completion": "",
|
||||||
"vehicle_info": "Véhicule",
|
"vehicle_info": "Véhicule",
|
||||||
"vehicleassociation": "",
|
"vehicleassociation": "",
|
||||||
"viewallocations": "",
|
"viewallocations": "",
|
||||||
@@ -2000,6 +2053,7 @@
|
|||||||
"general": "",
|
"general": "",
|
||||||
"insurance": "",
|
"insurance": "",
|
||||||
"labor": "La main d'oeuvre",
|
"labor": "La main d'oeuvre",
|
||||||
|
"lifecycle": "",
|
||||||
"partssublet": "Pièces / Sous-location",
|
"partssublet": "Pièces / Sous-location",
|
||||||
"rates": "",
|
"rates": "",
|
||||||
"repairdata": "Données de réparation",
|
"repairdata": "Données de réparation",
|
||||||
@@ -2046,6 +2100,9 @@
|
|||||||
"sentby": "",
|
"sentby": "",
|
||||||
"typeamessage": "Envoyer un message...",
|
"typeamessage": "Envoyer un message...",
|
||||||
"unarchive": ""
|
"unarchive": ""
|
||||||
|
},
|
||||||
|
"render": {
|
||||||
|
"conversation_list": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"notes": {
|
"notes": {
|
||||||
@@ -2521,6 +2578,16 @@
|
|||||||
"generate": ""
|
"generate": ""
|
||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
|
"advanced_filters": "",
|
||||||
|
"advanced_filters_show": "",
|
||||||
|
"advanced_filters_hide": "",
|
||||||
|
"advanced_filters_filters": "",
|
||||||
|
"advanced_filters_sorters": "",
|
||||||
|
"advanced_filters_filter_field": "",
|
||||||
|
"advanced_filters_sorter_field": "",
|
||||||
|
"advanced_filters_sorter_direction": "",
|
||||||
|
"advanced_filters_filter_operator": "",
|
||||||
|
"advanced_filters_filter_value": "",
|
||||||
"dates": "",
|
"dates": "",
|
||||||
"employee": "",
|
"employee": "",
|
||||||
"filterson": "",
|
"filterson": "",
|
||||||
@@ -2548,6 +2615,7 @@
|
|||||||
},
|
},
|
||||||
"templates": {
|
"templates": {
|
||||||
"anticipated_revenue": "",
|
"anticipated_revenue": "",
|
||||||
|
"ar_aging": "",
|
||||||
"attendance_detail": "",
|
"attendance_detail": "",
|
||||||
"attendance_employee": "",
|
"attendance_employee": "",
|
||||||
"attendance_summary": "",
|
"attendance_summary": "",
|
||||||
@@ -2610,6 +2678,7 @@
|
|||||||
"open_orders": "",
|
"open_orders": "",
|
||||||
"open_orders_csr": "",
|
"open_orders_csr": "",
|
||||||
"open_orders_estimator": "",
|
"open_orders_estimator": "",
|
||||||
|
"open_orders_excel": "",
|
||||||
"open_orders_ins_co": "",
|
"open_orders_ins_co": "",
|
||||||
"open_orders_referral": "",
|
"open_orders_referral": "",
|
||||||
"open_orders_specific_csr": "",
|
"open_orders_specific_csr": "",
|
||||||
@@ -2697,6 +2766,7 @@
|
|||||||
"efficiencyoverperiod": "",
|
"efficiencyoverperiod": "",
|
||||||
"entries": "",
|
"entries": "",
|
||||||
"jobs": "",
|
"jobs": "",
|
||||||
|
"jobscompletednotinvoiced": "",
|
||||||
"lastmonth": "",
|
"lastmonth": "",
|
||||||
"lastweek": "",
|
"lastweek": "",
|
||||||
"monthlytarget": "",
|
"monthlytarget": "",
|
||||||
@@ -2711,6 +2781,7 @@
|
|||||||
"timetickets": "",
|
"timetickets": "",
|
||||||
"timeticketsemployee": "",
|
"timeticketsemployee": "",
|
||||||
"todateactual": "",
|
"todateactual": "",
|
||||||
|
"totalhrs": "",
|
||||||
"totaloverperiod": "",
|
"totaloverperiod": "",
|
||||||
"weeklyactual": "",
|
"weeklyactual": "",
|
||||||
"weeklytarget": "",
|
"weeklytarget": "",
|
||||||
@@ -2881,7 +2952,7 @@
|
|||||||
"jobs-intake": "",
|
"jobs-intake": "",
|
||||||
"jobsavailable": "Emplois disponibles | $t(titles.app)",
|
"jobsavailable": "Emplois disponibles | $t(titles.app)",
|
||||||
"jobsdetail": "Travail {{ro_number}} | $t(titles.app)",
|
"jobsdetail": "Travail {{ro_number}} | $t(titles.app)",
|
||||||
"jobsdocuments": "Documents de travail {{ro_number}} | $ t (titres.app)",
|
"jobsdocuments": "Documents de travail {{ro_number}} | $t(titles.app)",
|
||||||
"manageroot": "Accueil | $t(titles.app)",
|
"manageroot": "Accueil | $t(titles.app)",
|
||||||
"owners": "Tous les propriétaires | $t(titles.app)",
|
"owners": "Tous les propriétaires | $t(titles.app)",
|
||||||
"owners-detail": "",
|
"owners-detail": "",
|
||||||
@@ -2900,6 +2971,10 @@
|
|||||||
"shop-csi": "",
|
"shop-csi": "",
|
||||||
"shop-templates": "",
|
"shop-templates": "",
|
||||||
"shop_vendors": "Vendeurs | $t(titles.app)",
|
"shop_vendors": "Vendeurs | $t(titles.app)",
|
||||||
|
"techconsole": "$t(titles.app)",
|
||||||
|
"techjobclock": "$t(titles.app)",
|
||||||
|
"techjoblookup": "$t(titles.app)",
|
||||||
|
"techshiftclock": "$t(titles.app)",
|
||||||
"temporarydocs": "",
|
"temporarydocs": "",
|
||||||
"timetickets": "",
|
"timetickets": "",
|
||||||
"ttapprovals": "",
|
"ttapprovals": "",
|
||||||
|
|||||||
@@ -1,54 +1,56 @@
|
|||||||
import i18n from "i18next";
|
import i18n from "i18next";
|
||||||
|
|
||||||
const AuditTrailMapping = {
|
const AuditTrailMapping = {
|
||||||
alertToggle: (status) => i18n.t("audit_trail.messages.alerttoggle", { status }),
|
admin_job_remove_from_ar: (status) =>
|
||||||
|
i18n.t("audit_trail.messages.admin_job_remove_from_ar", { status }),
|
||||||
|
admin_jobfieldchange: (field, value) =>
|
||||||
|
"ADMIN: " +
|
||||||
|
i18n.t("audit_trail.messages.jobfieldchanged", { field, value }),
|
||||||
|
admin_jobmarkexported: () =>
|
||||||
|
i18n.t("audit_trail.messages.admin_jobmarkexported"),
|
||||||
|
admin_jobmarkforreexport: () =>
|
||||||
|
i18n.t("audit_trail.messages.admin_jobmarkforreexport"),
|
||||||
|
admin_jobstatuschange: (status) =>
|
||||||
|
"ADMIN: " + i18n.t("audit_trail.messages.jobstatuschange", { status }),
|
||||||
|
admin_jobuninvoice: () => i18n.t("audit_trail.messages.admin_jobuninvoice"),
|
||||||
|
admin_jobunvoid: () => i18n.t("audit_trail.messages.admin_jobunvoid"),
|
||||||
|
alertToggle: (status) =>
|
||||||
|
i18n.t("audit_trail.messages.alerttoggle", { status }),
|
||||||
appointmentcancel: (lost_sale_reason) =>
|
appointmentcancel: (lost_sale_reason) =>
|
||||||
i18n.t("audit_trail.messages.appointmentcancel", { lost_sale_reason }),
|
i18n.t("audit_trail.messages.appointmentcancel", { lost_sale_reason }),
|
||||||
appointmentinsert: (start) =>
|
appointmentinsert: (start) =>
|
||||||
i18n.t("audit_trail.messages.appointmentinsert", { start }),
|
i18n.t("audit_trail.messages.appointmentinsert", { start }),
|
||||||
jobstatuschange: (status) =>
|
|
||||||
i18n.t("audit_trail.messages.jobstatuschange", { status }),
|
|
||||||
admin_jobstatuschange: (status) =>
|
|
||||||
"ADMIN: " + i18n.t("audit_trail.messages.jobstatuschange", { status }),
|
|
||||||
jobsupplement: () => i18n.t("audit_trail.messages.jobsupplement"),
|
|
||||||
jobimported: () => i18n.t("audit_trail.messages.jobimported"),
|
|
||||||
jobinvoiced: () =>
|
|
||||||
i18n.t("audit_trail.messages.jobinvoiced"),
|
|
||||||
jobconverted: (ro_number) =>
|
|
||||||
i18n.t("audit_trail.messages.jobconverted", { ro_number }),
|
|
||||||
jobfieldchange: (field, value) =>
|
|
||||||
i18n.t("audit_trail.messages.jobfieldchanged", { field, value }),
|
|
||||||
admin_jobfieldchange: (field, value) =>
|
|
||||||
"ADMIN: " +
|
|
||||||
i18n.t("audit_trail.messages.jobfieldchanged", { field, value }),
|
|
||||||
jobspartsorder: (order_number) =>
|
|
||||||
i18n.t("audit_trail.messages.jobspartsorder", { order_number }),
|
|
||||||
jobspartsreturn: (order_number) =>
|
|
||||||
i18n.t("audit_trail.messages.jobspartsreturn", { order_number }),
|
|
||||||
jobmodifylbradj: ({ mod_lbr_ty, hours }) =>
|
|
||||||
i18n.t("audit_trail.messages.jobmodifylbradj", { mod_lbr_ty, hours }),
|
|
||||||
billposted: (invoice_number) =>
|
billposted: (invoice_number) =>
|
||||||
i18n.t("audit_trail.messages.billposted", { invoice_number }),
|
i18n.t("audit_trail.messages.billposted", { invoice_number }),
|
||||||
billupdated: (invoice_number) =>
|
billupdated: (invoice_number) =>
|
||||||
i18n.t("audit_trail.messages.billupdated", { invoice_number }),
|
i18n.t("audit_trail.messages.billupdated", { invoice_number }),
|
||||||
|
failedpayment: () => i18n.t("audit_trail.messages.failedpayment"),
|
||||||
jobassignmentchange: (operation, name) =>
|
jobassignmentchange: (operation, name) =>
|
||||||
i18n.t("audit_trail.messages.jobassignmentchange", { operation, name }),
|
i18n.t("audit_trail.messages.jobassignmentchange", { operation, name }),
|
||||||
jobassignmentremoved: (operation) =>
|
jobassignmentremoved: (operation) =>
|
||||||
i18n.t("audit_trail.messages.jobassignmentremoved", { operation }),
|
i18n.t("audit_trail.messages.jobassignmentremoved", { operation }),
|
||||||
jobinproductionchange: (inproduction) =>
|
|
||||||
i18n.t("audit_trail.messages.jobinproductionchange", { inproduction }),
|
|
||||||
jobchecklist: (type, inproduction, status) =>
|
jobchecklist: (type, inproduction, status) =>
|
||||||
i18n.t("audit_trail.messages.jobchecklist", { type, inproduction, status }),
|
i18n.t("audit_trail.messages.jobchecklist", { type, inproduction, status }),
|
||||||
|
jobconverted: (ro_number) =>
|
||||||
|
i18n.t("audit_trail.messages.jobconverted", { ro_number }),
|
||||||
|
jobfieldchange: (field, value) =>
|
||||||
|
i18n.t("audit_trail.messages.jobfieldchanged", { field, value }),
|
||||||
|
jobimported: () => i18n.t("audit_trail.messages.jobimported"),
|
||||||
|
jobinproductionchange: (inproduction) =>
|
||||||
|
i18n.t("audit_trail.messages.jobinproductionchange", { inproduction }),
|
||||||
|
jobinvoiced: () => i18n.t("audit_trail.messages.jobinvoiced"),
|
||||||
|
jobmodifylbradj: ({ mod_lbr_ty, hours }) =>
|
||||||
|
i18n.t("audit_trail.messages.jobmodifylbradj", { mod_lbr_ty, hours }),
|
||||||
jobnoteadded: () => i18n.t("audit_trail.messages.jobnoteadded"),
|
jobnoteadded: () => i18n.t("audit_trail.messages.jobnoteadded"),
|
||||||
jobnoteupdated: () => i18n.t("audit_trail.messages.jobnoteupdated"),
|
|
||||||
jobnotedeleted: () => i18n.t("audit_trail.messages.jobnotedeleted"),
|
jobnotedeleted: () => i18n.t("audit_trail.messages.jobnotedeleted"),
|
||||||
admin_jobunvoid: () => i18n.t("audit_trail.messages.admin_jobunvoid"),
|
jobnoteupdated: () => i18n.t("audit_trail.messages.jobnoteupdated"),
|
||||||
admin_jobuninvoice: () => i18n.t("audit_trail.messages.admin_jobuninvoice"),
|
jobspartsorder: (order_number) =>
|
||||||
admin_jobmarkforreexport: () =>
|
i18n.t("audit_trail.messages.jobspartsorder", { order_number }),
|
||||||
i18n.t("audit_trail.messages.admin_jobmarkforreexport"),
|
jobspartsreturn: (order_number) =>
|
||||||
admin_jobmarkexported: () =>
|
i18n.t("audit_trail.messages.jobspartsreturn", { order_number }),
|
||||||
i18n.t("audit_trail.messages.admin_jobmarkexported"),
|
jobstatuschange: (status) =>
|
||||||
failedpayment: () => i18n.t("audit_trail.messages.failedpayment"),
|
i18n.t("audit_trail.messages.jobstatuschange", { status }),
|
||||||
|
jobsupplement: () => i18n.t("audit_trail.messages.jobsupplement"),
|
||||||
};
|
};
|
||||||
|
|
||||||
export default AuditTrailMapping;
|
export default AuditTrailMapping;
|
||||||
|
|||||||
@@ -17,6 +17,9 @@ export function DateTimeFormatter(props) {
|
|||||||
)
|
)
|
||||||
: null;
|
: null;
|
||||||
}
|
}
|
||||||
|
export function DateTimeFormatterFunction(date) {
|
||||||
|
return moment(date).format("MM/DD/YYYY hh:mm a");
|
||||||
|
}
|
||||||
export function TimeFormatter(props) {
|
export function TimeFormatter(props) {
|
||||||
return props.children
|
return props.children
|
||||||
? moment(props.children).format(props.format ? props.format : "hh:mm a")
|
? moment(props.children).format(props.format ? props.format : "hh:mm a")
|
||||||
|
|||||||
@@ -24,4 +24,13 @@ const range = {
|
|||||||
],
|
],
|
||||||
"Last 90 Days": [moment().add(-90, "days"), moment()],
|
"Last 90 Days": [moment().add(-90, "days"), moment()],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// We are development, lets get crazy
|
||||||
|
if (process.env.NODE_ENV === "development") {
|
||||||
|
range["Last year"] = [
|
||||||
|
moment().subtract(1, "year"),
|
||||||
|
moment(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
export default range;
|
export default range;
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ import apolloLogger from "apollo-link-logger";
|
|||||||
//import axios from "axios";
|
//import axios from "axios";
|
||||||
import { auth } from "../firebase/firebase.utils";
|
import { auth } from "../firebase/firebase.utils";
|
||||||
import errorLink from "../graphql/apollo-error-handling";
|
import errorLink from "../graphql/apollo-error-handling";
|
||||||
|
import { SentryLink } from "apollo-link-sentry";
|
||||||
|
|
||||||
//import { store } from "../redux/store";
|
//import { store } from "../redux/store";
|
||||||
const httpLink = new HttpLink({
|
const httpLink = new HttpLink({
|
||||||
uri: process.env.REACT_APP_GRAPHQL_ENDPOINT,
|
uri: process.env.REACT_APP_GRAPHQL_ENDPOINT,
|
||||||
@@ -105,18 +107,30 @@ const link = split(
|
|||||||
const authLink = setContext((_, { headers }) => {
|
const authLink = setContext((_, { headers }) => {
|
||||||
return (
|
return (
|
||||||
auth.currentUser &&
|
auth.currentUser &&
|
||||||
auth.currentUser.getIdToken().then((token) => {
|
auth.currentUser
|
||||||
if (token) {
|
.getIdToken()
|
||||||
return {
|
.then((token) => {
|
||||||
headers: {
|
if (token) {
|
||||||
...headers,
|
return {
|
||||||
authorization: token ? `Bearer ${token}` : "",
|
headers: {
|
||||||
},
|
...headers,
|
||||||
};
|
authorization: token ? `Bearer ${token}` : "",
|
||||||
} else {
|
},
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
console.error(
|
||||||
|
"Authentication error. Unable to add authorization token because it was empty."
|
||||||
|
);
|
||||||
|
return { headers };
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error(
|
||||||
|
"Authentication error. Unable to add authorization token.",
|
||||||
|
error.message
|
||||||
|
);
|
||||||
return { headers };
|
return { headers };
|
||||||
}
|
})
|
||||||
})
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -138,8 +152,10 @@ if (process.env.NODE_ENV === "development") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
middlewares.push(
|
middlewares.push(
|
||||||
roundTripLink.concat(
|
new SentryLink().concat(
|
||||||
retryLink.concat(errorLink.concat(authLink.concat(link)))
|
roundTripLink.concat(
|
||||||
|
retryLink.concat(errorLink.concat(authLink.concat(link)))
|
||||||
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
import { gql } from "@apollo/client";
|
import {gql} from "@apollo/client";
|
||||||
import jsreport from "@jsreport/browser-client";
|
import jsreport from "@jsreport/browser-client";
|
||||||
import { notification } from "antd";
|
import {notification} from "antd";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import { auth } from "../firebase/firebase.utils";
|
import {auth} from "../firebase/firebase.utils";
|
||||||
import { setEmailOptions } from "../redux/email/email.actions";
|
import {setEmailOptions} from "../redux/email/email.actions";
|
||||||
import { store } from "../redux/store";
|
import {store} from "../redux/store";
|
||||||
import client from "../utils/GraphQLClient";
|
import client from "../utils/GraphQLClient";
|
||||||
import cleanAxios from "./CleanAxios";
|
import cleanAxios from "./CleanAxios";
|
||||||
import { TemplateList } from "./TemplateConstants";
|
import {TemplateList} from "./TemplateConstants";
|
||||||
|
import {generateTemplate} from "./graphQLmodifier";
|
||||||
|
|
||||||
const server = process.env.REACT_APP_REPORTS_SERVER_URL;
|
const server = process.env.REACT_APP_REPORTS_SERVER_URL;
|
||||||
|
|
||||||
jsreport.serverUrl = server;
|
jsreport.serverUrl = server;
|
||||||
@@ -16,11 +18,11 @@ jsreport.serverUrl = server;
|
|||||||
const Templates = TemplateList();
|
const Templates = TemplateList();
|
||||||
|
|
||||||
export default async function RenderTemplate(
|
export default async function RenderTemplate(
|
||||||
templateObject,
|
templateObject,
|
||||||
bodyshop,
|
bodyshop,
|
||||||
renderAsHtml = false,
|
renderAsHtml = false,
|
||||||
renderAsExcel = false,
|
renderAsExcel = false,
|
||||||
renderAsText = false
|
renderAsText = false
|
||||||
) {
|
) {
|
||||||
if (window.jsr3) {
|
if (window.jsr3) {
|
||||||
jsreport.serverUrl = "https://reports3.test.imex.online/";
|
jsreport.serverUrl = "https://reports3.test.imex.online/";
|
||||||
@@ -30,41 +32,41 @@ export default async function RenderTemplate(
|
|||||||
jsreport.headers["Authorization"] = jsrAuth;
|
jsreport.headers["Authorization"] = jsrAuth;
|
||||||
|
|
||||||
//Query assets that match the template name. Must be in format <<templateName>>.query
|
//Query assets that match the template name. Must be in format <<templateName>>.query
|
||||||
let { contextData, useShopSpecificTemplate } = await fetchContextData(
|
let {contextData, useShopSpecificTemplate} = await fetchContextData(
|
||||||
templateObject,
|
templateObject,
|
||||||
jsrAuth
|
jsrAuth
|
||||||
);
|
);
|
||||||
|
|
||||||
const { ignoreCustomMargins } = Templates[templateObject.name];
|
const {ignoreCustomMargins} = Templates[templateObject.name];
|
||||||
|
|
||||||
let reportRequest = {
|
let reportRequest = {
|
||||||
template: {
|
template: {
|
||||||
name: useShopSpecificTemplate
|
name: useShopSpecificTemplate
|
||||||
? `/${bodyshop.imexshopid}/${templateObject.name}`
|
? `/${bodyshop.imexshopid}/${templateObject.name}`
|
||||||
: `/${templateObject.name}`,
|
: `/${templateObject.name}`,
|
||||||
...(renderAsHtml
|
...(renderAsHtml
|
||||||
? {}
|
? {}
|
||||||
: {
|
: {
|
||||||
recipe: "chrome-pdf",
|
recipe: "chrome-pdf",
|
||||||
...(!ignoreCustomMargins && {
|
...(!ignoreCustomMargins && {
|
||||||
chrome: {
|
chrome: {
|
||||||
marginTop:
|
marginTop:
|
||||||
bodyshop.logo_img_path &&
|
bodyshop.logo_img_path &&
|
||||||
bodyshop.logo_img_path.headerMargin &&
|
bodyshop.logo_img_path.headerMargin &&
|
||||||
bodyshop.logo_img_path.headerMargin > 36
|
bodyshop.logo_img_path.headerMargin > 36
|
||||||
? bodyshop.logo_img_path.headerMargin
|
? bodyshop.logo_img_path.headerMargin
|
||||||
: "36px",
|
: "36px",
|
||||||
marginBottom:
|
marginBottom:
|
||||||
bodyshop.logo_img_path &&
|
bodyshop.logo_img_path &&
|
||||||
bodyshop.logo_img_path.footerMargin &&
|
bodyshop.logo_img_path.footerMargin &&
|
||||||
bodyshop.logo_img_path.footerMargin > 50
|
bodyshop.logo_img_path.footerMargin > 50
|
||||||
? bodyshop.logo_img_path.footerMargin
|
? bodyshop.logo_img_path.footerMargin
|
||||||
: "50px",
|
: "50px",
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
...(renderAsExcel ? { recipe: "html-to-xlsx" } : {}),
|
...(renderAsExcel ? {recipe: "html-to-xlsx"} : {}),
|
||||||
...(renderAsText ? { recipe: "text" } : {}),
|
...(renderAsText ? {recipe: "text"} : {}),
|
||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
...contextData,
|
...contextData,
|
||||||
@@ -73,7 +75,10 @@ export default async function RenderTemplate(
|
|||||||
headerpath: `/${bodyshop.imexshopid}/header.html`,
|
headerpath: `/${bodyshop.imexshopid}/header.html`,
|
||||||
footerpath: `/${bodyshop.imexshopid}/footer.html`,
|
footerpath: `/${bodyshop.imexshopid}/footer.html`,
|
||||||
bodyshop: bodyshop,
|
bodyshop: bodyshop,
|
||||||
offset: bodyshop.timezone, //moment().utcOffset(),
|
filters: templateObject?.filters,
|
||||||
|
sorters: templateObject?.sorters,
|
||||||
|
offset: bodyshop.timezone, //dayjs().utcOffset(),
|
||||||
|
defaultSorters: templateObject?.defaultSorters,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -82,8 +87,8 @@ export default async function RenderTemplate(
|
|||||||
|
|
||||||
if (!renderAsHtml) {
|
if (!renderAsHtml) {
|
||||||
render.download(
|
render.download(
|
||||||
(Templates[templateObject.name] &&
|
(Templates[templateObject.name] &&
|
||||||
Templates[templateObject.name].title) ||
|
Templates[templateObject.name].title) ||
|
||||||
""
|
""
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@@ -97,17 +102,17 @@ export default async function RenderTemplate(
|
|||||||
...(!ignoreCustomMargins && {
|
...(!ignoreCustomMargins && {
|
||||||
chrome: {
|
chrome: {
|
||||||
marginTop:
|
marginTop:
|
||||||
bodyshop.logo_img_path &&
|
bodyshop.logo_img_path &&
|
||||||
bodyshop.logo_img_path.headerMargin &&
|
bodyshop.logo_img_path.headerMargin &&
|
||||||
bodyshop.logo_img_path.headerMargin > 36
|
bodyshop.logo_img_path.headerMargin > 36
|
||||||
? bodyshop.logo_img_path.headerMargin
|
? bodyshop.logo_img_path.headerMargin
|
||||||
: "36px",
|
: "36px",
|
||||||
marginBottom:
|
marginBottom:
|
||||||
bodyshop.logo_img_path &&
|
bodyshop.logo_img_path &&
|
||||||
bodyshop.logo_img_path.footerMargin &&
|
bodyshop.logo_img_path.footerMargin &&
|
||||||
bodyshop.logo_img_path.footerMargin > 50
|
bodyshop.logo_img_path.footerMargin > 50
|
||||||
? bodyshop.logo_img_path.footerMargin
|
? bodyshop.logo_img_path.footerMargin
|
||||||
: "50px",
|
: "50px",
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
@@ -121,21 +126,21 @@ export default async function RenderTemplate(
|
|||||||
resolve({
|
resolve({
|
||||||
pdf,
|
pdf,
|
||||||
filename:
|
filename:
|
||||||
Templates[templateObject.name] &&
|
Templates[templateObject.name] &&
|
||||||
Templates[templateObject.name].title,
|
Templates[templateObject.name].title,
|
||||||
html,
|
html,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
notification["error"]({ message: JSON.stringify(error) });
|
notification["error"]({message: JSON.stringify(error)});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function RenderTemplates(
|
export async function RenderTemplates(
|
||||||
templateObjects,
|
templateObjects,
|
||||||
bodyshop,
|
bodyshop,
|
||||||
renderAsHtml = false
|
renderAsHtml = false
|
||||||
) {
|
) {
|
||||||
//Query assets that match the template name. Must be in format <<templateName>>.query
|
//Query assets that match the template name. Must be in format <<templateName>>.query
|
||||||
let unsortedTemplatesAndData = [];
|
let unsortedTemplatesAndData = [];
|
||||||
@@ -145,17 +150,17 @@ export async function RenderTemplates(
|
|||||||
|
|
||||||
templateObjects.forEach((template) => {
|
templateObjects.forEach((template) => {
|
||||||
proms.push(
|
proms.push(
|
||||||
(async () => {
|
(async () => {
|
||||||
let { contextData, useShopSpecificTemplate } = await fetchContextData(
|
let {contextData, useShopSpecificTemplate} = await fetchContextData(
|
||||||
template,
|
template,
|
||||||
jsrAuth
|
jsrAuth
|
||||||
);
|
);
|
||||||
unsortedTemplatesAndData.push({
|
unsortedTemplatesAndData.push({
|
||||||
templateObject: template,
|
templateObject: template,
|
||||||
contextData,
|
contextData,
|
||||||
useShopSpecificTemplate,
|
useShopSpecificTemplate,
|
||||||
});
|
});
|
||||||
})()
|
})()
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
await Promise.all(proms);
|
await Promise.all(proms);
|
||||||
@@ -172,8 +177,8 @@ export async function RenderTemplates(
|
|||||||
|
|
||||||
unsortedTemplatesAndData.sort(function (a, b) {
|
unsortedTemplatesAndData.sort(function (a, b) {
|
||||||
return (
|
return (
|
||||||
templateObjects.findIndex((x) => x.name === a.templateObject.name) -
|
templateObjects.findIndex((x) => x.name === a.templateObject.name) -
|
||||||
templateObjects.findIndex((x) => x.name === b.templateObject.name)
|
templateObjects.findIndex((x) => x.name === b.templateObject.name)
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
const templateAndData = unsortedTemplatesAndData;
|
const templateAndData = unsortedTemplatesAndData;
|
||||||
@@ -183,25 +188,25 @@ export async function RenderTemplates(
|
|||||||
let reportRequest = {
|
let reportRequest = {
|
||||||
template: {
|
template: {
|
||||||
name: rootTemplate.useShopSpecificTemplate
|
name: rootTemplate.useShopSpecificTemplate
|
||||||
? `/${bodyshop.imexshopid}/${rootTemplate.templateObject.name}`
|
? `/${bodyshop.imexshopid}/${rootTemplate.templateObject.name}`
|
||||||
: `/${rootTemplate.templateObject.name}`,
|
: `/${rootTemplate.templateObject.name}`,
|
||||||
...(renderAsHtml
|
...(renderAsHtml
|
||||||
? {}
|
? {}
|
||||||
: {
|
: {
|
||||||
recipe: "chrome-pdf",
|
recipe: "chrome-pdf",
|
||||||
chrome: {
|
chrome: {
|
||||||
marginTop:
|
marginTop:
|
||||||
bodyshop.logo_img_path &&
|
bodyshop.logo_img_path &&
|
||||||
bodyshop.logo_img_path.headerMargin &&
|
bodyshop.logo_img_path.headerMargin &&
|
||||||
bodyshop.logo_img_path.headerMargin > 36
|
bodyshop.logo_img_path.headerMargin > 36
|
||||||
? bodyshop.logo_img_path.headerMargin
|
? bodyshop.logo_img_path.headerMargin
|
||||||
: "36px",
|
: "36px",
|
||||||
marginBottom:
|
marginBottom:
|
||||||
bodyshop.logo_img_path &&
|
bodyshop.logo_img_path &&
|
||||||
bodyshop.logo_img_path.footerMargin &&
|
bodyshop.logo_img_path.footerMargin &&
|
||||||
bodyshop.logo_img_path.footerMargin > 50
|
bodyshop.logo_img_path.footerMargin > 50
|
||||||
? bodyshop.logo_img_path.footerMargin
|
? bodyshop.logo_img_path.footerMargin
|
||||||
: "50px",
|
: "50px",
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
pdfOperations: [
|
pdfOperations: [
|
||||||
@@ -218,22 +223,22 @@ export async function RenderTemplates(
|
|||||||
template: {
|
template: {
|
||||||
chrome: {
|
chrome: {
|
||||||
marginTop:
|
marginTop:
|
||||||
bodyshop.logo_img_path &&
|
bodyshop.logo_img_path &&
|
||||||
bodyshop.logo_img_path.headerMargin &&
|
bodyshop.logo_img_path.headerMargin &&
|
||||||
bodyshop.logo_img_path.headerMargin > 36
|
bodyshop.logo_img_path.headerMargin > 36
|
||||||
? bodyshop.logo_img_path.headerMargin
|
? bodyshop.logo_img_path.headerMargin
|
||||||
: "36px",
|
: "36px",
|
||||||
marginBottom:
|
marginBottom:
|
||||||
bodyshop.logo_img_path &&
|
bodyshop.logo_img_path &&
|
||||||
bodyshop.logo_img_path.footerMargin &&
|
bodyshop.logo_img_path.footerMargin &&
|
||||||
bodyshop.logo_img_path.footerMargin > 50
|
bodyshop.logo_img_path.footerMargin > 50
|
||||||
? bodyshop.logo_img_path.footerMargin
|
? bodyshop.logo_img_path.footerMargin
|
||||||
: "50px",
|
: "50px",
|
||||||
},
|
},
|
||||||
name: template.useShopSpecificTemplate
|
name: template.useShopSpecificTemplate
|
||||||
? `/${bodyshop.imexshopid}/${template.templateObject.name}`
|
? `/${bodyshop.imexshopid}/${template.templateObject.name}`
|
||||||
: `/${template.templateObject.name}`,
|
: `/${template.templateObject.name}`,
|
||||||
...(renderAsHtml ? {} : { recipe: "chrome-pdf" }),
|
...(renderAsHtml ? {} : {recipe: "chrome-pdf"}),
|
||||||
},
|
},
|
||||||
type: "append",
|
type: "append",
|
||||||
|
|
||||||
@@ -245,8 +250,8 @@ export async function RenderTemplates(
|
|||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
...extend(
|
...extend(
|
||||||
rootTemplate.contextData,
|
rootTemplate.contextData,
|
||||||
...templateAndData.map((temp) => temp.contextData)
|
...templateAndData.map((temp) => temp.contextData)
|
||||||
),
|
),
|
||||||
|
|
||||||
// ...rootTemplate.templateObject.variables,
|
// ...rootTemplate.templateObject.variables,
|
||||||
@@ -266,32 +271,33 @@ export async function RenderTemplates(
|
|||||||
return render.toString();
|
return render.toString();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
notification["error"]({ message: JSON.stringify(error) });
|
notification["error"]({message: JSON.stringify(error)});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const GenerateDocument = async (
|
export const GenerateDocument = async (
|
||||||
template,
|
template,
|
||||||
messageOptions,
|
messageOptions,
|
||||||
sendType,
|
sendType,
|
||||||
jobid
|
jobid
|
||||||
) => {
|
) => {
|
||||||
|
|
||||||
const bodyshop = store.getState().user.bodyshop;
|
const bodyshop = store.getState().user.bodyshop;
|
||||||
|
|
||||||
if (sendType === "e") {
|
if (sendType === "e") {
|
||||||
store.dispatch(
|
store.dispatch(
|
||||||
setEmailOptions({
|
setEmailOptions({
|
||||||
jobid,
|
jobid,
|
||||||
messageOptions: {
|
messageOptions: {
|
||||||
...messageOptions,
|
...messageOptions,
|
||||||
to: Array.isArray(messageOptions.to)
|
to: Array.isArray(messageOptions.to)
|
||||||
? messageOptions.to
|
? messageOptions.to
|
||||||
: [messageOptions.to],
|
: [messageOptions.to],
|
||||||
},
|
},
|
||||||
template,
|
template,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
} else if (sendType === "x") {
|
} else if (sendType === "x") {
|
||||||
console.log("excel");
|
|
||||||
await RenderTemplate(template, bodyshop, false, true);
|
await RenderTemplate(template, bodyshop, false, true);
|
||||||
} else if (sendType === "text") {
|
} else if (sendType === "text") {
|
||||||
await RenderTemplate(template, bodyshop, false, false, true);
|
await RenderTemplate(template, bodyshop, false, false, true);
|
||||||
@@ -305,22 +311,74 @@ export const GenerateDocuments = async (templates) => {
|
|||||||
await RenderTemplates(templates, bodyshop);
|
await RenderTemplates(templates, bodyshop);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const fetchFilterData = async ({name}) => {
|
||||||
|
try {
|
||||||
|
const bodyshop = store.getState().user.bodyshop;
|
||||||
|
const jsrAuth = (await axios.post("/utils/jsr")).data;
|
||||||
|
jsreport.headers["FirebaseAuthorization"] =
|
||||||
|
"Bearer " + (await auth.currentUser.getIdToken());
|
||||||
|
|
||||||
|
const folders = await cleanAxios.get(`${server}/odata/folders`, {
|
||||||
|
headers: {Authorization: jsrAuth},
|
||||||
|
});
|
||||||
|
const shopSpecificFolder = folders.data.value.find(
|
||||||
|
(f) => f.name === bodyshop.imexshopid
|
||||||
|
);
|
||||||
|
|
||||||
|
const jsReportFilters = await cleanAxios.get(
|
||||||
|
`${server}/odata/assets?$filter=name eq '${name}.filters'`,
|
||||||
|
{headers: {Authorization: jsrAuth}}
|
||||||
|
);
|
||||||
|
console.log("🚀 ~ fetchFilterData ~ jsReportFilters:", jsReportFilters);
|
||||||
|
|
||||||
|
let parsedFilterData;
|
||||||
|
let useShopSpecificTemplate = false;
|
||||||
|
// let shopSpecificTemplate;
|
||||||
|
|
||||||
|
if (shopSpecificFolder) {
|
||||||
|
let shopSpecificTemplate = jsReportFilters.data.value.find(
|
||||||
|
(f) => f?.folder?.shortid === shopSpecificFolder.shortid
|
||||||
|
);
|
||||||
|
if (shopSpecificTemplate) {
|
||||||
|
useShopSpecificTemplate = true;
|
||||||
|
parsedFilterData = atob(shopSpecificTemplate.content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!parsedFilterData) {
|
||||||
|
const generalTemplate = jsReportFilters.data.value.find((f) => !f.folder);
|
||||||
|
useShopSpecificTemplate = false;
|
||||||
|
if (generalTemplate) parsedFilterData = atob(generalTemplate.content);
|
||||||
|
}
|
||||||
|
const data = JSON.parse(parsedFilterData);
|
||||||
|
return {
|
||||||
|
data,
|
||||||
|
useShopSpecificTemplate,
|
||||||
|
success: true,
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const fetchContextData = async (templateObject, jsrAuth) => {
|
const fetchContextData = async (templateObject, jsrAuth) => {
|
||||||
const bodyshop = store.getState().user.bodyshop;
|
const bodyshop = store.getState().user.bodyshop;
|
||||||
|
|
||||||
jsreport.headers["FirebaseAuthorization"] =
|
jsreport.headers["FirebaseAuthorization"] =
|
||||||
"Bearer " + (await auth.currentUser.getIdToken());
|
"Bearer " + (await auth.currentUser.getIdToken());
|
||||||
|
|
||||||
const folders = await cleanAxios.get(`${server}/odata/folders`, {
|
const folders = await cleanAxios.get(`${server}/odata/folders`, {
|
||||||
headers: { Authorization: jsrAuth },
|
headers: {Authorization: jsrAuth},
|
||||||
});
|
});
|
||||||
const shopSpecificFolder = folders.data.value.find(
|
const shopSpecificFolder = folders.data.value.find(
|
||||||
(f) => f.name === bodyshop.imexshopid
|
(f) => f.name === bodyshop.imexshopid
|
||||||
);
|
);
|
||||||
|
|
||||||
const jsReportQueries = await cleanAxios.get(
|
const jsReportQueries = await cleanAxios.get(
|
||||||
`${server}/odata/assets?$filter=name eq '${templateObject.name}.query'`,
|
`${server}/odata/assets?$filter=name eq '${templateObject.name}.query'`,
|
||||||
{ headers: { Authorization: jsrAuth } }
|
{headers: {Authorization: jsrAuth}}
|
||||||
);
|
);
|
||||||
|
|
||||||
let templateQueryToExecute;
|
let templateQueryToExecute;
|
||||||
@@ -329,7 +387,7 @@ const fetchContextData = async (templateObject, jsrAuth) => {
|
|||||||
|
|
||||||
if (shopSpecificFolder) {
|
if (shopSpecificFolder) {
|
||||||
let shopSpecificTemplate = jsReportQueries.data.value.find(
|
let shopSpecificTemplate = jsReportQueries.data.value.find(
|
||||||
(f) => f?.folder?.shortid === shopSpecificFolder.shortid
|
(f) => f?.folder?.shortid === shopSpecificFolder.shortid
|
||||||
);
|
);
|
||||||
if (shopSpecificTemplate) {
|
if (shopSpecificTemplate) {
|
||||||
useShopSpecificTemplate = true;
|
useShopSpecificTemplate = true;
|
||||||
@@ -343,16 +401,35 @@ const fetchContextData = async (templateObject, jsrAuth) => {
|
|||||||
templateQueryToExecute = atob(generalTemplate.content);
|
templateQueryToExecute = atob(generalTemplate.content);
|
||||||
}
|
}
|
||||||
|
|
||||||
let contextData = {};
|
// Commented out for future revision debugging
|
||||||
if (templateQueryToExecute) {
|
// console.log('Template Object');
|
||||||
const { data } = await client.query({
|
// console.dir(templateObject);
|
||||||
query: gql(templateQueryToExecute),
|
// console.log('Unmodified Query');
|
||||||
variables: { ...templateObject.variables },
|
// console.dir(templateQueryToExecute);
|
||||||
});
|
|
||||||
contextData = data;
|
const hasFilters = templateObject?.filters?.length > 0;
|
||||||
|
const hasSorters = templateObject?.sorters?.length > 0;
|
||||||
|
const hasDefaultSorters = templateObject?.defaultSorters?.length > 0;
|
||||||
|
|
||||||
|
// We have no template filters or sorters, so we can just execute the query and return the data
|
||||||
|
if (!hasFilters && !hasSorters && !hasDefaultSorters) {
|
||||||
|
let contextData = {};
|
||||||
|
if (templateQueryToExecute) {
|
||||||
|
const {data} = await client.query({
|
||||||
|
query: gql(templateQueryToExecute),
|
||||||
|
variables: {...templateObject.variables},
|
||||||
|
});
|
||||||
|
contextData = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {contextData, useShopSpecificTemplate};
|
||||||
}
|
}
|
||||||
|
|
||||||
return { contextData, useShopSpecificTemplate };
|
return await generateTemplate(
|
||||||
|
templateQueryToExecute,
|
||||||
|
templateObject,
|
||||||
|
useShopSpecificTemplate
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
//export const displayTemplateInWindow = (html) => {
|
//export const displayTemplateInWindow = (html) => {
|
||||||
@@ -389,7 +466,7 @@ const fetchContextData = async (templateObject, jsrAuth) => {
|
|||||||
|
|
||||||
function extend(o1, o2, o3) {
|
function extend(o1, o2, o3) {
|
||||||
var result = {},
|
var result = {},
|
||||||
obj;
|
obj;
|
||||||
|
|
||||||
for (var i = 0; i < arguments.length; i++) {
|
for (var i = 0; i < arguments.length; i++) {
|
||||||
obj = arguments[i];
|
obj = arguments[i];
|
||||||
@@ -405,4 +482,4 @@ function extend(o1, o2, o3) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user