Compare commits
93 Commits
feature/IO
...
feature/IO
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b41d69593d | ||
|
|
09e505399c | ||
|
|
42f9f275c7 | ||
|
|
39b119b2e8 | ||
|
|
5bc224206c | ||
|
|
190da863c2 | ||
|
|
2711b5fce5 | ||
|
|
8e9358cd6f | ||
|
|
0e31bbb789 | ||
|
|
3b635aeed3 | ||
|
|
1f896b1ede | ||
|
|
b1ca09bd4f | ||
|
|
f9ca36ec89 | ||
|
|
9d479d4b4d | ||
|
|
23b5b740cb | ||
|
|
8f9b05b974 | ||
|
|
d1132e7d45 | ||
|
|
960b0b4d09 | ||
|
|
f35ea026b8 | ||
|
|
b1fc2828c8 | ||
|
|
bc25c23982 | ||
|
|
2215c8439e | ||
|
|
f98c9e6f71 | ||
|
|
28f2e8ad30 | ||
|
|
c27e206687 | ||
|
|
01fd253f1d | ||
|
|
e67bc0d953 | ||
|
|
3eab3e2fb6 | ||
|
|
3adf6b649b | ||
|
|
f8243aa2b3 | ||
|
|
3c3f50d138 | ||
|
|
4f7e1b81ac | ||
|
|
806bdc4c70 | ||
|
|
a0572a0cec | ||
|
|
04cdf13e86 | ||
|
|
50349e91dc | ||
|
|
9998a8f154 | ||
|
|
9dcbcb2a43 | ||
|
|
5773f7a0f3 | ||
|
|
8614d88e71 | ||
|
|
fa5e26c52a | ||
|
|
90e1cbd390 | ||
|
|
947a3c6a88 | ||
|
|
a33662e6f0 | ||
|
|
6d1f04369e | ||
|
|
4a27726ef3 | ||
|
|
eedba97237 | ||
|
|
bcf095ed4f | ||
|
|
c3f9e268c7 | ||
|
|
c8442f0750 | ||
|
|
da5b446c30 | ||
|
|
e13b2bb969 | ||
|
|
e8969c4698 | ||
|
|
346d32a2bb | ||
|
|
6ba00a90be | ||
|
|
4293d20313 | ||
|
|
706c70c509 | ||
|
|
e872b1bf0a | ||
|
|
379fa060d8 | ||
|
|
a6258c6456 | ||
|
|
d6bf0a225b | ||
|
|
ec2b914e5e | ||
|
|
309a20148a | ||
|
|
013b56778b | ||
|
|
9dec4a3a61 | ||
|
|
189b4db90f | ||
|
|
c49fa1c527 | ||
|
|
53ef048f6f | ||
|
|
1cc7eed983 | ||
|
|
1c8f377212 | ||
|
|
c050947276 | ||
|
|
9bde1f820d | ||
|
|
393765640c | ||
|
|
69c2836425 | ||
|
|
ec9edf30eb | ||
|
|
6a812f9ea7 | ||
|
|
fa86254bfd | ||
|
|
58ab7afbb3 | ||
|
|
fa7d90d2a9 | ||
|
|
fbf9047974 | ||
|
|
3e9b795052 | ||
|
|
d29ffc21e5 | ||
|
|
959f7780e8 | ||
|
|
e47731702a | ||
|
|
85a3aeb335 | ||
|
|
80b7ae0e54 | ||
|
|
0529ac4478 | ||
|
|
3cafbebbee | ||
|
|
6f248d864e | ||
|
|
a4a84572b7 | ||
|
|
0bc00d46cf | ||
|
|
e80e40bb76 | ||
|
|
a88c102b27 |
16
.prettierrc.js
Normal file
16
.prettierrc.js
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
exports.default = {
|
||||||
|
printWidth: 120,
|
||||||
|
useTabs: false,
|
||||||
|
tabWidth: 2,
|
||||||
|
trailingComma: "es5",
|
||||||
|
semi: true,
|
||||||
|
singleQuote: false,
|
||||||
|
bracketSpacing: true,
|
||||||
|
arrowParens: "always",
|
||||||
|
jsxSingleQuote: false,
|
||||||
|
bracketSameLine: false,
|
||||||
|
endOfLine: "lf",
|
||||||
|
importOrder: ["^@core/(.*)$", "^@server/(.*)$", "^@ui/(.*)$", "^[./]"],
|
||||||
|
importOrderSeparation: true,
|
||||||
|
importOrderSortSpecifiers: true,
|
||||||
|
};
|
||||||
@@ -3,7 +3,13 @@
|
|||||||
This documentation details the schema required for `.filters` files on the report server. It is used to dynamically
|
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.
|
modify the graphQL query and provide the user more power over their reports.
|
||||||
|
|
||||||
# Special Notes
|
For filters and sorters, valid types include (`type` key in the schema):
|
||||||
|
- string (default)
|
||||||
|
- number
|
||||||
|
- bool or boolean
|
||||||
|
- date
|
||||||
|
|
||||||
|
## 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
|
- 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
|
## High level Schema Overview
|
||||||
@@ -40,9 +46,10 @@ Filters effect the where clause of the graphQL query. They are used to filter th
|
|||||||
A note on special notation used in the `name` field.
|
A note on special notation used in the `name` field.
|
||||||
|
|
||||||
## Reflection
|
## Reflection
|
||||||
|
|
||||||
Filters can make use of reflection to pre-fill select boxes, the following is an example of that in the filters file.
|
Filters can make use of reflection to pre-fill select boxes, the following is an example of that in the filters file.
|
||||||
|
|
||||||
```
|
```json
|
||||||
{
|
{
|
||||||
"name": "jobs.status",
|
"name": "jobs.status",
|
||||||
"translation": "jobs.fields.status",
|
"translation": "jobs.fields.status",
|
||||||
@@ -52,7 +59,7 @@ Filters can make use of reflection to pre-fill select boxes, the following is an
|
|||||||
"type": "internal",
|
"type": "internal",
|
||||||
"name": "special.job_statuses"
|
"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`
|
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`
|
||||||
@@ -67,7 +74,13 @@ The following cases are available
|
|||||||
- `special.employees` - This will reflect the employees `bodyshop.employees`
|
- `special.employees` - This will reflect the employees `bodyshop.employees`
|
||||||
- `special.first_names` - This will reflect the first names `bodyshop.employees`
|
- `special.first_names` - This will reflect the first names `bodyshop.employees`
|
||||||
- `special.last_names` - This will reflect the last names `bodyshop.employees`
|
- `special.last_names` - This will reflect the last names `bodyshop.employees`
|
||||||
-
|
- `special.referral_sources` - This will reflect the referral sources `bodyshop.md_referral_sources`
|
||||||
|
- `special.class`- This will reflect the class `bodyshop.md_classes`
|
||||||
|
- `special.lost_sale_reasons` - This will reflect the lost sale reasons `bodyshop.md_lost_sale_reasons`
|
||||||
|
- `special.alt_transports` - This will reflect the alternative transports `bodyshop.appt_alt_transport`
|
||||||
|
- `special.payment_types` - This will reflect the payment types `bodyshop.md_payment_types`
|
||||||
|
- `special.payment_payers` - This is a special case with a key value set of [Customer, Insurance]
|
||||||
|
|
||||||
### Path without brackets, multi level
|
### Path without brackets, multi level
|
||||||
|
|
||||||
`"name": "jobs.joblines.mod_lb_hrs",`
|
`"name": "jobs.joblines.mod_lb_hrs",`
|
||||||
@@ -104,6 +117,7 @@ query gendoc_hours_sold_detail_open($starttz: timestamptz!, $endtz: timestamptz!
|
|||||||
```
|
```
|
||||||
|
|
||||||
### Path with brackets,top level
|
### Path with brackets,top level
|
||||||
|
|
||||||
`"name": "[jobs].joblines.mod_lb_hrs",`
|
`"name": "[jobs].joblines.mod_lb_hrs",`
|
||||||
This will produce a where clause at the `jobs` level of the graphQL query.
|
This will produce a where clause at the `jobs` level of the graphQL query.
|
||||||
|
|
||||||
@@ -138,18 +152,26 @@ query gendoc_hours_sold_detail_open($starttz: timestamptz!, $endtz: timestamptz!
|
|||||||
```
|
```
|
||||||
|
|
||||||
## Known Caveats
|
## 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.
|
- Will only support two level of nesting in the graphQL query `jobs.joblines.mod_lb_hrs` vs `[jobs].joblines.mod_lb_hrs`
|
||||||
- The type object must be 'string' or 'number' and is case-sensitive.
|
is fine, but `jobs.[joblines.].some_table.mod_lb_hrs` is not.
|
||||||
|
- The type object must be 'string' or 'number' or 'bool' or 'boolean' or 'date' 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.
|
- 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 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.
|
- Do not add the ability to filter on things like FK constraints, must like the above example.
|
||||||
|
- *INHERITANCE CAVEAT* If you have a filters file on a parent report that has a child that you do not want the filters inherited from, you must place a blank filters file (valid json so {}) on the child report level. This will than fetch the child filters, which are empty and move along, versus inheriting the parent filters.
|
||||||
|
|
||||||
## Sorters
|
## 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.
|
- 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
|
### 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.
|
- 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`.
|
- 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`.
|
||||||
|
|
||||||
@@ -164,4 +186,4 @@ query gendoc_hours_sold_detail_open($starttz: timestamptz!, $endtz: timestamptz!
|
|||||||
"direction": "asc"
|
"direction": "asc"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,21 +1,21 @@
|
|||||||
import { Input, Table, Checkbox, Card, Space } from "antd";
|
import { Card, Checkbox, Input, Space, Table } from "antd";
|
||||||
|
import queryString from "query-string";
|
||||||
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 CurrencyFormatter from "../../utils/CurrencyFormatter";
|
|
||||||
import { alphaSort, dateSort } from "../../utils/sorters";
|
|
||||||
import PayableExportButton from "../payable-export-button/payable-export-button.component";
|
|
||||||
import PayableExportAll from "../payable-export-all-button/payable-export-all-button.component";
|
|
||||||
import { DateFormatter } from "../../utils/DateFormatter";
|
|
||||||
import queryString from "query-string";
|
|
||||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
|
||||||
import QboAuthorizeComponent from "../qbo-authorize/qbo-authorize.component";
|
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
|
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||||
|
import { DateFormatter } from "../../utils/DateFormatter";
|
||||||
|
import { pageLimit } from "../../utils/config";
|
||||||
|
import { alphaSort, dateSort } from "../../utils/sorters";
|
||||||
import ExportLogsCountDisplay from "../export-logs-count-display/export-logs-count-display.component";
|
import ExportLogsCountDisplay from "../export-logs-count-display/export-logs-count-display.component";
|
||||||
|
import PayableExportAll from "../payable-export-all-button/payable-export-all-button.component";
|
||||||
|
import PayableExportButton from "../payable-export-button/payable-export-button.component";
|
||||||
import BillMarkSelectedExported from "../payable-mark-selected-exported/payable-mark-selected-exported.component";
|
import BillMarkSelectedExported from "../payable-mark-selected-exported/payable-mark-selected-exported.component";
|
||||||
import {pageLimit} from "../../utils/config";
|
import QboAuthorizeComponent from "../qbo-authorize/qbo-authorize.component";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
@@ -138,7 +138,6 @@ export function AccountingPayablesTableComponent({
|
|||||||
title: t("exportlogs.labels.attempts"),
|
title: t("exportlogs.labels.attempts"),
|
||||||
dataIndex: "attempts",
|
dataIndex: "attempts",
|
||||||
key: "attempts",
|
key: "attempts",
|
||||||
|
|
||||||
render: (text, record) => (
|
render: (text, record) => (
|
||||||
<ExportLogsCountDisplay logs={record.exportlogs} />
|
<ExportLogsCountDisplay logs={record.exportlogs} />
|
||||||
),
|
),
|
||||||
@@ -147,8 +146,6 @@ export function AccountingPayablesTableComponent({
|
|||||||
title: t("general.labels.actions"),
|
title: t("general.labels.actions"),
|
||||||
dataIndex: "actions",
|
dataIndex: "actions",
|
||||||
key: "actions",
|
key: "actions",
|
||||||
sorter: (a, b) => a.clm_total - b.clm_total,
|
|
||||||
|
|
||||||
render: (text, record) => (
|
render: (text, record) => (
|
||||||
<PayableExportButton
|
<PayableExportButton
|
||||||
billId={record.id}
|
billId={record.id}
|
||||||
|
|||||||
@@ -8,14 +8,16 @@ import { logImEXEvent } from "../../firebase/firebase.utils";
|
|||||||
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, DateTimeFormatter } from "../../utils/DateFormatter";
|
import { DateFormatter, DateTimeFormatter } from "../../utils/DateFormatter";
|
||||||
|
import { pageLimit } from "../../utils/config";
|
||||||
import { alphaSort, dateSort } from "../../utils/sorters";
|
import { alphaSort, dateSort } from "../../utils/sorters";
|
||||||
import ExportLogsCountDisplay from "../export-logs-count-display/export-logs-count-display.component";
|
import ExportLogsCountDisplay from "../export-logs-count-display/export-logs-count-display.component";
|
||||||
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
import OwnerNameDisplay, {
|
||||||
|
OwnerNameDisplayFunction,
|
||||||
|
} from "../owner-name-display/owner-name-display.component";
|
||||||
import PaymentExportButton from "../payment-export-button/payment-export-button.component";
|
import PaymentExportButton from "../payment-export-button/payment-export-button.component";
|
||||||
import PaymentMarkSelectedExported from "../payment-mark-selected-exported/payment-mark-selected-exported.component";
|
import PaymentMarkSelectedExported from "../payment-mark-selected-exported/payment-mark-selected-exported.component";
|
||||||
import PaymentsExportAllButton from "../payments-export-all-button/payments-export-all-button.component";
|
import PaymentsExportAllButton from "../payments-export-all-button/payments-export-all-button.component";
|
||||||
import QboAuthorizeComponent from "../qbo-authorize/qbo-authorize.component";
|
import QboAuthorizeComponent from "../qbo-authorize/qbo-authorize.component";
|
||||||
import {pageLimit} from "../../utils/config";
|
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
@@ -75,7 +77,11 @@ export function AccountingPayablesTableComponent({
|
|||||||
dataIndex: "owner",
|
dataIndex: "owner",
|
||||||
key: "owner",
|
key: "owner",
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
sorter: (a, b) => alphaSort(a.job.ownr_ln, b.job.ownr_ln),
|
sorter: (a, b) =>
|
||||||
|
alphaSort(
|
||||||
|
OwnerNameDisplayFunction(a.job),
|
||||||
|
OwnerNameDisplayFunction(b.job)
|
||||||
|
),
|
||||||
sortOrder:
|
sortOrder:
|
||||||
state.sortedInfo.columnKey === "owner" && state.sortedInfo.order,
|
state.sortedInfo.columnKey === "owner" && state.sortedInfo.order,
|
||||||
render: (text, record) => {
|
render: (text, record) => {
|
||||||
@@ -94,6 +100,9 @@ export function AccountingPayablesTableComponent({
|
|||||||
title: t("payments.fields.amount"),
|
title: t("payments.fields.amount"),
|
||||||
dataIndex: "amount",
|
dataIndex: "amount",
|
||||||
key: "amount",
|
key: "amount",
|
||||||
|
sorter: (a, b) => a.amount - b.amount,
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "amount" && state.sortedInfo.order,
|
||||||
render: (text, record) => (
|
render: (text, record) => (
|
||||||
<CurrencyFormatter>{record.amount}</CurrencyFormatter>
|
<CurrencyFormatter>{record.amount}</CurrencyFormatter>
|
||||||
),
|
),
|
||||||
@@ -112,18 +121,21 @@ export function AccountingPayablesTableComponent({
|
|||||||
title: t("payments.fields.created_at"),
|
title: t("payments.fields.created_at"),
|
||||||
dataIndex: "created_at",
|
dataIndex: "created_at",
|
||||||
key: "created_at",
|
key: "created_at",
|
||||||
|
sorter: (a, b) => dateSort(a.created_at, b.created_at),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "created_at" && state.sortedInfo.order,
|
||||||
render: (text, record) => (
|
render: (text, record) => (
|
||||||
<DateTimeFormatter>{record.created_at}</DateTimeFormatter>
|
<DateTimeFormatter>{record.created_at}</DateTimeFormatter>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
// {
|
||||||
title: t("payments.fields.exportedat"),
|
// title: t("payments.fields.exportedat"),
|
||||||
dataIndex: "exportedat",
|
// dataIndex: "exportedat",
|
||||||
key: "exportedat",
|
// key: "exportedat",
|
||||||
render: (text, record) => (
|
// render: (text, record) => (
|
||||||
<DateTimeFormatter>{record.exportedat}</DateTimeFormatter>
|
// <DateTimeFormatter>{record.exportedat}</DateTimeFormatter>
|
||||||
),
|
// ),
|
||||||
},
|
// },
|
||||||
{
|
{
|
||||||
title: t("exportlogs.labels.attempts"),
|
title: t("exportlogs.labels.attempts"),
|
||||||
dataIndex: "attempts",
|
dataIndex: "attempts",
|
||||||
@@ -137,8 +149,6 @@ export function AccountingPayablesTableComponent({
|
|||||||
title: t("general.labels.actions"),
|
title: t("general.labels.actions"),
|
||||||
dataIndex: "actions",
|
dataIndex: "actions",
|
||||||
key: "actions",
|
key: "actions",
|
||||||
sorter: (a, b) => a.clm_total - b.clm_total,
|
|
||||||
|
|
||||||
render: (text, record) => (
|
render: (text, record) => (
|
||||||
<PaymentExportButton
|
<PaymentExportButton
|
||||||
paymentId={record.id}
|
paymentId={record.id}
|
||||||
|
|||||||
@@ -4,17 +4,19 @@ import { useTranslation } from "react-i18next";
|
|||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||||
import { alphaSort, dateSort } from "../../utils/sorters";
|
import { alphaSort, dateSort, statusSort } from "../../utils/sorters";
|
||||||
import JobExportButton from "../jobs-close-export-button/jobs-close-export-button.component";
|
import JobExportButton from "../jobs-close-export-button/jobs-close-export-button.component";
|
||||||
import JobsExportAllButton from "../jobs-export-all-button/jobs-export-all-button.component";
|
import JobsExportAllButton from "../jobs-export-all-button/jobs-export-all-button.component";
|
||||||
|
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
import QboAuthorizeComponent from "../qbo-authorize/qbo-authorize.component";
|
|
||||||
import { DateFormatter } from "../../utils/DateFormatter";
|
import { DateFormatter } from "../../utils/DateFormatter";
|
||||||
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
|
||||||
import ExportLogsCountDisplay from "../export-logs-count-display/export-logs-count-display.component";
|
import ExportLogsCountDisplay from "../export-logs-count-display/export-logs-count-display.component";
|
||||||
|
import OwnerNameDisplay, {
|
||||||
|
OwnerNameDisplayFunction,
|
||||||
|
} from "../owner-name-display/owner-name-display.component";
|
||||||
|
import QboAuthorizeComponent from "../qbo-authorize/qbo-authorize.component";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
@@ -63,7 +65,7 @@ export function AccountingReceivablesTableComponent({
|
|||||||
title: t("jobs.fields.status"),
|
title: t("jobs.fields.status"),
|
||||||
dataIndex: "status",
|
dataIndex: "status",
|
||||||
key: "status",
|
key: "status",
|
||||||
sorter: (a, b) => a.status - b.status,
|
sorter: (a, b) => statusSort(a, b, bodyshop.md_ro_statuses.statuses),
|
||||||
sortOrder:
|
sortOrder:
|
||||||
state.sortedInfo.columnKey === "status" && state.sortedInfo.order,
|
state.sortedInfo.columnKey === "status" && state.sortedInfo.order,
|
||||||
},
|
},
|
||||||
@@ -83,7 +85,8 @@ export function AccountingReceivablesTableComponent({
|
|||||||
title: t("jobs.fields.owner"),
|
title: t("jobs.fields.owner"),
|
||||||
dataIndex: "owner",
|
dataIndex: "owner",
|
||||||
key: "owner",
|
key: "owner",
|
||||||
sorter: (a, b) => alphaSort(a.ownr_ln, b.ownr_ln),
|
sorter: (a, b) =>
|
||||||
|
alphaSort(OwnerNameDisplayFunction(a), OwnerNameDisplayFunction(b)),
|
||||||
sortOrder:
|
sortOrder:
|
||||||
state.sortedInfo.columnKey === "owner" && state.sortedInfo.order,
|
state.sortedInfo.columnKey === "owner" && state.sortedInfo.order,
|
||||||
render: (text, record) => {
|
render: (text, record) => {
|
||||||
@@ -103,6 +106,15 @@ export function AccountingReceivablesTableComponent({
|
|||||||
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 to={"/manage/vehicles/" + record.vehicleid}>
|
<Link to={"/manage/vehicles/" + record.vehicleid}>
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ import RbacWrapper from "../rbac-wrapper/rbac-wrapper.component";
|
|||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({});
|
const mapStateToProps = createStructuredSelector({});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
insertAuditTrail: ({ jobid, operation }) =>
|
insertAuditTrail: ({ jobid, operation, type }) =>
|
||||||
dispatch(insertAuditTrail({ jobid, operation })),
|
dispatch(insertAuditTrail({ jobid, operation, type })),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(BillDeleteButton);
|
export default connect(mapStateToProps, mapDispatchToProps)(BillDeleteButton);
|
||||||
@@ -51,6 +51,7 @@ export function BillDeleteButton({ bill, jobid, callback, insertAuditTrail }) {
|
|||||||
insertAuditTrail({
|
insertAuditTrail({
|
||||||
jobid: jobid,
|
jobid: jobid,
|
||||||
operation: AuditTrailMapping.billdeleted(bill.invoice_number),
|
operation: AuditTrailMapping.billdeleted(bill.invoice_number),
|
||||||
|
type: "billdeleted",
|
||||||
});
|
});
|
||||||
|
|
||||||
if (callback && typeof callback === "function") callback(bill.id);
|
if (callback && typeof callback === "function") callback(bill.id);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useMutation, useQuery } from "@apollo/client";
|
import { useMutation, useQuery } from "@apollo/client";
|
||||||
import { Button, Form, PageHeader, Popconfirm, Space } from "antd";
|
import { Button, Divider, Form, PageHeader, Popconfirm, Space } from "antd";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import queryString from "query-string";
|
import queryString from "query-string";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
@@ -33,8 +33,8 @@ const mapStateToProps = createStructuredSelector({
|
|||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
setPartsOrderContext: (context) =>
|
setPartsOrderContext: (context) =>
|
||||||
dispatch(setModalContext({ context: context, modal: "partsOrder" })),
|
dispatch(setModalContext({ context: context, modal: "partsOrder" })),
|
||||||
insertAuditTrail: ({ jobid, operation }) =>
|
insertAuditTrail: ({ jobid, operation, type }) =>
|
||||||
dispatch(insertAuditTrail({ jobid, operation })),
|
dispatch(insertAuditTrail({ jobid, operation, type })),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(
|
export default connect(
|
||||||
@@ -150,6 +150,7 @@ export function BillDetailEditcontainer({
|
|||||||
jobid: bill.jobid,
|
jobid: bill.jobid,
|
||||||
billid: search.billid,
|
billid: search.billid,
|
||||||
operation: AuditTrailMapping.billupdated(bill.invoice_number),
|
operation: AuditTrailMapping.billupdated(bill.invoice_number),
|
||||||
|
type: "billupdated",
|
||||||
});
|
});
|
||||||
|
|
||||||
await refetch();
|
await refetch();
|
||||||
@@ -207,7 +208,7 @@ export function BillDetailEditcontainer({
|
|||||||
layout="vertical"
|
layout="vertical"
|
||||||
>
|
>
|
||||||
<BillFormContainer form={form} billEdit disabled={exported} />
|
<BillFormContainer form={form} billEdit disabled={exported} />
|
||||||
|
<Divider orientation="left">{t("general.labels.media")}</Divider>
|
||||||
{bodyshop.uselocalmediaserver ? (
|
{bodyshop.uselocalmediaserver ? (
|
||||||
<JobsDocumentsLocalGallery
|
<JobsDocumentsLocalGallery
|
||||||
job={{ id: data ? data.bills_by_pk.jobid : null }}
|
job={{ id: data ? data.bills_by_pk.jobid : null }}
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ const mapStateToProps = createStructuredSelector({
|
|||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
setPartsOrderContext: (context) =>
|
setPartsOrderContext: (context) =>
|
||||||
dispatch(setModalContext({ context: context, modal: "partsOrder" })),
|
dispatch(setModalContext({ context: context, modal: "partsOrder" })),
|
||||||
insertAuditTrail: ({ jobid, operation }) =>
|
insertAuditTrail: ({ jobid, operation, type }) =>
|
||||||
dispatch(insertAuditTrail({ jobid, operation })),
|
dispatch(insertAuditTrail({ jobid, operation, type })),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(
|
export default connect(
|
||||||
@@ -173,7 +173,11 @@ export function BillDetailEditReturn({
|
|||||||
</Form>
|
</Form>
|
||||||
</Modal>
|
</Modal>
|
||||||
<Button
|
<Button
|
||||||
disabled={data.bills_by_pk.is_credit_memo || disabled}
|
disabled={
|
||||||
|
data.bills_by_pk.is_credit_memo ||
|
||||||
|
data.bills_by_pk.isinhouse ||
|
||||||
|
disabled
|
||||||
|
}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setVisible(true);
|
setVisible(true);
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -37,8 +37,8 @@ const mapStateToProps = createStructuredSelector({
|
|||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
toggleModalVisible: () => dispatch(toggleModalVisible("billEnter")),
|
toggleModalVisible: () => dispatch(toggleModalVisible("billEnter")),
|
||||||
insertAuditTrail: ({ jobid, billid, operation }) =>
|
insertAuditTrail: ({ jobid, billid, operation, type }) =>
|
||||||
dispatch(insertAuditTrail({ jobid, billid, operation })),
|
dispatch(insertAuditTrail({ jobid, billid, operation, type })),
|
||||||
});
|
});
|
||||||
|
|
||||||
const Templates = TemplateList("job_special");
|
const Templates = TemplateList("job_special");
|
||||||
@@ -171,6 +171,7 @@ function BillEnterModalContainer({
|
|||||||
mod_lbr_ty: key,
|
mod_lbr_ty: key,
|
||||||
hours: adjustmentsToInsert[key].toFixed(1),
|
hours: adjustmentsToInsert[key].toFixed(1),
|
||||||
}),
|
}),
|
||||||
|
type: "jobmodifylbradj",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -320,6 +321,7 @@ function BillEnterModalContainer({
|
|||||||
operation: AuditTrailMapping.billposted(
|
operation: AuditTrailMapping.billposted(
|
||||||
r1.data.insert_bills.returning[0].invoice_number
|
r1.data.insert_bills.returning[0].invoice_number
|
||||||
),
|
),
|
||||||
|
type: "billposted",
|
||||||
});
|
});
|
||||||
|
|
||||||
if (enterAgain) {
|
if (enterAgain) {
|
||||||
|
|||||||
@@ -504,10 +504,11 @@ export function BillFormComponent({
|
|||||||
billEdit={billEdit}
|
billEdit={billEdit}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
<Divider orientation="left" style={{ display: billEdit ? "none" : null }}>
|
||||||
|
{t("documents.labels.upload")}
|
||||||
|
</Divider>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name="upload"
|
name="upload"
|
||||||
label="Upload"
|
|
||||||
style={{ display: billEdit ? "none" : null }}
|
style={{ display: billEdit ? "none" : null }}
|
||||||
valuePropName="fileList"
|
valuePropName="fileList"
|
||||||
getValueFromEvent={(e) => {
|
getValueFromEvent={(e) => {
|
||||||
|
|||||||
@@ -37,8 +37,8 @@ const mapStateToProps = createStructuredSelector({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
insertAuditTrail: ({ jobid, operation }) =>
|
insertAuditTrail: ({ jobid, operation, type }) =>
|
||||||
dispatch(insertAuditTrail({ jobid, operation })),
|
dispatch(insertAuditTrail({ jobid, operation, type })),
|
||||||
toggleModalVisible: () => dispatch(toggleModalVisible("cardPayment")),
|
toggleModalVisible: () => dispatch(toggleModalVisible("cardPayment")),
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -102,6 +102,7 @@ const CardPaymentModalComponent = ({
|
|||||||
insertAuditTrail({
|
insertAuditTrail({
|
||||||
jobid: payment.jobid,
|
jobid: payment.jobid,
|
||||||
operation: AuditTrailMapping.failedpayment(),
|
operation: AuditTrailMapping.failedpayment(),
|
||||||
|
type: "failedpayment",
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -17,13 +17,18 @@ import { DateTimeFormatter } from "../../utils/DateFormatter";
|
|||||||
import { GenerateDocument } from "../../utils/RenderTemplate";
|
import { GenerateDocument } from "../../utils/RenderTemplate";
|
||||||
import { TemplateList } from "../../utils/TemplateConstants";
|
import { TemplateList } from "../../utils/TemplateConstants";
|
||||||
import { alphaSort } from "../../utils/sorters";
|
import { alphaSort } from "../../utils/sorters";
|
||||||
|
import useLocalStorage from "../../utils/useLocalStorage";
|
||||||
import { OwnerNameDisplayFunction } from "../owner-name-display/owner-name-display.component";
|
import { OwnerNameDisplayFunction } from "../owner-name-display/owner-name-display.component";
|
||||||
|
|
||||||
export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
|
export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
|
||||||
const [state, setState] = useState({
|
const [state, setState] = useState({
|
||||||
sortedInfo: {},
|
sortedInfo: {},
|
||||||
filteredInfo: { text: "" },
|
|
||||||
});
|
});
|
||||||
const [searchText, setSearchText] = useState("");
|
const [searchText, setSearchText] = useState("");
|
||||||
|
const [filter, setFilter] = useLocalStorage(
|
||||||
|
"filter_courtesy_cars_list",
|
||||||
|
null
|
||||||
|
);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
@@ -50,11 +55,16 @@ export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
|
|||||||
dataIndex: "status",
|
dataIndex: "status",
|
||||||
key: "status",
|
key: "status",
|
||||||
sorter: (a, b) => alphaSort(a.status, b.status),
|
sorter: (a, b) => alphaSort(a.status, b.status),
|
||||||
|
filteredValue: filter?.status || null,
|
||||||
filters: [
|
filters: [
|
||||||
{
|
{
|
||||||
text: t("courtesycars.status.in"),
|
text: t("courtesycars.status.in"),
|
||||||
value: "courtesycars.status.in",
|
value: "courtesycars.status.in",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
text: t("courtesycars.status.inservice"),
|
||||||
|
value: "courtesycars.status.inservice",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
text: t("courtesycars.status.out"),
|
text: t("courtesycars.status.out"),
|
||||||
value: "courtesycars.status.out",
|
value: "courtesycars.status.out",
|
||||||
@@ -68,7 +78,7 @@ export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
|
|||||||
value: "courtesycars.status.leasereturn",
|
value: "courtesycars.status.leasereturn",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
onFilter: (value, record) => value.includes(record.status),
|
onFilter: (value, record) => record.status === value,
|
||||||
sortOrder:
|
sortOrder:
|
||||||
state.sortedInfo.columnKey === "status" && state.sortedInfo.order,
|
state.sortedInfo.columnKey === "status" && state.sortedInfo.order,
|
||||||
render: (text, record) => {
|
render: (text, record) => {
|
||||||
@@ -112,6 +122,7 @@ export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
|
|||||||
dataIndex: "readiness",
|
dataIndex: "readiness",
|
||||||
key: "readiness",
|
key: "readiness",
|
||||||
sorter: (a, b) => alphaSort(a.readiness, b.readiness),
|
sorter: (a, b) => alphaSort(a.readiness, b.readiness),
|
||||||
|
filteredValue: filter?.readiness || null,
|
||||||
filters: [
|
filters: [
|
||||||
{
|
{
|
||||||
text: t("courtesycars.readiness.ready"),
|
text: t("courtesycars.readiness.ready"),
|
||||||
@@ -171,7 +182,7 @@ export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
|
|||||||
title: t("courtesycars.fields.fuel"),
|
title: t("courtesycars.fields.fuel"),
|
||||||
dataIndex: "fuel",
|
dataIndex: "fuel",
|
||||||
key: "fuel",
|
key: "fuel",
|
||||||
sorter: (a, b) => alphaSort(a.fuel, b.fuel),
|
sorter: (a, b) => a.fuel - b.fuel,
|
||||||
sortOrder:
|
sortOrder:
|
||||||
state.sortedInfo.columnKey === "fuel" && state.sortedInfo.order,
|
state.sortedInfo.columnKey === "fuel" && state.sortedInfo.order,
|
||||||
render: (text, record) => {
|
render: (text, record) => {
|
||||||
@@ -180,12 +191,14 @@ export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
|
|||||||
return t("courtesycars.labels.fuel.full");
|
return t("courtesycars.labels.fuel.full");
|
||||||
case 88:
|
case 88:
|
||||||
return t("courtesycars.labels.fuel.78");
|
return t("courtesycars.labels.fuel.78");
|
||||||
|
case 75:
|
||||||
|
return t("courtesycars.labels.fuel.34");
|
||||||
case 63:
|
case 63:
|
||||||
return t("courtesycars.labels.fuel.58");
|
return t("courtesycars.labels.fuel.58");
|
||||||
case 50:
|
case 50:
|
||||||
return t("courtesycars.labels.fuel.12");
|
return t("courtesycars.labels.fuel.12");
|
||||||
case 38:
|
case 38:
|
||||||
return t("courtesycars.labels.fuel.34");
|
return t("courtesycars.labels.fuel.38");
|
||||||
case 25:
|
case 25:
|
||||||
return t("courtesycars.labels.fuel.14");
|
return t("courtesycars.labels.fuel.14");
|
||||||
case 13:
|
case 13:
|
||||||
@@ -227,7 +240,8 @@ export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
|
|||||||
];
|
];
|
||||||
|
|
||||||
const handleTableChange = (pagination, filters, sorter) => {
|
const handleTableChange = (pagination, filters, sorter) => {
|
||||||
setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
|
setState({ ...state, sortedInfo: sorter });
|
||||||
|
setFilter(filters);
|
||||||
};
|
};
|
||||||
|
|
||||||
const tableData = searchText
|
const tableData = searchText
|
||||||
|
|||||||
@@ -0,0 +1,169 @@
|
|||||||
|
import {Card, Table, Tag} from "antd";
|
||||||
|
import LoadingSkeleton from "../../loading-skeleton/loading-skeleton.component";
|
||||||
|
import {useTranslation} from "react-i18next";
|
||||||
|
import React, {useEffect, useState} from "react";
|
||||||
|
import moment from "moment";
|
||||||
|
import DashboardRefreshRequired from "../refresh-required.component";
|
||||||
|
import axios from "axios";
|
||||||
|
|
||||||
|
const fortyFiveDaysAgo = () => moment().subtract(45, 'days').toLocaleString();
|
||||||
|
|
||||||
|
export default function JobLifecycleDashboardComponent({data, bodyshop, ...cardProps}) {
|
||||||
|
const {t} = useTranslation();
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [lifecycleData, setLifecycleData] = useState(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
async function getLifecycleData() {
|
||||||
|
if (data && data.job_lifecycle) {
|
||||||
|
setLoading(true);
|
||||||
|
const response = await axios.post("/job/lifecycle", {
|
||||||
|
jobids: data.job_lifecycle.map(x => x.id),
|
||||||
|
statuses: bodyshop.md_ro_statuses
|
||||||
|
});
|
||||||
|
setLifecycleData(response.data.durations);
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getLifecycleData().catch(e => {
|
||||||
|
console.error(`Error in getLifecycleData: ${e}`);
|
||||||
|
})
|
||||||
|
}, [data, bodyshop]);
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: t('job_lifecycle.columns.status'),
|
||||||
|
dataIndex: 'status',
|
||||||
|
bgColor: 'red',
|
||||||
|
key: 'status',
|
||||||
|
render: (text, record) => {
|
||||||
|
return <Tag color={record.color}>{record.status}</Tag>
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('job_lifecycle.columns.human_readable'),
|
||||||
|
dataIndex: 'humanReadable',
|
||||||
|
key: 'humanReadable',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('job_lifecycle.columns.status_count'),
|
||||||
|
key: 'statusCount',
|
||||||
|
render: (text, record) => {
|
||||||
|
return lifecycleData.statusCounts[record.status];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('job_lifecycle.columns.percentage'),
|
||||||
|
dataIndex: 'percentage',
|
||||||
|
key: 'percentage',
|
||||||
|
render: (text, record) => {
|
||||||
|
return record.percentage.toFixed(2) + '%';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
if (!data) return null;
|
||||||
|
|
||||||
|
if (!data.job_lifecycle || !lifecycleData) return <DashboardRefreshRequired {...cardProps} />;
|
||||||
|
|
||||||
|
const extra = `${t('job_lifecycle.content.calculated_based_on')} ${lifecycleData.jobs} ${t('job_lifecycle.content.jobs_in_since')} ${fortyFiveDaysAgo()}`
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card title={t("job_lifecycle.titles.dashboard")} {...cardProps}>
|
||||||
|
<LoadingSkeleton loading={loading}>
|
||||||
|
<div style={{overflow: 'scroll', height: "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.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 extra={extra} type='inner' title={t('job_lifecycle.content.legend_title')}
|
||||||
|
style={{marginTop: '10px'}}>
|
||||||
|
<div>
|
||||||
|
{lifecycleData.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} [{lifecycleData.statusCounts[key.status]}] ({key.roundedPercentage})
|
||||||
|
</div>
|
||||||
|
</Tag>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
<Card style={{marginTop: "5px"}} type='inner' title={t("job_lifecycle.titles.top_durations")}>
|
||||||
|
<Table size="small" pagination={false} columns={columns}
|
||||||
|
dataSource={lifecycleData.summations.sort((a, b) => b.value - a.value).slice(0, 3)}/>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
</LoadingSkeleton>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const JobLifecycleDashboardGQL = `
|
||||||
|
job_lifecycle: jobs(where: {
|
||||||
|
actual_in: {
|
||||||
|
_gte: "${moment().subtract(45, 'days').toISOString()}"
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
id
|
||||||
|
actual_in
|
||||||
|
} `;
|
||||||
@@ -1,380 +1,391 @@
|
|||||||
import Icon, { SyncOutlined } from "@ant-design/icons";
|
import Icon, {SyncOutlined} from "@ant-design/icons";
|
||||||
import { gql, useMutation, useQuery } from "@apollo/client";
|
import {gql, useMutation, useQuery} from "@apollo/client";
|
||||||
import { Button, Dropdown, Menu, PageHeader, Space, notification } from "antd";
|
import {Button, Dropdown, Menu, notification, PageHeader, Space} from "antd";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
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 { Responsive, WidthProvider } from "react-grid-layout";
|
import {Responsive, WidthProvider} from "react-grid-layout";
|
||||||
import { useTranslation } from "react-i18next";
|
import {useTranslation} from "react-i18next";
|
||||||
import { MdClose } from "react-icons/md";
|
import {MdClose} from "react-icons/md";
|
||||||
import { connect } from "react-redux";
|
import {connect} from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import {createStructuredSelector} from "reselect";
|
||||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
import {logImEXEvent} from "../../firebase/firebase.utils";
|
||||||
import { UPDATE_DASHBOARD_LAYOUT } from "../../graphql/user.queries";
|
import {UPDATE_DASHBOARD_LAYOUT} from "../../graphql/user.queries";
|
||||||
import {
|
import {selectBodyshop, selectCurrentUser,} from "../../redux/user/user.selectors";
|
||||||
selectBodyshop,
|
|
||||||
selectCurrentUser,
|
|
||||||
} from "../../redux/user/user.selectors";
|
|
||||||
import AlertComponent from "../alert/alert.component";
|
import AlertComponent from "../alert/alert.component";
|
||||||
import DashboardMonthlyEmployeeEfficiency, {
|
import DashboardMonthlyEmployeeEfficiency, {
|
||||||
DashboardMonthlyEmployeeEfficiencyGql,
|
DashboardMonthlyEmployeeEfficiencyGql,
|
||||||
} from "../dashboard-components/monthly-employee-efficiency/monthly-employee-efficiency.component";
|
} from "../dashboard-components/monthly-employee-efficiency/monthly-employee-efficiency.component";
|
||||||
import DashboardMonthlyJobCosting from "../dashboard-components/monthly-job-costing/monthly-job-costing.component";
|
import DashboardMonthlyJobCosting from "../dashboard-components/monthly-job-costing/monthly-job-costing.component";
|
||||||
import DashboardMonthlyLaborSales from "../dashboard-components/monthly-labor-sales/monthly-labor-sales.component";
|
import DashboardMonthlyLaborSales from "../dashboard-components/monthly-labor-sales/monthly-labor-sales.component";
|
||||||
import DashboardMonthlyPartsSales from "../dashboard-components/monthly-parts-sales/monthly-parts-sales.component";
|
import DashboardMonthlyPartsSales from "../dashboard-components/monthly-parts-sales/monthly-parts-sales.component";
|
||||||
import DashboardMonthlyRevenueGraph, {
|
import DashboardMonthlyRevenueGraph, {
|
||||||
DashboardMonthlyRevenueGraphGql,
|
DashboardMonthlyRevenueGraphGql,
|
||||||
} from "../dashboard-components/monthly-revenue-graph/monthly-revenue-graph.component";
|
} from "../dashboard-components/monthly-revenue-graph/monthly-revenue-graph.component";
|
||||||
import DashboardProjectedMonthlySales, {
|
import DashboardProjectedMonthlySales, {
|
||||||
DashboardProjectedMonthlySalesGql,
|
DashboardProjectedMonthlySalesGql,
|
||||||
} from "../dashboard-components/pojected-monthly-sales/projected-monthly-sales.component";
|
} from "../dashboard-components/pojected-monthly-sales/projected-monthly-sales.component";
|
||||||
import DashboardTotalProductionDollars from "../dashboard-components/total-production-dollars/total-production-dollars.component";
|
import DashboardTotalProductionDollars
|
||||||
|
from "../dashboard-components/total-production-dollars/total-production-dollars.component";
|
||||||
import DashboardTotalProductionHours, {
|
import DashboardTotalProductionHours, {
|
||||||
DashboardTotalProductionHoursGql,
|
DashboardTotalProductionHoursGql,
|
||||||
} from "../dashboard-components/total-production-hours/total-production-hours.component";
|
} from "../dashboard-components/total-production-hours/total-production-hours.component";
|
||||||
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
|
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
|
||||||
//Combination of the following:
|
//Combination of the following:
|
||||||
// /node_modules/react-grid-layout/css/styles.css
|
// /node_modules/react-grid-layout/css/styles.css
|
||||||
// /node_modules/react-resizable/css/styles.css
|
// /node_modules/react-resizable/css/styles.css
|
||||||
import DashboardScheduledInToday, {
|
import DashboardScheduledInToday, {
|
||||||
DashboardScheduledInTodayGql,
|
DashboardScheduledInTodayGql,
|
||||||
} from "../dashboard-components/scheduled-in-today/scheduled-in-today.component";
|
} from "../dashboard-components/scheduled-in-today/scheduled-in-today.component";
|
||||||
import DashboardScheduledOutToday, {
|
import DashboardScheduledOutToday, {
|
||||||
DashboardScheduledOutTodayGql,
|
DashboardScheduledOutTodayGql,
|
||||||
} from "../dashboard-components/scheduled-out-today/scheduled-out-today.component";
|
} from "../dashboard-components/scheduled-out-today/scheduled-out-today.component";
|
||||||
|
import JobLifecycleDashboardComponent, {
|
||||||
|
JobLifecycleDashboardGQL
|
||||||
|
} from "../dashboard-components/job-lifecycle/job-lifecycle-dashboard.component";
|
||||||
import "./dashboard-grid.styles.scss";
|
import "./dashboard-grid.styles.scss";
|
||||||
import { GenerateDashboardData } from "./dashboard-grid.utils";
|
import {GenerateDashboardData} from "./dashboard-grid.utils";
|
||||||
|
|
||||||
const ResponsiveReactGridLayout = WidthProvider(Responsive);
|
const ResponsiveReactGridLayout = WidthProvider(Responsive);
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
currentUser: selectCurrentUser,
|
currentUser: selectCurrentUser,
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||||
});
|
});
|
||||||
|
|
||||||
export function DashboardGridComponent({ currentUser, bodyshop }) {
|
export function DashboardGridComponent({currentUser, bodyshop}) {
|
||||||
const { t } = useTranslation();
|
const {t} = useTranslation();
|
||||||
const [state, setState] = useState({
|
const [state, setState] = useState({
|
||||||
...(bodyshop.associations[0].user.dashboardlayout
|
...(bodyshop.associations[0].user.dashboardlayout
|
||||||
? bodyshop.associations[0].user.dashboardlayout
|
? bodyshop.associations[0].user.dashboardlayout
|
||||||
: { items: [], layout: {}, layouts: [] }),
|
: {items: [], layout: {}, layouts: []}),
|
||||||
});
|
|
||||||
|
|
||||||
const { loading, error, data, refetch } = useQuery(
|
|
||||||
createDashboardQuery(state),
|
|
||||||
{ fetchPolicy: "network-only", nextFetchPolicy: "network-only" }
|
|
||||||
);
|
|
||||||
|
|
||||||
const [updateLayout] = useMutation(UPDATE_DASHBOARD_LAYOUT);
|
|
||||||
|
|
||||||
const handleLayoutChange = async (layout, layouts) => {
|
|
||||||
logImEXEvent("dashboard_change_layout");
|
|
||||||
|
|
||||||
setState({ ...state, layout, layouts });
|
|
||||||
|
|
||||||
const result = await updateLayout({
|
|
||||||
variables: {
|
|
||||||
email: currentUser.email,
|
|
||||||
layout: { ...state, layout, layouts },
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
if (!!result.errors) {
|
|
||||||
notification["error"]({
|
|
||||||
message: t("dashboard.errors.updatinglayout", {
|
|
||||||
message: JSON.stringify(result.errors),
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const handleRemoveComponent = (key) => {
|
|
||||||
logImEXEvent("dashboard_remove_component", { name: key });
|
|
||||||
const idxToRemove = state.items.findIndex((i) => i.i === key);
|
|
||||||
|
|
||||||
const items = _.cloneDeep(state.items);
|
const {loading, error, data, refetch} = useQuery(
|
||||||
|
createDashboardQuery(state),
|
||||||
|
{fetchPolicy: "network-only", nextFetchPolicy: "network-only"}
|
||||||
|
);
|
||||||
|
|
||||||
items.splice(idxToRemove, 1);
|
const [updateLayout] = useMutation(UPDATE_DASHBOARD_LAYOUT);
|
||||||
setState({ ...state, items });
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleAddComponent = (e) => {
|
const handleLayoutChange = async (layout, layouts) => {
|
||||||
logImEXEvent("dashboard_add_component", { name: e });
|
logImEXEvent("dashboard_change_layout");
|
||||||
setState({
|
|
||||||
...state,
|
|
||||||
items: [
|
|
||||||
...state.items,
|
|
||||||
{
|
|
||||||
i: e.key,
|
|
||||||
x: (state.items.length * 2) % (state.cols || 12),
|
|
||||||
y: 99, // puts it at the bottom
|
|
||||||
w: componentList[e.key].w || 2,
|
|
||||||
h: componentList[e.key].h || 2,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const dashboarddata = React.useMemo(
|
setState({...state, layout, layouts});
|
||||||
() => GenerateDashboardData(data),
|
|
||||||
[data]
|
|
||||||
);
|
|
||||||
const existingLayoutKeys = state.items.map((i) => i.i);
|
|
||||||
const addComponentOverlay = (
|
|
||||||
<Menu onClick={handleAddComponent}>
|
|
||||||
{Object.keys(componentList).map((key) => (
|
|
||||||
<Menu.Item
|
|
||||||
key={key}
|
|
||||||
value={key}
|
|
||||||
disabled={existingLayoutKeys.includes(key)}
|
|
||||||
>
|
|
||||||
{componentList[key].label}
|
|
||||||
</Menu.Item>
|
|
||||||
))}
|
|
||||||
</Menu>
|
|
||||||
);
|
|
||||||
|
|
||||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
const result = await updateLayout({
|
||||||
|
variables: {
|
||||||
return (
|
email: currentUser.email,
|
||||||
<div>
|
layout: {...state, layout, layouts},
|
||||||
<PageHeader
|
},
|
||||||
extra={
|
});
|
||||||
<Space>
|
if (!!result.errors) {
|
||||||
<Button onClick={() => refetch()}>
|
notification["error"]({
|
||||||
<SyncOutlined />
|
message: t("dashboard.errors.updatinglayout", {
|
||||||
</Button>
|
message: JSON.stringify(result.errors),
|
||||||
<Dropdown overlay={addComponentOverlay} trigger={["click"]}>
|
}),
|
||||||
<Button>{t("dashboard.actions.addcomponent")}</Button>
|
});
|
||||||
</Dropdown>
|
|
||||||
</Space>
|
|
||||||
}
|
}
|
||||||
/>
|
};
|
||||||
|
const handleRemoveComponent = (key) => {
|
||||||
|
logImEXEvent("dashboard_remove_component", {name: key});
|
||||||
|
const idxToRemove = state.items.findIndex((i) => i.i === key);
|
||||||
|
|
||||||
<ResponsiveReactGridLayout
|
const items = _.cloneDeep(state.items);
|
||||||
className="layout"
|
|
||||||
breakpoints={{ lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 }}
|
items.splice(idxToRemove, 1);
|
||||||
cols={{ lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 }}
|
setState({...state, items});
|
||||||
width="100%"
|
};
|
||||||
layouts={state.layouts}
|
|
||||||
onLayoutChange={handleLayoutChange}
|
const handleAddComponent = (e) => {
|
||||||
// onBreakpointChange={onBreakpointChange}
|
logImEXEvent("dashboard_add_component", {name: e});
|
||||||
>
|
setState({
|
||||||
{state.items.map((item, index) => {
|
...state,
|
||||||
const TheComponent = componentList[item.i].component;
|
items: [
|
||||||
return (
|
...state.items,
|
||||||
<div
|
{
|
||||||
key={item.i}
|
i: e.key,
|
||||||
data-grid={{
|
x: (state.items.length * 2) % (state.cols || 12),
|
||||||
...item,
|
y: 99, // puts it at the bottom
|
||||||
minH: componentList[item.i].minH || 1,
|
w: componentList[e.key].w || 2,
|
||||||
minW: componentList[item.i].minW || 1,
|
h: componentList[e.key].h || 2,
|
||||||
}}
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const dashboarddata = React.useMemo(
|
||||||
|
() => GenerateDashboardData(data),
|
||||||
|
[data]
|
||||||
|
);
|
||||||
|
const existingLayoutKeys = state.items.map((i) => i.i);
|
||||||
|
const addComponentOverlay = (
|
||||||
|
<Menu onClick={handleAddComponent}>
|
||||||
|
{Object.keys(componentList).map((key) => (
|
||||||
|
<Menu.Item
|
||||||
|
key={key}
|
||||||
|
value={key}
|
||||||
|
disabled={existingLayoutKeys.includes(key)}
|
||||||
|
>
|
||||||
|
{componentList[key].label}
|
||||||
|
</Menu.Item>
|
||||||
|
))}
|
||||||
|
</Menu>
|
||||||
|
);
|
||||||
|
|
||||||
|
if (error) return <AlertComponent message={error.message} type="error"/>;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<PageHeader
|
||||||
|
extra={
|
||||||
|
<Space>
|
||||||
|
<Button onClick={() => refetch()}>
|
||||||
|
<SyncOutlined/>
|
||||||
|
</Button>
|
||||||
|
<Dropdown overlay={addComponentOverlay} trigger={["click"]}>
|
||||||
|
<Button>{t("dashboard.actions.addcomponent")}</Button>
|
||||||
|
</Dropdown>
|
||||||
|
</Space>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ResponsiveReactGridLayout
|
||||||
|
className="layout"
|
||||||
|
breakpoints={{lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0}}
|
||||||
|
cols={{lg: 12, md: 10, sm: 6, xs: 4, xxs: 2}}
|
||||||
|
width="100%"
|
||||||
|
layouts={state.layouts}
|
||||||
|
onLayoutChange={handleLayoutChange}
|
||||||
|
// onBreakpointChange={onBreakpointChange}
|
||||||
>
|
>
|
||||||
<LoadingSkeleton loading={loading}>
|
{state.items.map((item, index) => {
|
||||||
<Icon
|
const TheComponent = componentList[item.i].component;
|
||||||
component={MdClose}
|
return (
|
||||||
key={item.i}
|
<div
|
||||||
style={{
|
key={item.i}
|
||||||
position: "absolute",
|
data-grid={{
|
||||||
zIndex: "2",
|
...item,
|
||||||
right: ".25rem",
|
minH: componentList[item.i].minH || 1,
|
||||||
top: ".25rem",
|
minW: componentList[item.i].minW || 1,
|
||||||
cursor: "pointer",
|
}}
|
||||||
}}
|
>
|
||||||
onClick={() => handleRemoveComponent(item.i)}
|
<LoadingSkeleton loading={loading}>
|
||||||
/>
|
<Icon
|
||||||
<TheComponent className="dashboard-card" data={dashboarddata} />
|
component={MdClose}
|
||||||
</LoadingSkeleton>
|
key={item.i}
|
||||||
</div>
|
style={{
|
||||||
);
|
position: "absolute",
|
||||||
})}
|
zIndex: "2",
|
||||||
</ResponsiveReactGridLayout>
|
right: ".25rem",
|
||||||
</div>
|
top: ".25rem",
|
||||||
);
|
cursor: "pointer",
|
||||||
|
}}
|
||||||
|
onClick={() => handleRemoveComponent(item.i)}
|
||||||
|
/>
|
||||||
|
<TheComponent className="dashboard-card" bodyshop={bodyshop} data={dashboarddata}/>
|
||||||
|
</LoadingSkeleton>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</ResponsiveReactGridLayout>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect(
|
export default connect(
|
||||||
mapStateToProps,
|
mapStateToProps,
|
||||||
mapDispatchToProps
|
mapDispatchToProps
|
||||||
)(DashboardGridComponent);
|
)(DashboardGridComponent);
|
||||||
|
|
||||||
const componentList = {
|
const componentList = {
|
||||||
ProductionDollars: {
|
ProductionDollars: {
|
||||||
label: i18next.t("dashboard.titles.productiondollars"),
|
label: i18next.t("dashboard.titles.productiondollars"),
|
||||||
component: DashboardTotalProductionDollars,
|
component: DashboardTotalProductionDollars,
|
||||||
gqlFragment: null,
|
gqlFragment: null,
|
||||||
w: 1,
|
w: 1,
|
||||||
h: 1,
|
h: 1,
|
||||||
minW: 2,
|
minW: 2,
|
||||||
minH: 1,
|
minH: 1,
|
||||||
},
|
},
|
||||||
ProductionHours: {
|
ProductionHours: {
|
||||||
label: i18next.t("dashboard.titles.productionhours"),
|
label: i18next.t("dashboard.titles.productionhours"),
|
||||||
component: DashboardTotalProductionHours,
|
component: DashboardTotalProductionHours,
|
||||||
gqlFragment: DashboardTotalProductionHoursGql,
|
gqlFragment: DashboardTotalProductionHoursGql,
|
||||||
w: 3,
|
w: 3,
|
||||||
h: 1,
|
h: 1,
|
||||||
minW: 3,
|
minW: 3,
|
||||||
minH: 1,
|
minH: 1,
|
||||||
},
|
},
|
||||||
ProjectedMonthlySales: {
|
ProjectedMonthlySales: {
|
||||||
label: i18next.t("dashboard.titles.projectedmonthlysales"),
|
label: i18next.t("dashboard.titles.projectedmonthlysales"),
|
||||||
component: DashboardProjectedMonthlySales,
|
component: DashboardProjectedMonthlySales,
|
||||||
gqlFragment: DashboardProjectedMonthlySalesGql,
|
gqlFragment: DashboardProjectedMonthlySalesGql,
|
||||||
w: 2,
|
w: 2,
|
||||||
h: 1,
|
h: 1,
|
||||||
minW: 2,
|
minW: 2,
|
||||||
minH: 1,
|
minH: 1,
|
||||||
},
|
},
|
||||||
MonthlyRevenueGraph: {
|
MonthlyRevenueGraph: {
|
||||||
label: i18next.t("dashboard.titles.monthlyrevenuegraph"),
|
label: i18next.t("dashboard.titles.monthlyrevenuegraph"),
|
||||||
component: DashboardMonthlyRevenueGraph,
|
component: DashboardMonthlyRevenueGraph,
|
||||||
gqlFragment: DashboardMonthlyRevenueGraphGql,
|
gqlFragment: DashboardMonthlyRevenueGraphGql,
|
||||||
w: 4,
|
w: 4,
|
||||||
h: 2,
|
h: 2,
|
||||||
minW: 4,
|
minW: 4,
|
||||||
minH: 2,
|
minH: 2,
|
||||||
},
|
},
|
||||||
MonthlyJobCosting: {
|
MonthlyJobCosting: {
|
||||||
label: i18next.t("dashboard.titles.monthlyjobcosting"),
|
label: i18next.t("dashboard.titles.monthlyjobcosting"),
|
||||||
component: DashboardMonthlyJobCosting,
|
component: DashboardMonthlyJobCosting,
|
||||||
gqlFragment: null,
|
gqlFragment: null,
|
||||||
minW: 6,
|
minW: 6,
|
||||||
minH: 3,
|
minH: 3,
|
||||||
w: 6,
|
w: 6,
|
||||||
h: 3,
|
h: 3,
|
||||||
},
|
},
|
||||||
MonthlyPartsSales: {
|
MonthlyPartsSales: {
|
||||||
label: i18next.t("dashboard.titles.monthlypartssales"),
|
label: i18next.t("dashboard.titles.monthlypartssales"),
|
||||||
component: DashboardMonthlyPartsSales,
|
component: DashboardMonthlyPartsSales,
|
||||||
gqlFragment: null,
|
gqlFragment: null,
|
||||||
minW: 2,
|
minW: 2,
|
||||||
minH: 2,
|
minH: 2,
|
||||||
w: 2,
|
w: 2,
|
||||||
h: 2,
|
h: 2,
|
||||||
},
|
},
|
||||||
MonthlyLaborSales: {
|
MonthlyLaborSales: {
|
||||||
label: i18next.t("dashboard.titles.monthlylaborsales"),
|
label: i18next.t("dashboard.titles.monthlylaborsales"),
|
||||||
component: DashboardMonthlyLaborSales,
|
component: DashboardMonthlyLaborSales,
|
||||||
gqlFragment: null,
|
gqlFragment: null,
|
||||||
minW: 2,
|
minW: 2,
|
||||||
minH: 2,
|
minH: 2,
|
||||||
w: 2,
|
w: 2,
|
||||||
h: 2,
|
h: 2,
|
||||||
},
|
},
|
||||||
MonthlyEmployeeEfficency: {
|
// Typo in Efficency should be Efficiency, but changing it would reset users dashboard settings
|
||||||
label: i18next.t("dashboard.titles.monthlyemployeeefficiency"),
|
MonthlyEmployeeEfficency: {
|
||||||
component: DashboardMonthlyEmployeeEfficiency,
|
label: i18next.t("dashboard.titles.monthlyemployeeefficiency"),
|
||||||
gqlFragment: DashboardMonthlyEmployeeEfficiencyGql,
|
component: DashboardMonthlyEmployeeEfficiency,
|
||||||
minW: 2,
|
gqlFragment: DashboardMonthlyEmployeeEfficiencyGql,
|
||||||
minH: 2,
|
minW: 2,
|
||||||
w: 2,
|
minH: 2,
|
||||||
h: 2,
|
w: 2,
|
||||||
},
|
h: 2,
|
||||||
ScheduleInToday: {
|
},
|
||||||
label: i18next.t("dashboard.titles.scheduledintoday"),
|
ScheduleInToday: {
|
||||||
component: DashboardScheduledInToday,
|
label: i18next.t("dashboard.titles.scheduledintoday"),
|
||||||
gqlFragment: DashboardScheduledInTodayGql,
|
component: DashboardScheduledInToday,
|
||||||
minW: 6,
|
gqlFragment: DashboardScheduledInTodayGql,
|
||||||
minH: 2,
|
minW: 6,
|
||||||
w: 10,
|
minH: 2,
|
||||||
h: 3,
|
w: 10,
|
||||||
},
|
h: 3,
|
||||||
ScheduleOutToday: {
|
},
|
||||||
label: i18next.t("dashboard.titles.scheduledouttoday"),
|
ScheduleOutToday: {
|
||||||
component: DashboardScheduledOutToday,
|
label: i18next.t("dashboard.titles.scheduledouttoday"),
|
||||||
gqlFragment: DashboardScheduledOutTodayGql,
|
component: DashboardScheduledOutToday,
|
||||||
minW: 6,
|
gqlFragment: DashboardScheduledOutTodayGql,
|
||||||
minH: 2,
|
minW: 6,
|
||||||
w: 10,
|
minH: 2,
|
||||||
h: 3,
|
w: 10,
|
||||||
},
|
h: 3,
|
||||||
|
},
|
||||||
|
JobLifecycle: {
|
||||||
|
label: i18next.t("dashboard.titles.joblifecycle"),
|
||||||
|
component: JobLifecycleDashboardComponent,
|
||||||
|
gqlFragment: JobLifecycleDashboardGQL,
|
||||||
|
minW: 6,
|
||||||
|
minH: 3,
|
||||||
|
w: 6,
|
||||||
|
h: 3,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const createDashboardQuery = (state) => {
|
const createDashboardQuery = (state) => {
|
||||||
const componentBasedAdditions =
|
const componentBasedAdditions =
|
||||||
state &&
|
state &&
|
||||||
Array.isArray(state.layout) &&
|
Array.isArray(state.layout) &&
|
||||||
state.layout
|
state.layout
|
||||||
.map((item, index) => componentList[item.i].gqlFragment || "")
|
.map((item, index) => componentList[item.i].gqlFragment || "")
|
||||||
.join("");
|
.join("");
|
||||||
return gql`
|
return gql`
|
||||||
query QUERY_DASHBOARD_DETAILS { ${componentBasedAdditions || ""}
|
query QUERY_DASHBOARD_DETAILS { ${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()
|
||||||
.startOf("month")
|
.startOf("month")
|
||||||
.startOf("day")
|
.startOf("day")
|
||||||
.toISOString()}"}}, {date_invoiced: {_lte: "${moment()
|
.toISOString()}"}}, {date_invoiced: {_lte: "${moment()
|
||||||
.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
|
||||||
rate_laa
|
rate_laa
|
||||||
rate_lab
|
rate_lab
|
||||||
rate_lad
|
rate_lad
|
||||||
rate_lae
|
rate_lae
|
||||||
rate_laf
|
rate_laf
|
||||||
rate_lag
|
rate_lag
|
||||||
rate_lam
|
rate_lam
|
||||||
rate_lar
|
rate_lar
|
||||||
rate_las
|
rate_las
|
||||||
rate_lau
|
rate_lau
|
||||||
rate_ma2s
|
rate_ma2s
|
||||||
rate_ma2t
|
rate_ma2t
|
||||||
rate_ma3s
|
rate_ma3s
|
||||||
rate_mabl
|
rate_mabl
|
||||||
rate_macs
|
rate_macs
|
||||||
rate_mahw
|
rate_mahw
|
||||||
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
|
id
|
||||||
ro_number
|
ro_number
|
||||||
ins_co_nm
|
ins_co_nm
|
||||||
job_totals
|
job_totals
|
||||||
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
|
||||||
}
|
}
|
||||||
labhrs: joblines_aggregate(where: { mod_lbr_ty: { _neq: "LAR" }, removed: { _eq: false } }) {
|
labhrs: joblines_aggregate(where: { mod_lbr_ty: { _neq: "LAR" }, removed: { _eq: false } }) {
|
||||||
aggregate {
|
aggregate {
|
||||||
sum {
|
sum {
|
||||||
mod_lb_hrs
|
mod_lb_hrs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
larhrs: joblines_aggregate(where: { mod_lbr_ty: { _eq: "LAR" }, removed: { _eq: false } }) {
|
larhrs: joblines_aggregate(where: { mod_lbr_ty: { _eq: "LAR" }, removed: { _eq: false } }) {
|
||||||
aggregate {
|
aggregate {
|
||||||
sum {
|
sum {
|
||||||
mod_lb_hrs
|
mod_lb_hrs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}`;
|
}`;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ export default function ScheduleEventContainer({ bodyshop, event, refetch }) {
|
|||||||
insertAuditTrail({
|
insertAuditTrail({
|
||||||
jobid: event.job.id,
|
jobid: event.job.id,
|
||||||
operation: AuditTrailMapping.appointmentcancel(lost_sale_reason),
|
operation: AuditTrailMapping.appointmentcancel(lost_sale_reason),
|
||||||
|
type: "appointmentcancel",
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,8 +28,8 @@ const mapStateToProps = createStructuredSelector({
|
|||||||
currentUser: selectCurrentUser,
|
currentUser: selectCurrentUser,
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
insertAuditTrail: ({ jobid, operation }) =>
|
insertAuditTrail: ({ jobid, operation, type }) =>
|
||||||
dispatch(insertAuditTrail({ jobid, operation })),
|
dispatch(insertAuditTrail({ jobid, operation, type })),
|
||||||
});
|
});
|
||||||
|
|
||||||
export function JobChecklistForm({
|
export function JobChecklistForm({
|
||||||
@@ -183,6 +183,7 @@ export function JobChecklistForm({
|
|||||||
(type === "intake" && bodyshop.md_ro_statuses.default_arrived) ||
|
(type === "intake" && bodyshop.md_ro_statuses.default_arrived) ||
|
||||||
(type === "deliver" && bodyshop.md_ro_statuses.default_delivered)
|
(type === "deliver" && bodyshop.md_ro_statuses.default_delivered)
|
||||||
),
|
),
|
||||||
|
type: "jobchecklist",
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
notification["error"]({
|
notification["error"]({
|
||||||
|
|||||||
@@ -18,10 +18,8 @@ export default function JobDetailCardsTotalsComponent({ loading, data }) {
|
|||||||
/>
|
/>
|
||||||
<Statistic
|
<Statistic
|
||||||
className="imex-flex-row__margin-large"
|
className="imex-flex-row__margin-large"
|
||||||
title={t("jobs.fields.ded_amt")}
|
title={t("jobs.fields.customerowing")}
|
||||||
value={Dinero({
|
value={Dinero(data.job_totals.totals.custPayable.total).toFormat()}
|
||||||
amount: Math.round((data.ded_amt || 0) * 100),
|
|
||||||
}).toFormat()}
|
|
||||||
/>
|
/>
|
||||||
<Statistic
|
<Statistic
|
||||||
className="imex-flex-row__margin-large"
|
className="imex-flex-row__margin-large"
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ export function JobEmployeeAssignments({
|
|||||||
jobRO,
|
jobRO,
|
||||||
body,
|
body,
|
||||||
refinish,
|
refinish,
|
||||||
|
|
||||||
prep,
|
prep,
|
||||||
csr,
|
csr,
|
||||||
handleAdd,
|
handleAdd,
|
||||||
@@ -78,7 +77,7 @@ export function JobEmployeeAssignments({
|
|||||||
setVisibility(false);
|
setVisibility(false);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Assign
|
{t("allocations.actions.assign")}
|
||||||
</Button>
|
</Button>
|
||||||
<Button onClick={() => setVisibility(false)}>Close</Button>
|
<Button onClick={() => setVisibility(false)}>Close</Button>
|
||||||
</Space>
|
</Space>
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ const mapStateToProps = createStructuredSelector({
|
|||||||
//currentUser: selectCurrentUser
|
//currentUser: selectCurrentUser
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
insertAuditTrail: ({ jobid, operation }) =>
|
insertAuditTrail: ({ jobid, operation, type }) =>
|
||||||
dispatch(insertAuditTrail({ jobid, operation })),
|
dispatch(insertAuditTrail({ jobid, operation, type })),
|
||||||
});
|
});
|
||||||
export default connect(
|
export default connect(
|
||||||
mapStateToProps,
|
mapStateToProps,
|
||||||
@@ -43,12 +43,13 @@ export function JobEmployeeAssignmentsContainer({
|
|||||||
});
|
});
|
||||||
if (refetch) refetch();
|
if (refetch) refetch();
|
||||||
|
|
||||||
insertAuditTrail({
|
if (!!!result.errors) {
|
||||||
jobid: job.id,
|
insertAuditTrail({
|
||||||
operation: AuditTrailMapping.jobassignmentchange(operation, name),
|
jobid: job.id,
|
||||||
});
|
operation: AuditTrailMapping.jobassignmentchange(operation, name),
|
||||||
|
type: "jobassignmentchange",
|
||||||
if (!!result.errors) {
|
});
|
||||||
|
} else {
|
||||||
notification["error"]({
|
notification["error"]({
|
||||||
message: t("jobs.errors.assigning", {
|
message: t("jobs.errors.assigning", {
|
||||||
message: JSON.stringify(result.errors),
|
message: JSON.stringify(result.errors),
|
||||||
@@ -66,17 +67,19 @@ export function JobEmployeeAssignmentsContainer({
|
|||||||
variables: { jobId: job.id, job: { [empAssignment]: null } },
|
variables: { jobId: job.id, job: { [empAssignment]: null } },
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!!result.errors) {
|
if (!!!result.errors) {
|
||||||
|
insertAuditTrail({
|
||||||
|
jobid: job.id,
|
||||||
|
operation: AuditTrailMapping.jobassignmentremoved(operation),
|
||||||
|
type: "jobassignmentremoved",
|
||||||
|
});
|
||||||
|
} else {
|
||||||
notification["error"]({
|
notification["error"]({
|
||||||
message: t("jobs.errors.assigning", {
|
message: t("jobs.errors.assigning", {
|
||||||
message: JSON.stringify(result.errors),
|
message: JSON.stringify(result.errors),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
insertAuditTrail({
|
|
||||||
jobid: job.id,
|
|
||||||
operation: AuditTrailMapping.jobassignmentremoved(operation),
|
|
||||||
});
|
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -28,8 +28,8 @@ const mapStateToProps = createStructuredSelector({
|
|||||||
//currentUser: selectCurrentUser
|
//currentUser: selectCurrentUser
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
insertAuditTrail: ({ jobid, operation }) =>
|
insertAuditTrail: ({ jobid, operation, type }) =>
|
||||||
dispatch(insertAuditTrail({ jobid, operation })),
|
dispatch(insertAuditTrail({ jobid, operation, type })),
|
||||||
});
|
});
|
||||||
export default connect(
|
export default connect(
|
||||||
mapStateToProps,
|
mapStateToProps,
|
||||||
@@ -113,6 +113,7 @@ export function JobLineConvertToLabor({
|
|||||||
hours: calculateAdjustment({ mod_lbr_ty, job, jobline }).toFixed(1),
|
hours: calculateAdjustment({ mod_lbr_ty, job, jobline }).toFixed(1),
|
||||||
mod_lbr_ty,
|
mod_lbr_ty,
|
||||||
}),
|
}),
|
||||||
|
type: "jobmodifylbradj",
|
||||||
});
|
});
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
setVisibility(false);
|
setVisibility(false);
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ export default function JobReconciliationBillsTable({
|
|||||||
state.sortedInfo.order,
|
state.sortedInfo.order,
|
||||||
|
|
||||||
render: (text, record) => (
|
render: (text, record) => (
|
||||||
<Checkbox disabled checked={record.bill.is_credit_memo} />
|
<Checkbox checked={record.bill.is_credit_memo} />
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ const mapStateToProps = createStructuredSelector({
|
|||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
insertAuditTrail: ({ jobid, operation }) =>
|
insertAuditTrail: ({ jobid, operation, type }) =>
|
||||||
dispatch(insertAuditTrail({ jobid, operation })),
|
dispatch(insertAuditTrail({ jobid, operation, type })),
|
||||||
});
|
});
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(JobsAdminStatus);
|
export default connect(mapStateToProps, mapDispatchToProps)(JobsAdminStatus);
|
||||||
|
|
||||||
@@ -32,6 +32,7 @@ export function JobsAdminStatus({ insertAuditTrail, bodyshop, job }) {
|
|||||||
insertAuditTrail({
|
insertAuditTrail({
|
||||||
jobid: job.id,
|
jobid: job.id,
|
||||||
operation: AuditTrailMapping.admin_jobstatuschange(status),
|
operation: AuditTrailMapping.admin_jobstatuschange(status),
|
||||||
|
type: "admin_jobstatuschange",
|
||||||
});
|
});
|
||||||
// refetch();
|
// refetch();
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ const mapStateToProps = createStructuredSelector({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
insertAuditTrail: ({ jobid, operation }) =>
|
insertAuditTrail: ({ jobid, operation, type }) =>
|
||||||
dispatch(insertAuditTrail({ jobid, operation })),
|
dispatch(insertAuditTrail({ jobid, operation, type })),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(
|
export default connect(
|
||||||
@@ -57,6 +57,7 @@ export function JobsAdminDatesChange({ insertAuditTrail, job }) {
|
|||||||
? DateTimeFormat(changedAuditFields[key])
|
? DateTimeFormat(changedAuditFields[key])
|
||||||
: changedAuditFields[key]
|
: changedAuditFields[key]
|
||||||
),
|
),
|
||||||
|
type: "admin_jobfieldchange",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ const mapStateToProps = createStructuredSelector({
|
|||||||
currentUser: selectCurrentUser,
|
currentUser: selectCurrentUser,
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
insertAuditTrail: ({ jobid, operation }) =>
|
insertAuditTrail: ({ jobid, operation, type }) =>
|
||||||
dispatch(insertAuditTrail({ jobid, operation })),
|
dispatch(insertAuditTrail({ jobid, operation, type })),
|
||||||
});
|
});
|
||||||
export default connect(
|
export default connect(
|
||||||
mapStateToProps,
|
mapStateToProps,
|
||||||
@@ -59,6 +59,7 @@ export function JobAdminMarkReexport({
|
|||||||
insertAuditTrail({
|
insertAuditTrail({
|
||||||
jobid: job.id,
|
jobid: job.id,
|
||||||
operation: AuditTrailMapping.admin_jobmarkforreexport(),
|
operation: AuditTrailMapping.admin_jobmarkforreexport(),
|
||||||
|
type: "admin_jobmarkforreexport",
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
notification["error"]({
|
notification["error"]({
|
||||||
@@ -99,6 +100,7 @@ export function JobAdminMarkReexport({
|
|||||||
insertAuditTrail({
|
insertAuditTrail({
|
||||||
jobid: job.id,
|
jobid: job.id,
|
||||||
operation: AuditTrailMapping.admin_jobmarkexported(),
|
operation: AuditTrailMapping.admin_jobmarkexported(),
|
||||||
|
type: "admin_jobmarkexported",
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
notification["error"]({
|
notification["error"]({
|
||||||
@@ -124,6 +126,7 @@ export function JobAdminMarkReexport({
|
|||||||
insertAuditTrail({
|
insertAuditTrail({
|
||||||
jobid: job.id,
|
jobid: job.id,
|
||||||
operation: AuditTrailMapping.admin_jobuninvoice(),
|
operation: AuditTrailMapping.admin_jobuninvoice(),
|
||||||
|
type: "admin_jobuninvoice",
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
notification["error"]({
|
notification["error"]({
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
|||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({});
|
const mapStateToProps = createStructuredSelector({});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
insertAuditTrail: ({ jobid, operation }) =>
|
insertAuditTrail: ({ jobid, operation, type }) =>
|
||||||
dispatch(insertAuditTrail({ jobid, operation })),
|
dispatch(insertAuditTrail({ jobid, operation, type })),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(JobsAdminRemoveAR);
|
export default connect(mapStateToProps, mapDispatchToProps)(JobsAdminRemoveAR);
|
||||||
@@ -34,6 +34,7 @@ export function JobsAdminRemoveAR({ insertAuditTrail, job }) {
|
|||||||
insertAuditTrail({
|
insertAuditTrail({
|
||||||
jobid: job.id,
|
jobid: job.id,
|
||||||
operation: AuditTrailMapping.admin_job_remove_from_ar(value),
|
operation: AuditTrailMapping.admin_job_remove_from_ar(value),
|
||||||
|
type: "admin_job_remove_from_ar",
|
||||||
});
|
});
|
||||||
setSwitchValue(value);
|
setSwitchValue(value);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -17,8 +17,8 @@ const mapStateToProps = createStructuredSelector({
|
|||||||
currentUser: selectCurrentUser,
|
currentUser: selectCurrentUser,
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
insertAuditTrail: ({ jobid, operation }) =>
|
insertAuditTrail: ({ jobid, operation, type }) =>
|
||||||
dispatch(insertAuditTrail({ jobid, operation })),
|
dispatch(insertAuditTrail({ jobid, operation, type })),
|
||||||
});
|
});
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(JobsAdminUnvoid);
|
export default connect(mapStateToProps, mapDispatchToProps)(JobsAdminUnvoid);
|
||||||
|
|
||||||
@@ -49,6 +49,7 @@ export function JobsAdminUnvoid({
|
|||||||
insertAuditTrail({
|
insertAuditTrail({
|
||||||
jobid: job.id,
|
jobid: job.id,
|
||||||
operation: AuditTrailMapping.admin_jobunvoid(),
|
operation: AuditTrailMapping.admin_jobunvoid(),
|
||||||
|
type: "admin_jobunvoid",
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
notification["error"]({
|
notification["error"]({
|
||||||
|
|||||||
@@ -47,8 +47,8 @@ const mapStateToProps = createStructuredSelector({
|
|||||||
currentUser: selectCurrentUser,
|
currentUser: selectCurrentUser,
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
insertAuditTrail: ({ jobid, operation }) =>
|
insertAuditTrail: ({ jobid, operation, type }) =>
|
||||||
dispatch(insertAuditTrail({ jobid, operation })),
|
dispatch(insertAuditTrail({ jobid, operation, type })),
|
||||||
});
|
});
|
||||||
export function JobsAvailableContainer({
|
export function JobsAvailableContainer({
|
||||||
bodyshop,
|
bodyshop,
|
||||||
@@ -190,6 +190,7 @@ export function JobsAvailableContainer({
|
|||||||
insertAuditTrail({
|
insertAuditTrail({
|
||||||
jobid: r.data.insert_jobs.returning[0].id,
|
jobid: r.data.insert_jobs.returning[0].id,
|
||||||
operation: AuditTrailMapping.jobimported(),
|
operation: AuditTrailMapping.jobimported(),
|
||||||
|
type: "jobimported",
|
||||||
});
|
});
|
||||||
|
|
||||||
deleteJob({
|
deleteJob({
|
||||||
@@ -350,6 +351,7 @@ export function JobsAvailableContainer({
|
|||||||
insertAuditTrail({
|
insertAuditTrail({
|
||||||
jobid: selectedJob,
|
jobid: selectedJob,
|
||||||
operation: AuditTrailMapping.jobsupplement(),
|
operation: AuditTrailMapping.jobsupplement(),
|
||||||
|
type: "jobsupplement",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ const mapStateToProps = createStructuredSelector({
|
|||||||
jobRO: selectJobReadOnly,
|
jobRO: selectJobReadOnly,
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
insertAuditTrail: ({ jobid, operation }) =>
|
insertAuditTrail: ({ jobid, operation, type }) =>
|
||||||
dispatch(insertAuditTrail({ jobid, operation })),
|
dispatch(insertAuditTrail({ jobid, operation, type })),
|
||||||
});
|
});
|
||||||
|
|
||||||
export function JobsChangeStatus({ job, bodyshop, jobRO, insertAuditTrail }) {
|
export function JobsChangeStatus({ job, bodyshop, jobRO, insertAuditTrail }) {
|
||||||
@@ -35,6 +35,7 @@ export function JobsChangeStatus({ job, bodyshop, jobRO, insertAuditTrail }) {
|
|||||||
insertAuditTrail({
|
insertAuditTrail({
|
||||||
jobid: job.id,
|
jobid: job.id,
|
||||||
operation: AuditTrailMapping.jobstatuschange(status),
|
operation: AuditTrailMapping.jobstatuschange(status),
|
||||||
|
type: "jobstatuschange",
|
||||||
});
|
});
|
||||||
// refetch();
|
// refetch();
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ const mapStateToProps = createStructuredSelector({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
insertAuditTrail: ({ jobid, operation }) =>
|
insertAuditTrail: ({ jobid, operation, type }) =>
|
||||||
dispatch(insertAuditTrail({ jobid, operation })),
|
dispatch(insertAuditTrail({ jobid, operation, type })),
|
||||||
});
|
});
|
||||||
|
|
||||||
function updateJobCache(items) {
|
function updateJobCache(items) {
|
||||||
@@ -192,6 +192,7 @@ export function JobsCloseExportButton({
|
|||||||
insertAuditTrail({
|
insertAuditTrail({
|
||||||
jobid: jobId,
|
jobid: jobId,
|
||||||
operation: AuditTrailMapping.jobexported(),
|
operation: AuditTrailMapping.jobexported(),
|
||||||
|
type: "jobexported",
|
||||||
});
|
});
|
||||||
updateJobCache(
|
updateJobCache(
|
||||||
jobUpdateResponse.data.update_jobs.returning.map((job) => job.id)
|
jobUpdateResponse.data.update_jobs.returning.map((job) => job.id)
|
||||||
@@ -217,6 +218,7 @@ export function JobsCloseExportButton({
|
|||||||
insertAuditTrail({
|
insertAuditTrail({
|
||||||
jobid: jobId,
|
jobid: jobId,
|
||||||
operation: AuditTrailMapping.jobexported(),
|
operation: AuditTrailMapping.jobexported(),
|
||||||
|
type: "jobexported",
|
||||||
});
|
});
|
||||||
updateJobCache([
|
updateJobCache([
|
||||||
...new Set(
|
...new Set(
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ const mapStateToProps = createStructuredSelector({
|
|||||||
jobRO: selectJobReadOnly,
|
jobRO: selectJobReadOnly,
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
insertAuditTrail: ({ jobid, operation }) =>
|
insertAuditTrail: ({ jobid, operation, type }) =>
|
||||||
dispatch(insertAuditTrail({ jobid, operation })),
|
dispatch(insertAuditTrail({ jobid, operation, type })),
|
||||||
});
|
});
|
||||||
|
|
||||||
export function JobsConvertButton({
|
export function JobsConvertButton({
|
||||||
@@ -78,6 +78,7 @@ export function JobsConvertButton({
|
|||||||
operation: AuditTrailMapping.jobconverted(
|
operation: AuditTrailMapping.jobconverted(
|
||||||
res.data.update_jobs.returning[0].ro_number
|
res.data.update_jobs.returning[0].ro_number
|
||||||
),
|
),
|
||||||
|
type: "jobconverted",
|
||||||
});
|
});
|
||||||
|
|
||||||
setVisible(false);
|
setVisible(false);
|
||||||
|
|||||||
@@ -1,4 +1,12 @@
|
|||||||
import { Collapse, Form, Input, InputNumber, Select, Switch } from "antd";
|
import {
|
||||||
|
Collapse,
|
||||||
|
Form,
|
||||||
|
Input,
|
||||||
|
InputNumber,
|
||||||
|
Select,
|
||||||
|
Space,
|
||||||
|
Switch,
|
||||||
|
} from "antd";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
@@ -10,6 +18,8 @@ import FormItemEmail from "../form-items-formatted/email-form-item.component";
|
|||||||
import FormItemPhone, {
|
import FormItemPhone, {
|
||||||
PhoneItemFormatterValidation,
|
PhoneItemFormatterValidation,
|
||||||
} from "../form-items-formatted/phone-form-item.component";
|
} from "../form-items-formatted/phone-form-item.component";
|
||||||
|
import JobsDetailChangeEstimator from "../jobs-detail-change-estimator/jobs-detail-change-estimator.component";
|
||||||
|
import JobsDetailChangeFilehandler from "../jobs-detail-change-filehandler/jobs-detail-change-filehandler.component";
|
||||||
import JobsDetailRatesChangeButton from "../jobs-detail-rates-change-button/jobs-detail-rates-change-button.component";
|
import JobsDetailRatesChangeButton from "../jobs-detail-rates-change-button/jobs-detail-rates-change-button.component";
|
||||||
import JobsDetailRatesParts from "../jobs-detail-rates/jobs-detail-rates.parts.component";
|
import JobsDetailRatesParts from "../jobs-detail-rates/jobs-detail-rates.parts.component";
|
||||||
import JobsMarkPstExempt from "../jobs-mark-pst-exempt/jobs-mark-pst-exempt.component";
|
import JobsMarkPstExempt from "../jobs-mark-pst-exempt/jobs-mark-pst-exempt.component";
|
||||||
@@ -25,6 +35,15 @@ const mapDispatchToProps = (dispatch) => ({
|
|||||||
export function JobsCreateJobsInfo({ bodyshop, form, selected }) {
|
export function JobsCreateJobsInfo({ bodyshop, form, selected }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { getFieldValue } = form;
|
const { getFieldValue } = form;
|
||||||
|
|
||||||
|
const handleInsCoChange = (value) => {
|
||||||
|
const selectedCompany = bodyshop.md_ins_cos.find((s) => s.name === value);
|
||||||
|
if (selectedCompany) {
|
||||||
|
form.setFieldValue("ins_addr1", selectedCompany.street1);
|
||||||
|
form.setFieldValue("ins_city", selectedCompany.city);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Collapse defaultActiveKey="insurance">
|
<Collapse defaultActiveKey="insurance">
|
||||||
@@ -34,26 +53,20 @@ export function JobsCreateJobsInfo({ bodyshop, form, selected }) {
|
|||||||
forceRender
|
forceRender
|
||||||
>
|
>
|
||||||
<LayoutFormRow>
|
<LayoutFormRow>
|
||||||
<Form.Item label={t("jobs.fields.ins_co_id")} name="ins_co_id">
|
<Form.Item label={t("jobs.fields.clm_no")} name="clm_no">
|
||||||
<Input />
|
<Input />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label={t("jobs.fields.policy_no")} name="policy_no">
|
<Form.Item label={t("jobs.fields.policy_no")} name="policy_no">
|
||||||
<Input />
|
<Input />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label={t("jobs.fields.clm_no")} name="clm_no">
|
|
||||||
<Input />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("jobs.fields.regie_number")}
|
label={t("jobs.fields.regie_number")}
|
||||||
name="regie_number"
|
name="regie_number"
|
||||||
>
|
>
|
||||||
<Input />
|
<Input />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label={t("jobs.fields.loss_date")} name="loss_date">
|
|
||||||
<FormDatePicker />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item label={t("jobs.fields.ins_co_nm")} name="ins_co_nm">
|
<Form.Item label={t("jobs.fields.ins_co_nm")} name="ins_co_nm">
|
||||||
<Select>
|
<Select onChange={handleInsCoChange}>
|
||||||
{bodyshop.md_ins_cos.map((s) => (
|
{bodyshop.md_ins_cos.map((s) => (
|
||||||
<Select.Option key={s.name} value={s.name}>
|
<Select.Option key={s.name} value={s.name}>
|
||||||
{s.name}
|
{s.name}
|
||||||
@@ -67,7 +80,15 @@ export function JobsCreateJobsInfo({ bodyshop, form, selected }) {
|
|||||||
<Form.Item label={t("jobs.fields.ins_city")} name="ins_city">
|
<Form.Item label={t("jobs.fields.ins_city")} name="ins_city">
|
||||||
<Input />
|
<Input />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label={t("jobs.fields.ins_ct_ln")} name="ins_ct_ln">
|
<Form.Item
|
||||||
|
label={
|
||||||
|
<Space>
|
||||||
|
{t("jobs.fields.ins_ct_ln")}
|
||||||
|
<JobsDetailChangeFilehandler form={form} />
|
||||||
|
</Space>
|
||||||
|
}
|
||||||
|
name="ins_ct_ln"
|
||||||
|
>
|
||||||
<Input />
|
<Input />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label={t("jobs.fields.ins_ct_fn")} name="ins_ct_fn">
|
<Form.Item label={t("jobs.fields.ins_ct_fn")} name="ins_ct_fn">
|
||||||
@@ -95,11 +116,24 @@ export function JobsCreateJobsInfo({ bodyshop, form, selected }) {
|
|||||||
>
|
>
|
||||||
<FormItemEmail email={getFieldValue("ins_ea")} />
|
<FormItemEmail email={getFieldValue("ins_ea")} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
<Form.Item label={t("jobs.fields.loss_date")} name="loss_date">
|
||||||
|
<FormDatePicker />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item label={t("jobs.fields.kmin")} name="kmin">
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
<Form.Item label={t("jobs.fields.est_co_nm")} name="est_co_nm">
|
<Form.Item label={t("jobs.fields.est_co_nm")} name="est_co_nm">
|
||||||
<Input />
|
<Input />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label={t("jobs.fields.est_ct_fn")} name="est_ct_fn">
|
<Form.Item
|
||||||
|
label={
|
||||||
|
<Space>
|
||||||
|
{t("jobs.fields.est_ct_fn")}
|
||||||
|
<JobsDetailChangeEstimator form={form} />
|
||||||
|
</Space>
|
||||||
|
}
|
||||||
|
name="est_ct_fn"
|
||||||
|
>
|
||||||
<Input />
|
<Input />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label={t("jobs.fields.est_ct_ln")} name="est_ct_ln">
|
<Form.Item label={t("jobs.fields.est_ct_ln")} name="est_ct_ln">
|
||||||
|
|||||||
@@ -37,6 +37,15 @@ const lossColDamage = { sm: { span: 24 }, md: { span: 6 }, lg: { span: 4 } };
|
|||||||
export function JobsDetailGeneral({ bodyshop, jobRO, job, form }) {
|
export function JobsDetailGeneral({ bodyshop, jobRO, job, form }) {
|
||||||
const { getFieldValue } = form;
|
const { getFieldValue } = form;
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const handleInsCoChange = (value) => {
|
||||||
|
const selectedCompany = bodyshop.md_ins_cos.find((s) => s.name === value);
|
||||||
|
if (selectedCompany) {
|
||||||
|
form.setFieldValue("ins_addr1", selectedCompany.street1);
|
||||||
|
form.setFieldValue("ins_city", selectedCompany.city);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<FormRow header={t("jobs.forms.claiminfo")}>
|
<FormRow header={t("jobs.forms.claiminfo")}>
|
||||||
@@ -71,7 +80,7 @@ export function JobsDetailGeneral({ bodyshop, jobRO, job, form }) {
|
|||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item label={t("jobs.fields.ins_co_nm")} name="ins_co_nm">
|
<Form.Item label={t("jobs.fields.ins_co_nm")} name="ins_co_nm">
|
||||||
<Select disabled={jobRO}>
|
<Select disabled={jobRO} onChange={handleInsCoChange}>
|
||||||
{bodyshop.md_ins_cos.map((s) => (
|
{bodyshop.md_ins_cos.map((s) => (
|
||||||
<Select.Option key={s.name} value={s.name}>
|
<Select.Option key={s.name} value={s.name}>
|
||||||
{s.name}
|
{s.name}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ export default function AddToProduction(
|
|||||||
insertAuditTrail({
|
insertAuditTrail({
|
||||||
jobid: jobId,
|
jobid: jobId,
|
||||||
operation: AuditTrailMapping.jobinproductionchange(!remove),
|
operation: AuditTrailMapping.jobinproductionchange(!remove),
|
||||||
|
type: "jobinproductionchange",
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
if (completionCallback) completionCallback();
|
if (completionCallback) completionCallback();
|
||||||
|
|||||||
@@ -52,8 +52,8 @@ const mapDispatchToProps = (dispatch) => ({
|
|||||||
dispatch(setModalContext({ context: context, modal: "timeTicket" })),
|
dispatch(setModalContext({ context: context, modal: "timeTicket" })),
|
||||||
setCardPaymentContext: (context) =>
|
setCardPaymentContext: (context) =>
|
||||||
dispatch(setModalContext({ context: context, modal: "cardPayment" })),
|
dispatch(setModalContext({ context: context, modal: "cardPayment" })),
|
||||||
insertAuditTrail: ({ jobid, operation }) =>
|
insertAuditTrail: ({ jobid, operation, type }) =>
|
||||||
dispatch(insertAuditTrail({ jobid, operation })),
|
dispatch(insertAuditTrail({ jobid, operation, type })),
|
||||||
});
|
});
|
||||||
|
|
||||||
export function JobsDetailHeaderActions({
|
export function JobsDetailHeaderActions({
|
||||||
@@ -115,6 +115,7 @@ export function JobsDetailHeaderActions({
|
|||||||
? !job.production_vars.alert
|
? !job.production_vars.alert
|
||||||
: true
|
: true
|
||||||
),
|
),
|
||||||
|
type: "alertToggle",
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -134,6 +135,7 @@ export function JobsDetailHeaderActions({
|
|||||||
operation: AuditTrailMapping.jobsuspend(
|
operation: AuditTrailMapping.jobsuspend(
|
||||||
!!job.suspended ? !job.suspended : true
|
!!job.suspended ? !job.suspended : true
|
||||||
),
|
),
|
||||||
|
type: "jobsuspend",
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -190,6 +192,7 @@ export function JobsDetailHeaderActions({
|
|||||||
jobid: job.id,
|
jobid: job.id,
|
||||||
operation:
|
operation:
|
||||||
AuditTrailMapping.appointmentcancel(lost_sale_reason),
|
AuditTrailMapping.appointmentcancel(lost_sale_reason),
|
||||||
|
type: "appointmentcancel",
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -549,6 +552,7 @@ export function JobsDetailHeaderActions({
|
|||||||
insertAuditTrail({
|
insertAuditTrail({
|
||||||
jobid: job.id,
|
jobid: job.id,
|
||||||
operation: AuditTrailMapping.jobvoid(),
|
operation: AuditTrailMapping.jobvoid(),
|
||||||
|
type: "jobvoid",
|
||||||
});
|
});
|
||||||
//go back to jobs list.
|
//go back to jobs list.
|
||||||
history.push(`/manage/`);
|
history.push(`/manage/`);
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ import {
|
|||||||
PauseCircleOutlined,
|
PauseCircleOutlined,
|
||||||
WarningFilled,
|
WarningFilled,
|
||||||
} from "@ant-design/icons";
|
} from "@ant-design/icons";
|
||||||
import { Card, Col, Row, Space, Tag, Tooltip } from "antd";
|
import { Card, Col, Divider, Row, Space, Tag, Tooltip } 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 { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
@@ -62,6 +63,13 @@ export function JobsDetailHeader({ job, bodyshop, disabled }) {
|
|||||||
${job.v_make_desc || ""}
|
${job.v_make_desc || ""}
|
||||||
${job.v_model_desc || ""}`.trim();
|
${job.v_model_desc || ""}`.trim();
|
||||||
|
|
||||||
|
const bodyHrs = job.joblines
|
||||||
|
.filter((j) => j.mod_lbr_ty !== "LAR")
|
||||||
|
.reduce((acc, val) => acc + val.mod_lb_hrs, 0);
|
||||||
|
const refinishHrs = job.joblines
|
||||||
|
.filter((line) => line.mod_lbr_ty === "LAR")
|
||||||
|
.reduce((acc, val) => acc + val.mod_lb_hrs, 0);
|
||||||
|
|
||||||
const ownerTitle = OwnerNameDisplayFunction(job).trim();
|
const ownerTitle = OwnerNameDisplayFunction(job).trim();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -93,7 +101,13 @@ export function JobsDetailHeader({ job, bodyshop, disabled }) {
|
|||||||
{job.status === bodyshop.md_ro_statuses.default_scheduled &&
|
{job.status === bodyshop.md_ro_statuses.default_scheduled &&
|
||||||
job.scheduled_in ? (
|
job.scheduled_in ? (
|
||||||
<Tag>
|
<Tag>
|
||||||
<DateTimeFormatter>{job.scheduled_in}</DateTimeFormatter>
|
<Link
|
||||||
|
to={`/manage/schedule?date=${moment(
|
||||||
|
job.scheduled_in
|
||||||
|
).format("YYYY-MM-DD")}`}
|
||||||
|
>
|
||||||
|
<DateTimeFormatter>{job.scheduled_in}</DateTimeFormatter>
|
||||||
|
</Link>
|
||||||
</Tag>
|
</Tag>
|
||||||
) : null}
|
) : null}
|
||||||
</Space>
|
</Space>
|
||||||
@@ -211,6 +225,12 @@ export function JobsDetailHeader({ job, bodyshop, disabled }) {
|
|||||||
{job.owner?.tax_number || ""}
|
{job.owner?.tax_number || ""}
|
||||||
</DataLabel>
|
</DataLabel>
|
||||||
)}
|
)}
|
||||||
|
<DataLabel
|
||||||
|
label={t("owners.fields.note")}
|
||||||
|
valueStyle={{ overflow: "hidden", textOverflow: "ellipsis" }}
|
||||||
|
>
|
||||||
|
{job.owner?.note || ""}
|
||||||
|
</DataLabel>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
</Col>
|
</Col>
|
||||||
@@ -299,6 +319,11 @@ export function JobsDetailHeader({ job, bodyshop, disabled }) {
|
|||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<JobEmployeeAssignments job={job} />
|
<JobEmployeeAssignments job={job} />
|
||||||
|
<Divider style={{ margin: ".5rem" }} />
|
||||||
|
<DataLabel label={t("jobs.labels.labor_hrs")}>
|
||||||
|
{bodyHrs.toFixed(1)} / {refinishHrs.toFixed(1)} /{" "}
|
||||||
|
{(bodyHrs + refinishHrs).toFixed(1)}
|
||||||
|
</DataLabel>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
</Col>
|
</Col>
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ const mapStateToProps = createStructuredSelector({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
insertAuditTrail: ({ jobid, operation }) =>
|
insertAuditTrail: ({ jobid, operation, type }) =>
|
||||||
dispatch(insertAuditTrail({ jobid, operation })),
|
dispatch(insertAuditTrail({ jobid, operation, type })),
|
||||||
});
|
});
|
||||||
|
|
||||||
function updateJobCache(items) {
|
function updateJobCache(items) {
|
||||||
@@ -189,6 +189,7 @@ export function JobsExportAllButton({
|
|||||||
insertAuditTrail({
|
insertAuditTrail({
|
||||||
jobid: job.id,
|
jobid: job.id,
|
||||||
operation: AuditTrailMapping.jobexported(),
|
operation: AuditTrailMapping.jobexported(),
|
||||||
|
type: "jobexported",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
updateJobCache(
|
updateJobCache(
|
||||||
@@ -230,6 +231,7 @@ export function JobsExportAllButton({
|
|||||||
insertAuditTrail({
|
insertAuditTrail({
|
||||||
jobid: successfulTransactionsSet[0],
|
jobid: successfulTransactionsSet[0],
|
||||||
operation: AuditTrailMapping.jobexported(),
|
operation: AuditTrailMapping.jobexported(),
|
||||||
|
type: "jobexported",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
updateJobCache(successfulTransactionsSet);
|
updateJobCache(successfulTransactionsSet);
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ 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 { OwnerNameDisplayFunction } from "./../owner-name-display/owner-name-display.component";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
@@ -145,7 +146,8 @@ export function JobsList({ bodyshop }) {
|
|||||||
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(OwnerNameDisplayFunction(a), OwnerNameDisplayFunction(b)),
|
||||||
sortOrder:
|
sortOrder:
|
||||||
state.sortedInfo.columnKey === "owner" && state.sortedInfo.order,
|
state.sortedInfo.columnKey === "owner" && state.sortedInfo.order,
|
||||||
render: (text, record) => {
|
render: (text, record) => {
|
||||||
@@ -188,7 +190,8 @@ export function JobsList({ bodyshop }) {
|
|||||||
dataIndex: "status",
|
dataIndex: "status",
|
||||||
key: "status",
|
key: "status",
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
sorter: (a, b) => alphaSort(a.status, b.status),
|
sorter: (a, b) =>
|
||||||
|
statusSort(a.status, b.status, bodyshop.md_ro_statuses.active_statuses),
|
||||||
sortOrder:
|
sortOrder:
|
||||||
state.sortedInfo.columnKey === "status" && state.sortedInfo.order,
|
state.sortedInfo.columnKey === "status" && state.sortedInfo.order,
|
||||||
filteredValue: filter?.status || null,
|
filteredValue: filter?.status || null,
|
||||||
@@ -219,6 +222,15 @@ export function JobsList({ bodyshop }) {
|
|||||||
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
|
||||||
@@ -266,6 +278,9 @@ export function JobsList({ bodyshop }) {
|
|||||||
dataIndex: "ins_co_nm",
|
dataIndex: "ins_co_nm",
|
||||||
key: "ins_co_nm",
|
key: "ins_co_nm",
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
|
sorter: (a, b) => alphaSort(a.ins_co_nm, b.ins_co_nm),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "ins_co_nm" && state.sortedInfo.order,
|
||||||
filteredValue: filter?.ins_co_nm || null,
|
filteredValue: filter?.ins_co_nm || null,
|
||||||
filters:
|
filters:
|
||||||
(jobs &&
|
(jobs &&
|
||||||
@@ -302,6 +317,13 @@ export function JobsList({ bodyshop }) {
|
|||||||
key: "estimator",
|
key: "estimator",
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
responsive: ["xl"],
|
responsive: ["xl"],
|
||||||
|
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,
|
||||||
filterSearch: true,
|
filterSearch: true,
|
||||||
filteredValue: filter?.estimator || null,
|
filteredValue: filter?.estimator || null,
|
||||||
filters:
|
filters:
|
||||||
|
|||||||
@@ -19,8 +19,8 @@ const mapStateToProps = createStructuredSelector({
|
|||||||
//currentUser: selectCurrentUser
|
//currentUser: selectCurrentUser
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
insertAuditTrail: ({ jobid, operation }) =>
|
insertAuditTrail: ({ jobid, operation, type }) =>
|
||||||
dispatch(insertAuditTrail({ jobid, operation })),
|
dispatch(insertAuditTrail({ jobid, operation, type })),
|
||||||
});
|
});
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(JobNotesContainer);
|
export default connect(mapStateToProps, mapDispatchToProps)(JobNotesContainer);
|
||||||
|
|
||||||
@@ -49,6 +49,7 @@ export function JobNotesContainer({ jobId, insertAuditTrail }) {
|
|||||||
insertAuditTrail({
|
insertAuditTrail({
|
||||||
jobid: jobId,
|
jobid: jobId,
|
||||||
operation: AuditTrailMapping.jobnotedeleted(),
|
operation: AuditTrailMapping.jobnotedeleted(),
|
||||||
|
type: "jobnotedeleted",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
setDeleteLoading(false);
|
setDeleteLoading(false);
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ const mapStateToProps = createStructuredSelector({
|
|||||||
//currentUser: selectCurrentUser
|
//currentUser: selectCurrentUser
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
insertAuditTrail: ({ jobid, operation }) =>
|
insertAuditTrail: ({ jobid, operation, type }) =>
|
||||||
dispatch(insertAuditTrail({ jobid, operation })),
|
dispatch(insertAuditTrail({ jobid, operation, type })),
|
||||||
});
|
});
|
||||||
export default connect(
|
export default connect(
|
||||||
mapStateToProps,
|
mapStateToProps,
|
||||||
@@ -76,6 +76,7 @@ export function LaborAllocationsAdjustmentEdit({
|
|||||||
values.hours -
|
values.hours -
|
||||||
((adjustments && adjustments[mod_lbr_ty]) || 0).toFixed(1),
|
((adjustments && adjustments[mod_lbr_ty]) || 0).toFixed(1),
|
||||||
}),
|
}),
|
||||||
|
type: "jobmodifylbradj",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
|
|||||||
@@ -19,8 +19,8 @@ const mapStateToProps = createStructuredSelector({
|
|||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
toggleModalVisible: () => dispatch(toggleModalVisible("noteUpsert")),
|
toggleModalVisible: () => dispatch(toggleModalVisible("noteUpsert")),
|
||||||
insertAuditTrail: ({ jobid, operation }) =>
|
insertAuditTrail: ({ jobid, operation, type }) =>
|
||||||
dispatch(insertAuditTrail({ jobid, operation })),
|
dispatch(insertAuditTrail({ jobid, operation, type })),
|
||||||
});
|
});
|
||||||
|
|
||||||
export function NoteUpsertModalContainer({
|
export function NoteUpsertModalContainer({
|
||||||
@@ -70,6 +70,7 @@ export function NoteUpsertModalContainer({
|
|||||||
insertAuditTrail({
|
insertAuditTrail({
|
||||||
jobid: context.jobId,
|
jobid: context.jobId,
|
||||||
operation: AuditTrailMapping.jobnoteupdated(),
|
operation: AuditTrailMapping.jobnoteupdated(),
|
||||||
|
type: "jobnoteupdated",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
if (refetch) refetch();
|
if (refetch) refetch();
|
||||||
@@ -102,6 +103,7 @@ export function NoteUpsertModalContainer({
|
|||||||
insertAuditTrail({
|
insertAuditTrail({
|
||||||
jobid: newJobId,
|
jobid: newJobId,
|
||||||
operation: AuditTrailMapping.jobnoteadded(),
|
operation: AuditTrailMapping.jobnoteadded(),
|
||||||
|
type: "jobnoteadded",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -115,6 +117,7 @@ export function NoteUpsertModalContainer({
|
|||||||
insertAuditTrail({
|
insertAuditTrail({
|
||||||
jobid: context.jobId,
|
jobid: context.jobId,
|
||||||
operation: AuditTrailMapping.jobnoteadded(),
|
operation: AuditTrailMapping.jobnoteadded(),
|
||||||
|
type: "jobnoteadded",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ function OwnerDetailJobsComponent({ bodyshop, owner }) {
|
|||||||
const handleTableChange = (pagination, filters, sorter) => {
|
const handleTableChange = (pagination, filters, sorter) => {
|
||||||
setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
|
setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
|
||||||
};
|
};
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
title: t("jobs.fields.ro_number"),
|
title: t("jobs.fields.ro_number"),
|
||||||
@@ -44,6 +44,15 @@ function OwnerDetailJobsComponent({ bodyshop, owner }) {
|
|||||||
title: t("jobs.fields.vehicle"),
|
title: t("jobs.fields.vehicle"),
|
||||||
dataIndex: "vehicleid",
|
dataIndex: "vehicleid",
|
||||||
key: "vehicleid",
|
key: "vehicleid",
|
||||||
|
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 === "vehicleid" && state.sortedInfo.order,
|
||||||
render: (text, record) =>
|
render: (text, record) =>
|
||||||
record.vehicleid ? (
|
record.vehicleid ? (
|
||||||
<Link to={`/manage/vehicles/${record.vehicleid}`}>
|
<Link to={`/manage/vehicles/${record.vehicleid}`}>
|
||||||
@@ -67,9 +76,15 @@ function OwnerDetailJobsComponent({ bodyshop, owner }) {
|
|||||||
title: t("jobs.fields.status"),
|
title: t("jobs.fields.status"),
|
||||||
dataIndex: "status",
|
dataIndex: "status",
|
||||||
key: "status",
|
key: "status",
|
||||||
sorter: (a, b) => statusSort(a.status, b.status, bodyshop.md_ro_statuses.statuses),
|
sorter: (a, b) =>
|
||||||
|
statusSort(a.status, b.status, bodyshop.md_ro_statuses.statuses),
|
||||||
sortOrder:
|
sortOrder:
|
||||||
state.sortedInfo.columnKey === "status" && state.sortedInfo.order,
|
state.sortedInfo.columnKey === "status" && state.sortedInfo.order,
|
||||||
|
filters: bodyshop.md_ro_statuses.statuses.map((status) => ({
|
||||||
|
text: status,
|
||||||
|
value: status,
|
||||||
|
})),
|
||||||
|
onFilter: (value, record) => value.includes(record.status),
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
import { useMutation, useQuery, useApolloClient } from "@apollo/client";
|
import { useApolloClient, useMutation, useQuery } from "@apollo/client";
|
||||||
|
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||||
import { Form, Modal, notification } from "antd";
|
import { Form, Modal, notification } from "antd";
|
||||||
|
import axios from "axios";
|
||||||
|
import _ from "lodash";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
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 { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import { logImEXEvent, auth } from "../../firebase/firebase.utils";
|
import { auth, logImEXEvent } from "../../firebase/firebase.utils";
|
||||||
import { UPDATE_JOB_LINE_STATUS } from "../../graphql/jobs-lines.queries";
|
import { UPDATE_JOB_LINE_STATUS } from "../../graphql/jobs-lines.queries";
|
||||||
|
import { UPDATE_JOB } from "../../graphql/jobs.queries";
|
||||||
import {
|
import {
|
||||||
INSERT_NEW_PARTS_ORDERS,
|
INSERT_NEW_PARTS_ORDERS,
|
||||||
QUERY_PARTS_ORDER_OEC,
|
QUERY_PARTS_ORDER_OEC,
|
||||||
@@ -29,10 +33,6 @@ import { TemplateList } from "../../utils/TemplateConstants";
|
|||||||
import AlertComponent from "../alert/alert.component";
|
import AlertComponent from "../alert/alert.component";
|
||||||
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
|
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
|
||||||
import PartsOrderModalComponent from "./parts-order-modal.component";
|
import PartsOrderModalComponent from "./parts-order-modal.component";
|
||||||
import axios from "axios";
|
|
||||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
|
||||||
import _ from "lodash";
|
|
||||||
import { UPDATE_JOB } from "../../graphql/jobs.queries";
|
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
currentUser: selectCurrentUser,
|
currentUser: selectCurrentUser,
|
||||||
@@ -45,8 +45,8 @@ const mapDispatchToProps = (dispatch) => ({
|
|||||||
toggleModalVisible: () => dispatch(toggleModalVisible("partsOrder")),
|
toggleModalVisible: () => dispatch(toggleModalVisible("partsOrder")),
|
||||||
setBillEnterContext: (context) =>
|
setBillEnterContext: (context) =>
|
||||||
dispatch(setModalContext({ context: context, modal: "billEnter" })),
|
dispatch(setModalContext({ context: context, modal: "billEnter" })),
|
||||||
insertAuditTrail: ({ jobid, operation }) =>
|
insertAuditTrail: ({ jobid, operation, type }) =>
|
||||||
dispatch(insertAuditTrail({ jobid, operation })),
|
dispatch(insertAuditTrail({ jobid, operation, type })),
|
||||||
});
|
});
|
||||||
|
|
||||||
export function PartsOrderModalContainer({
|
export function PartsOrderModalContainer({
|
||||||
@@ -142,6 +142,7 @@ export function PartsOrderModalContainer({
|
|||||||
: AuditTrailMapping.jobspartsorder(
|
: AuditTrailMapping.jobspartsorder(
|
||||||
insertResult.data.insert_parts_orders.returning[0].order_number
|
insertResult.data.insert_parts_orders.returning[0].order_number
|
||||||
),
|
),
|
||||||
|
type: isReturn ? "jobspartsreturn" : "jobspartsorder",
|
||||||
});
|
});
|
||||||
|
|
||||||
const jobLinesResult = await updateJobLines({
|
const jobLinesResult = await updateJobLines({
|
||||||
|
|||||||
@@ -95,15 +95,13 @@ export function PartsQueueListComponent({ bodyshop }) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleOnRowClick = (record) => {
|
const handleOnRowClick = (record) => {
|
||||||
if (record) {
|
if (record?.id) {
|
||||||
if (record.id) {
|
history.replace({
|
||||||
history.push({
|
search: queryString.stringify({
|
||||||
search: queryString.stringify({
|
...searchParams,
|
||||||
...searchParams,
|
selected: record.id,
|
||||||
selected: record.id,
|
}),
|
||||||
}),
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -350,6 +348,13 @@ export function PartsQueueListComponent({ bodyshop }) {
|
|||||||
selectedRowKeys: [selected],
|
selectedRowKeys: [selected],
|
||||||
type: "radio",
|
type: "radio",
|
||||||
}}
|
}}
|
||||||
|
onRow={(record, rowIndex) => {
|
||||||
|
return {
|
||||||
|
onClick: (event) => {
|
||||||
|
handleOnRowClick(record);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -59,8 +59,8 @@ const PaymentExpandedRowComponent = ({ record, bodyshop }) => {
|
|||||||
await insertPayment({
|
await insertPayment({
|
||||||
variables: {
|
variables: {
|
||||||
paymentInput: {
|
paymentInput: {
|
||||||
amount: -refund_response.data.amount,
|
amount: -refund_response?.data?.amount,
|
||||||
transactionid: payment_response.response.receiptelements.transid,
|
transactionid: payment_response?.response?.receiptelements?.transid,
|
||||||
payer: record.payer,
|
payer: record.payer,
|
||||||
type: "Refund",
|
type: "Refund",
|
||||||
jobid: payment_response.jobid,
|
jobid: payment_response.jobid,
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
import { Button, Form, Input, PageHeader, Space } from "antd";
|
import { Button, Form, Input, PageHeader, Space } from "antd";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import {
|
||||||
|
selectAuthLevel,
|
||||||
|
selectBodyshop,
|
||||||
|
} from "../../redux/user/user.selectors";
|
||||||
import FormFieldsChanged from "../form-fields-changed-alert/form-fields-changed-alert.component";
|
import FormFieldsChanged from "../form-fields-changed-alert/form-fields-changed-alert.component";
|
||||||
import FormItemEmail from "../form-items-formatted/email-form-item.component";
|
import FormItemEmail from "../form-items-formatted/email-form-item.component";
|
||||||
import PhoneFormItem, {
|
import PhoneFormItem, {
|
||||||
@@ -8,12 +14,6 @@ import PhoneFormItem, {
|
|||||||
} from "../form-items-formatted/phone-form-item.component";
|
} from "../form-items-formatted/phone-form-item.component";
|
||||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||||
import { HasRbacAccess } from "../rbac-wrapper/rbac-wrapper.component";
|
import { HasRbacAccess } from "../rbac-wrapper/rbac-wrapper.component";
|
||||||
import { connect } from "react-redux";
|
|
||||||
import { createStructuredSelector } from "reselect";
|
|
||||||
import {
|
|
||||||
selectAuthLevel,
|
|
||||||
selectBodyshop,
|
|
||||||
} from "../../redux/user/user.selectors";
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
authLevel: selectAuthLevel,
|
authLevel: selectAuthLevel,
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
@@ -46,13 +46,19 @@ export function PhonebookFormComponent({
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<PageHeader
|
<PageHeader
|
||||||
title={`${form.getFieldValue("firstname") || ""} ${
|
title={
|
||||||
form.getFieldValue("lastname") || ""
|
<Form.Item shouldUpdate>
|
||||||
}${
|
{() =>
|
||||||
form.getFieldValue("company")
|
`${form.getFieldValue("firstname") || ""} ${
|
||||||
? ` - ${form.getFieldValue("company")}`
|
form.getFieldValue("lastname") || ""
|
||||||
: ""
|
}${
|
||||||
}`}
|
form.getFieldValue("company")
|
||||||
|
? ` - ${form.getFieldValue("company")}`
|
||||||
|
: ""
|
||||||
|
}`
|
||||||
|
}
|
||||||
|
</Form.Item>
|
||||||
|
}
|
||||||
extra={
|
extra={
|
||||||
<Space>
|
<Space>
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -29,8 +29,8 @@ const mapStateToProps = createStructuredSelector({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
insertAuditTrail: ({ jobid, operation }) =>
|
insertAuditTrail: ({ jobid, operation, type }) =>
|
||||||
dispatch(insertAuditTrail({ jobid, operation })),
|
dispatch(insertAuditTrail({ jobid, operation, type })),
|
||||||
});
|
});
|
||||||
|
|
||||||
export function ProductionBoardKanbanComponent({
|
export function ProductionBoardKanbanComponent({
|
||||||
@@ -133,6 +133,7 @@ export function ProductionBoardKanbanComponent({
|
|||||||
insertAuditTrail({
|
insertAuditTrail({
|
||||||
jobid: card.id,
|
jobid: card.id,
|
||||||
operation: AuditTrailMapping.jobstatuschange(destination.toColumnId),
|
operation: AuditTrailMapping.jobstatuschange(destination.toColumnId),
|
||||||
|
type: "jobstatuschange",
|
||||||
});
|
});
|
||||||
|
|
||||||
if (update.errors) {
|
if (update.errors) {
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
|||||||
const mapStateToProps = createStructuredSelector({});
|
const mapStateToProps = createStructuredSelector({});
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
insertAuditTrail: ({ jobid, operation }) =>
|
insertAuditTrail: ({ jobid, operation, type }) =>
|
||||||
dispatch(insertAuditTrail({ jobid, operation })),
|
dispatch(insertAuditTrail({ jobid, operation, type })),
|
||||||
});
|
});
|
||||||
|
|
||||||
export function ProductionListColumnAlert({ record, insertAuditTrail }) {
|
export function ProductionListColumnAlert({ record, insertAuditTrail }) {
|
||||||
@@ -46,6 +46,7 @@ export function ProductionListColumnAlert({ record, insertAuditTrail }) {
|
|||||||
? !record.production_vars.alert
|
? !record.production_vars.alert
|
||||||
: true
|
: true
|
||||||
),
|
),
|
||||||
|
type: "alertToggle",
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
if (record.refetch) record.refetch();
|
if (record.refetch) record.refetch();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { BranchesOutlined, PauseCircleOutlined } from "@ant-design/icons";
|
import { BranchesOutlined, PauseCircleOutlined } from "@ant-design/icons";
|
||||||
import { Space, Tooltip } from "antd";
|
import { Checkbox, Space, Tooltip } from "antd";
|
||||||
import i18n from "i18next";
|
import i18n from "i18next";
|
||||||
import moment from "moment";
|
|
||||||
import { Link } from "react-router-dom";
|
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";
|
||||||
@@ -10,7 +9,9 @@ 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";
|
||||||
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
import OwnerNameDisplay, {
|
||||||
|
OwnerNameDisplayFunction,
|
||||||
|
} from "../owner-name-display/owner-name-display.component";
|
||||||
import ProductionSubletsManageComponent from "../production-sublets-manage/production-sublets-manage.component";
|
import ProductionSubletsManageComponent from "../production-sublets-manage/production-sublets-manage.component";
|
||||||
import ProductionListColumnAlert from "./production-list-columns.alert.component";
|
import ProductionListColumnAlert from "./production-list-columns.alert.component";
|
||||||
import ProductionListColumnBodyPriority from "./production-list-columns.bodypriority.component";
|
import ProductionListColumnBodyPriority from "./production-list-columns.bodypriority.component";
|
||||||
@@ -84,7 +85,8 @@ const r = ({ technician, state, activeStatuses, data, bodyshop }) => {
|
|||||||
<OwnerNameDisplay ownerObject={record} />
|
<OwnerNameDisplay ownerObject={record} />
|
||||||
</Link>
|
</Link>
|
||||||
),
|
),
|
||||||
sorter: (a, b) => alphaSort(a.ownr_ln, b.ownr_ln),
|
sorter: (a, b) =>
|
||||||
|
alphaSort(OwnerNameDisplayFunction(a), OwnerNameDisplayFunction(b)),
|
||||||
sortOrder:
|
sortOrder:
|
||||||
state.sortedInfo.columnKey === "ownr" && state.sortedInfo.order,
|
state.sortedInfo.columnKey === "ownr" && state.sortedInfo.order,
|
||||||
},
|
},
|
||||||
@@ -95,8 +97,10 @@ const r = ({ technician, state, activeStatuses, data, bodyshop }) => {
|
|||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
sorter: (a, b) =>
|
sorter: (a, b) =>
|
||||||
alphaSort(
|
alphaSort(
|
||||||
a.v_make_desc + a.v_model_desc,
|
`${a.v_model_yr || ""} ${a.v_make_desc || ""} ${
|
||||||
b.v_make_desc + b.v_model_desc
|
a.v_model_desc || ""
|
||||||
|
}`,
|
||||||
|
`${b.v_model_yr || ""} ${b.v_make_desc || ""} ${b.v_model_desc || ""}`
|
||||||
),
|
),
|
||||||
sortOrder:
|
sortOrder:
|
||||||
state.sortedInfo.columnKey === "vehicle" && state.sortedInfo.order,
|
state.sortedInfo.columnKey === "vehicle" && state.sortedInfo.order,
|
||||||
@@ -185,17 +189,12 @@ const r = ({ technician, state, activeStatuses, data, bodyshop }) => {
|
|||||||
state.sortedInfo.columnKey === "date_next_contact" &&
|
state.sortedInfo.columnKey === "date_next_contact" &&
|
||||||
state.sortedInfo.order,
|
state.sortedInfo.order,
|
||||||
render: (text, record) => (
|
render: (text, record) => (
|
||||||
<span
|
<ProductionListDate
|
||||||
style={{
|
record={record}
|
||||||
color:
|
field="date_next_contact"
|
||||||
record.date_next_contact &&
|
pastIndicator
|
||||||
moment(record.date_next_contact).isBefore(moment())
|
time
|
||||||
? "red"
|
/>
|
||||||
: "",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ProductionListDate record={record} field="date_next_contact" time />
|
|
||||||
</span>
|
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -291,6 +290,20 @@ const r = ({ technician, state, activeStatuses, data, bodyshop }) => {
|
|||||||
dataIndex: "special_coverage_policy",
|
dataIndex: "special_coverage_policy",
|
||||||
key: "special_coverage_policy",
|
key: "special_coverage_policy",
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
|
sorter: (a, b) =>
|
||||||
|
Number(a.special_coverage_policy) - Number(b.special_coverage_policy),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "special_coverage_policy" &&
|
||||||
|
state.sortedInfo.order,
|
||||||
|
filters: [
|
||||||
|
{ text: "True", value: true },
|
||||||
|
{ text: "False", value: false },
|
||||||
|
],
|
||||||
|
onFilter: (value, record) =>
|
||||||
|
value.includes(record.special_coverage_policy),
|
||||||
|
render: (text, record) => (
|
||||||
|
<Checkbox checked={record.special_coverage_policy} />
|
||||||
|
),
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -302,6 +315,16 @@ const r = ({ technician, state, activeStatuses, data, bodyshop }) => {
|
|||||||
sortOrder:
|
sortOrder:
|
||||||
state.sortedInfo.columnKey === "alt_transport" &&
|
state.sortedInfo.columnKey === "alt_transport" &&
|
||||||
state.sortedInfo.order,
|
state.sortedInfo.order,
|
||||||
|
filters:
|
||||||
|
(bodyshop &&
|
||||||
|
bodyshop.appt_alt_transport.map((s) => {
|
||||||
|
return {
|
||||||
|
text: s,
|
||||||
|
value: [s],
|
||||||
|
};
|
||||||
|
})) ||
|
||||||
|
[],
|
||||||
|
onFilter: (value, record) => value.includes(record.alt_transport),
|
||||||
render: (text, record) => (
|
render: (text, record) => (
|
||||||
<div>
|
<div>
|
||||||
{record.alt_transport}
|
{record.alt_transport}
|
||||||
@@ -382,7 +405,11 @@ const r = ({ technician, state, activeStatuses, data, bodyshop }) => {
|
|||||||
title: i18n.t("production.labels.alert"),
|
title: i18n.t("production.labels.alert"),
|
||||||
dataIndex: "alert",
|
dataIndex: "alert",
|
||||||
key: "alert",
|
key: "alert",
|
||||||
|
sorter: (a, b) =>
|
||||||
|
Number(a.production_vars?.alert || false) -
|
||||||
|
Number(b.production_vars?.alert || false),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "alert" && state.sortedInfo.order,
|
||||||
render: (text, record) => <ProductionListColumnAlert record={record} />,
|
render: (text, record) => <ProductionListColumnAlert record={record} />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3,12 +3,12 @@ import { useMutation } from "@apollo/client";
|
|||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
Col,
|
Col,
|
||||||
notification,
|
|
||||||
Popover,
|
Popover,
|
||||||
Row,
|
Row,
|
||||||
Select,
|
Select,
|
||||||
Space,
|
Space,
|
||||||
Spin,
|
Spin,
|
||||||
|
notification,
|
||||||
} from "antd";
|
} from "antd";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
@@ -25,8 +25,8 @@ const mapStateToProps = createStructuredSelector({
|
|||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
insertAuditTrail: ({ jobid, operation }) =>
|
insertAuditTrail: ({ jobid, operation, type }) =>
|
||||||
dispatch(insertAuditTrail({ jobid, operation })),
|
dispatch(insertAuditTrail({ jobid, operation, type })),
|
||||||
});
|
});
|
||||||
|
|
||||||
export function ProductionListEmpAssignment({
|
export function ProductionListEmpAssignment({
|
||||||
@@ -55,6 +55,7 @@ export function ProductionListEmpAssignment({
|
|||||||
insertAuditTrail({
|
insertAuditTrail({
|
||||||
jobid: record.id,
|
jobid: record.id,
|
||||||
operation: AuditTrailMapping.jobassignmentchange(empAssignment, name),
|
operation: AuditTrailMapping.jobassignmentchange(empAssignment, name),
|
||||||
|
type: "jobassignmentchange",
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!!result.errors) {
|
if (!!result.errors) {
|
||||||
@@ -80,6 +81,7 @@ export function ProductionListEmpAssignment({
|
|||||||
insertAuditTrail({
|
insertAuditTrail({
|
||||||
jobid: record.id,
|
jobid: record.id,
|
||||||
operation: AuditTrailMapping.jobassignmentremoved(empAssignment),
|
operation: AuditTrailMapping.jobassignmentremoved(empAssignment),
|
||||||
|
type: "jobassignmentremoved",
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!!result.errors) {
|
if (!!result.errors) {
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ const mapStateToProps = createStructuredSelector({
|
|||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
insertAuditTrail: ({ jobid, operation }) =>
|
insertAuditTrail: ({ jobid, operation, type }) =>
|
||||||
dispatch(insertAuditTrail({ jobid, operation })),
|
dispatch(insertAuditTrail({ jobid, operation, type })),
|
||||||
});
|
});
|
||||||
export function ProductionListColumnCategory({ record, bodyshop }) {
|
export function ProductionListColumnCategory({ record, bodyshop }) {
|
||||||
const [updateJob] = useMutation(UPDATE_JOB);
|
const [updateJob] = useMutation(UPDATE_JOB);
|
||||||
|
|||||||
@@ -5,16 +5,16 @@ import { connect } from "react-redux";
|
|||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||||
import { UPDATE_JOB } from "../../graphql/jobs.queries";
|
import { UPDATE_JOB } from "../../graphql/jobs.queries";
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
|
||||||
import { insertAuditTrail } from "../../redux/application/application.actions";
|
import { insertAuditTrail } from "../../redux/application/application.actions";
|
||||||
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
insertAuditTrail: ({ jobid, operation }) =>
|
insertAuditTrail: ({ jobid, operation, type }) =>
|
||||||
dispatch(insertAuditTrail({ jobid, operation })),
|
dispatch(insertAuditTrail({ jobid, operation, type })),
|
||||||
});
|
});
|
||||||
export function ProductionListColumnStatus({
|
export function ProductionListColumnStatus({
|
||||||
record,
|
record,
|
||||||
@@ -40,6 +40,7 @@ export function ProductionListColumnStatus({
|
|||||||
insertAuditTrail({
|
insertAuditTrail({
|
||||||
jobid: record.id,
|
jobid: record.id,
|
||||||
operation: AuditTrailMapping.jobstatuschange(key),
|
operation: AuditTrailMapping.jobstatuschange(key),
|
||||||
|
type: "jobstatuschange",
|
||||||
});
|
});
|
||||||
|
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {useTranslation} from "react-i18next";
|
|||||||
import {getOrderOperatorsByType, getWhereOperatorsByType} from "../../utils/graphQLmodifier";
|
import {getOrderOperatorsByType, getWhereOperatorsByType} from "../../utils/graphQLmodifier";
|
||||||
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
|
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
|
||||||
import {generateInternalReflections} from "./report-center-modal-utils";
|
import {generateInternalReflections} from "./report-center-modal-utils";
|
||||||
|
import {FormDatePicker} from "../form-date-picker/form-date-picker.component.jsx";
|
||||||
|
|
||||||
export default function ReportCenterModalFiltersSortersComponent({form, bodyshop}) {
|
export default function ReportCenterModalFiltersSortersComponent({form, bodyshop}) {
|
||||||
return (
|
return (
|
||||||
@@ -33,7 +33,7 @@ function FiltersSection({filters, form, bodyshop}) {
|
|||||||
return (
|
return (
|
||||||
<Card type='inner' title={t('reportcenter.labels.advanced_filters_filters')} style={{marginTop: '10px'}}>
|
<Card type='inner' title={t('reportcenter.labels.advanced_filters_filters')} style={{marginTop: '10px'}}>
|
||||||
<Form.List name={["filters"]}>
|
<Form.List name={["filters"]}>
|
||||||
{(fields, {add, remove, move}) => {
|
{(fields, {add, remove}) => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{fields.map((field, index) => (
|
{fields.map((field, index) => (
|
||||||
@@ -70,7 +70,9 @@ function FiltersSection({filters, form, bodyshop}) {
|
|||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={6}>
|
<Col span={6}>
|
||||||
<Form.Item dependencies={[['filters', field.name, "field"]]}>
|
<Form.Item
|
||||||
|
dependencies={[['filters', field.name, "field"],['filters', field.name, "value"]]}
|
||||||
|
>
|
||||||
{
|
{
|
||||||
() => {
|
() => {
|
||||||
const name = form.getFieldValue(['filters', field.name, "field"]);
|
const name = form.getFieldValue(['filters', field.name, "field"]);
|
||||||
@@ -80,7 +82,6 @@ function FiltersSection({filters, form, bodyshop}) {
|
|||||||
key={`${index}operator`}
|
key={`${index}operator`}
|
||||||
label={t('reportcenter.labels.advanced_filters_filter_operator')}
|
label={t('reportcenter.labels.advanced_filters_filter_operator')}
|
||||||
name={[field.name, "operator"]}
|
name={[field.name, "operator"]}
|
||||||
dependencies={[]}
|
|
||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
@@ -90,19 +91,32 @@ function FiltersSection({filters, form, bodyshop}) {
|
|||||||
>
|
>
|
||||||
<Select
|
<Select
|
||||||
getPopupContainer={trigger => trigger.parentNode}
|
getPopupContainer={trigger => trigger.parentNode}
|
||||||
options={getWhereOperatorsByType(type)}/>
|
options={ getWhereOperatorsByType(type)}
|
||||||
|
onChange={() => {
|
||||||
|
// Clear related Fields
|
||||||
|
|
||||||
|
form.setFieldValue(['filters', field.name, 'value'], undefined);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={6}>
|
<Col span={6}>
|
||||||
<Form.Item dependencies={[['filters', field.name, "field"]]}>
|
<Form.Item dependencies={[
|
||||||
|
['filters', field.name, "field"],
|
||||||
|
['filters', field.name, "operator"]
|
||||||
|
]}
|
||||||
|
>
|
||||||
{
|
{
|
||||||
() => {
|
() => {
|
||||||
|
// Because it looks cleaner than inlining.
|
||||||
const name = form.getFieldValue(['filters', field.name, "field"]);
|
const name = form.getFieldValue(['filters', field.name, "field"]);
|
||||||
const type = filters.find(f => f.name === name)?.type;
|
const type = filters.find(f => f.name === name)?.type;
|
||||||
const reflector = filters.find(f => f.name === name)?.reflector;
|
const reflector = filters.find(f => f.name === name)?.reflector;
|
||||||
|
const operator = form.getFieldValue(['filters', field.name, "operator"]);
|
||||||
|
const operatorType = operator ? getWhereOperatorsByType(type).find((o) => o.value === operator)?.type : null;
|
||||||
|
|
||||||
return <Form.Item
|
return <Form.Item
|
||||||
key={`${index}value`}
|
key={`${index}value`}
|
||||||
@@ -128,14 +142,29 @@ function FiltersSection({filters, form, bodyshop}) {
|
|||||||
return generateInternalReflections({
|
return generateInternalReflections({
|
||||||
bodyshop,
|
bodyshop,
|
||||||
upperPath,
|
upperPath,
|
||||||
finalPath
|
finalPath,
|
||||||
|
t
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const reflections = reflector ? generateReflections(reflector) : [];
|
const reflections = reflector ? generateReflections(reflector) : [];
|
||||||
const fieldPath = [[field.name, "value"]];
|
const fieldPath = [[field.name, "value"]];
|
||||||
|
// We have reflections so we will use a select box
|
||||||
if (reflections.length > 0) {
|
if (reflections.length > 0) {
|
||||||
|
// We have reflections and the operator type is array, so we will use a select box with multiple options
|
||||||
|
if (operatorType === "array") {
|
||||||
|
return (
|
||||||
|
<Select
|
||||||
|
disabled={!operator}
|
||||||
|
mode="multiple"
|
||||||
|
options={reflections}
|
||||||
|
getPopupContainer={trigger => trigger.parentNode}
|
||||||
|
onChange={(value) => {
|
||||||
|
form.setFieldValue(fieldPath, value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<Select
|
<Select
|
||||||
options={reflections}
|
options={reflections}
|
||||||
@@ -147,16 +176,50 @@ function FiltersSection({filters, form, bodyshop}) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We have a type of number, so we will use a number input
|
||||||
if (type === "number") {
|
if (type === "number") {
|
||||||
return (
|
return (
|
||||||
<InputNumber
|
<InputNumber
|
||||||
|
disabled={!operator}
|
||||||
onChange={(value) => form.setFieldValue(fieldPath, value)}/>
|
onChange={(value) => form.setFieldValue(fieldPath, value)}/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We have a type of date, so we will use a date picker
|
||||||
|
if (type === "date") {
|
||||||
|
return (
|
||||||
|
<FormDatePicker
|
||||||
|
disabled={!operator}
|
||||||
|
onChange={(date) => form.setFieldValue(fieldPath, date)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// we have a type of boolean, so we will use a select box with a true or false option.
|
||||||
|
if (type === "boolean" || type === "bool") {
|
||||||
|
return (
|
||||||
|
<Select
|
||||||
|
disabled={!operator}
|
||||||
|
getPopupContainer={trigger => trigger.parentNode}
|
||||||
|
options={[
|
||||||
|
{
|
||||||
|
label: t('reportcenter.labels.advanced_filters_true'),
|
||||||
|
value: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('reportcenter.labels.advanced_filters_false'),
|
||||||
|
value: false
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
onChange={(value) => form.setFieldValue(fieldPath, value)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<Input
|
<Input
|
||||||
onChange={(e) => form.setFieldValue(fieldPath, e.target.value)}/>
|
disabled={!operator}
|
||||||
|
onChange={(e) => form.setFieldValue(fieldPath, e.target.value)}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
})()
|
})()
|
||||||
}
|
}
|
||||||
@@ -203,12 +266,12 @@ function FiltersSection({filters, form, bodyshop}) {
|
|||||||
* @returns {JSX.Element}
|
* @returns {JSX.Element}
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
function SortersSection({sorters, form}) {
|
function SortersSection({sorters}) {
|
||||||
const {t} = useTranslation();
|
const {t} = useTranslation();
|
||||||
return (
|
return (
|
||||||
<Card type='inner' title={t('reportcenter.labels.advanced_filters_sorters')} style={{marginTop: '10px'}}>
|
<Card type='inner' title={t('reportcenter.labels.advanced_filters_sorters')} style={{marginTop: '10px'}}>
|
||||||
<Form.List name={["sorters"]}>
|
<Form.List name={["sorters"]}>
|
||||||
{(fields, {add, remove, move}) => {
|
{(fields, {add, remove}) => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
Sorters
|
Sorters
|
||||||
|
|||||||
@@ -8,6 +8,20 @@ import {uniqBy} from "lodash";
|
|||||||
*/
|
*/
|
||||||
const getValueFromPath = (obj, path) => path.split('.').reduce((prev, curr) => prev?.[curr], obj);
|
const getValueFromPath = (obj, path) => path.split('.').reduce((prev, curr) => prev?.[curr], obj);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate options from array
|
||||||
|
* @param bodyshop
|
||||||
|
* @param path
|
||||||
|
* @returns {unknown[]}
|
||||||
|
*/
|
||||||
|
const generateOptionsFromArray = (bodyshop, path) => {
|
||||||
|
const options = getValueFromPath(bodyshop, path);
|
||||||
|
return uniqBy(options.map((value) => ({
|
||||||
|
label: value,
|
||||||
|
value: value,
|
||||||
|
})), 'value');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Valid internal reflections
|
* Valid internal reflections
|
||||||
* Note: This is intended for future functionality
|
* Note: This is intended for future functionality
|
||||||
@@ -42,19 +56,44 @@ const generateOptionsFromObject = (bodyshop, path, labelPath, valuePath) => {
|
|||||||
* Generate special reflections
|
* Generate special reflections
|
||||||
* @param bodyshop
|
* @param bodyshop
|
||||||
* @param finalPath
|
* @param finalPath
|
||||||
|
* @param t - i18n
|
||||||
* @returns {{label: *, value: *}[]|{label: *, value: *}[]|{label: string, value: *}[]|*[]}
|
* @returns {{label: *, value: *}[]|{label: *, value: *}[]|{label: string, value: *}[]|*[]}
|
||||||
*/
|
*/
|
||||||
const generateSpecialReflections = (bodyshop, finalPath) => {
|
const generateSpecialReflections = (bodyshop, finalPath, t) => {
|
||||||
switch (finalPath) {
|
switch (finalPath) {
|
||||||
|
case 'payment_payers':
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
label: t("payments.labels.customer"),
|
||||||
|
value: t("payments.labels.customer"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t("payments.labels.insurance"),
|
||||||
|
value: t("payments.labels.insurance"),
|
||||||
|
},
|
||||||
|
// This is a weird one supposedly only used by one shop and could potentially be
|
||||||
|
// placed behind a SplitSDK
|
||||||
|
{
|
||||||
|
label: t("payments.labels.external"),
|
||||||
|
value: t("payments.labels.external"),
|
||||||
|
}
|
||||||
|
];
|
||||||
|
case 'payment_types':
|
||||||
|
return generateOptionsFromArray(bodyshop, 'md_payment_types');
|
||||||
|
case 'alt_transports':
|
||||||
|
return generateOptionsFromArray(bodyshop, 'appt_alt_transport');
|
||||||
|
case 'lost_sale_reasons':
|
||||||
|
return generateOptionsFromArray(bodyshop, 'md_lost_sale_reasons');
|
||||||
|
// Special case because Referral Sources is an Array, not an Object.
|
||||||
|
case 'referral_source':
|
||||||
|
return generateOptionsFromArray(bodyshop, 'md_referral_sources');
|
||||||
|
case 'class':
|
||||||
|
return generateOptionsFromArray(bodyshop, 'md_classes');
|
||||||
case 'cost_centers':
|
case 'cost_centers':
|
||||||
return generateOptionsFromObject(bodyshop, 'md_responsibility_centers.costs', 'name', 'name');
|
return generateOptionsFromObject(bodyshop, 'md_responsibility_centers.costs', 'name', 'name');
|
||||||
// Special case because Categories is an Array, not an Object.
|
// Special case because Categories is an Array, not an Object.
|
||||||
case 'categories':
|
case 'categories':
|
||||||
const catOptions = getValueFromPath(bodyshop, 'md_categories');
|
return generateOptionsFromArray(bodyshop, 'md_categories');
|
||||||
return uniqBy(catOptions.map((value) => ({
|
|
||||||
label: value,
|
|
||||||
value: value,
|
|
||||||
})), 'value');
|
|
||||||
case 'insurance_companies':
|
case 'insurance_companies':
|
||||||
return generateOptionsFromObject(bodyshop, 'md_ins_cos', 'name', 'name');
|
return generateOptionsFromObject(bodyshop, 'md_ins_cos', 'name', 'name');
|
||||||
case 'employee_teams':
|
case 'employee_teams':
|
||||||
@@ -105,12 +144,13 @@ const generateBodyshopReflections = (bodyshop, finalPath) => {
|
|||||||
* @param bodyshop
|
* @param bodyshop
|
||||||
* @param upperPath
|
* @param upperPath
|
||||||
* @param finalPath
|
* @param finalPath
|
||||||
|
* @param t - i18n
|
||||||
* @returns {{label: *, value: *}[]|[]|{label: *, value: *}[]|{label: string, value: *}[]|{label: *, value: *}[]|*[]}
|
* @returns {{label: *, value: *}[]|[]|{label: *, value: *}[]|{label: string, value: *}[]|{label: *, value: *}[]|*[]}
|
||||||
*/
|
*/
|
||||||
const generateInternalReflections = ({bodyshop, upperPath, finalPath}) => {
|
const generateInternalReflections = ({bodyshop, upperPath, finalPath, t}) => {
|
||||||
switch (upperPath) {
|
switch (upperPath) {
|
||||||
case 'special':
|
case 'special':
|
||||||
return generateSpecialReflections(bodyshop, finalPath);
|
return generateSpecialReflections(bodyshop, finalPath, t);
|
||||||
case 'bodyshop':
|
case 'bodyshop':
|
||||||
return generateBodyshopReflections(bodyshop, finalPath);
|
return generateBodyshopReflections(bodyshop, finalPath);
|
||||||
default:
|
default:
|
||||||
@@ -118,4 +158,4 @@ const generateInternalReflections = ({bodyshop, upperPath, finalPath}) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export {generateInternalReflections,}
|
export {generateInternalReflections}
|
||||||
@@ -34,8 +34,8 @@ const mapStateToProps = createStructuredSelector({
|
|||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
toggleModalVisible: () => dispatch(toggleModalVisible("schedule")),
|
toggleModalVisible: () => dispatch(toggleModalVisible("schedule")),
|
||||||
setEmailOptions: (e) => dispatch(setEmailOptions(e)),
|
setEmailOptions: (e) => dispatch(setEmailOptions(e)),
|
||||||
insertAuditTrail: ({ jobid, operation }) =>
|
insertAuditTrail: ({ jobid, operation, type }) =>
|
||||||
dispatch(insertAuditTrail({ jobid, operation })),
|
dispatch(insertAuditTrail({ jobid, operation, type })),
|
||||||
});
|
});
|
||||||
|
|
||||||
export function ScheduleJobModalContainer({
|
export function ScheduleJobModalContainer({
|
||||||
@@ -146,6 +146,7 @@ export function ScheduleJobModalContainer({
|
|||||||
operation: AuditTrailMapping.appointmentinsert(
|
operation: AuditTrailMapping.appointmentinsert(
|
||||||
DateTimeFormat(values.start)
|
DateTimeFormat(values.start)
|
||||||
),
|
),
|
||||||
|
type: "appointmentinsert",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ export function ScoreboardDayStats({ bodyshop, date, entries }) {
|
|||||||
return acc + value.bodyhrs;
|
return acc + value.bodyhrs;
|
||||||
}, 0);
|
}, 0);
|
||||||
|
|
||||||
|
const numJobs = entries.length;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
title={moment(date).format("D - ddd")}
|
title={moment(date).format("D - ddd")}
|
||||||
@@ -33,17 +35,18 @@ export function ScoreboardDayStats({ bodyshop, date, entries }) {
|
|||||||
>
|
>
|
||||||
<Statistic
|
<Statistic
|
||||||
valueStyle={{ color: dailyBodyTarget > bodyHrs ? "red" : "green" }}
|
valueStyle={{ color: dailyBodyTarget > bodyHrs ? "red" : "green" }}
|
||||||
label="B"
|
label="Body"
|
||||||
value={bodyHrs.toFixed(1)}
|
value={bodyHrs.toFixed(1)}
|
||||||
/>
|
/>
|
||||||
<Statistic
|
<Statistic
|
||||||
valueStyle={{ color: dailyPaintTarget > paintHrs ? "red" : "green" }}
|
valueStyle={{ color: dailyPaintTarget > paintHrs ? "red" : "green" }}
|
||||||
label="P"
|
label="Refinish"
|
||||||
value={paintHrs.toFixed(1)}
|
value={paintHrs.toFixed(1)}
|
||||||
/>
|
/>
|
||||||
<Divider style={{ margin: 0 }} />
|
<Divider style={{ margin: 0 }} />
|
||||||
|
<Statistic label="Total" value={(bodyHrs + paintHrs).toFixed(1)} />
|
||||||
<Statistic value={(bodyHrs + paintHrs).toFixed(1)} />
|
<Divider style={{ margin: 0 }} />
|
||||||
|
<Statistic label="Jobs" value={numJobs} />
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,10 +29,13 @@ export function ScoreboardTargetsTable({ bodyshop, scoreBoardlist }) {
|
|||||||
let ret = {
|
let ret = {
|
||||||
todayBody: 0,
|
todayBody: 0,
|
||||||
todayPaint: 0,
|
todayPaint: 0,
|
||||||
|
todayJobs: 0,
|
||||||
weeklyPaint: 0,
|
weeklyPaint: 0,
|
||||||
|
weeklyJobs: 0,
|
||||||
weeklyBody: 0,
|
weeklyBody: 0,
|
||||||
toDateBody: 0,
|
toDateBody: 0,
|
||||||
toDatePaint: 0,
|
toDatePaint: 0,
|
||||||
|
toDateJobs: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
const today = moment();
|
const today = moment();
|
||||||
@@ -40,6 +43,7 @@ export function ScoreboardTargetsTable({ bodyshop, scoreBoardlist }) {
|
|||||||
dateHash[today.format("YYYY-MM-DD")].forEach((d) => {
|
dateHash[today.format("YYYY-MM-DD")].forEach((d) => {
|
||||||
ret.todayBody = ret.todayBody + d.bodyhrs;
|
ret.todayBody = ret.todayBody + d.bodyhrs;
|
||||||
ret.todayPaint = ret.todayPaint + d.painthrs;
|
ret.todayPaint = ret.todayPaint + d.painthrs;
|
||||||
|
ret.todayJobs++;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,6 +53,7 @@ export function ScoreboardTargetsTable({ bodyshop, scoreBoardlist }) {
|
|||||||
dateHash[StartOfWeek.format("YYYY-MM-DD")].forEach((d) => {
|
dateHash[StartOfWeek.format("YYYY-MM-DD")].forEach((d) => {
|
||||||
ret.weeklyBody = ret.weeklyBody + d.bodyhrs;
|
ret.weeklyBody = ret.weeklyBody + d.bodyhrs;
|
||||||
ret.weeklyPaint = ret.weeklyPaint + d.painthrs;
|
ret.weeklyPaint = ret.weeklyPaint + d.painthrs;
|
||||||
|
ret.weeklyJobs++;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
StartOfWeek = StartOfWeek.add(1, "day");
|
StartOfWeek = StartOfWeek.add(1, "day");
|
||||||
@@ -60,6 +65,7 @@ export function ScoreboardTargetsTable({ bodyshop, scoreBoardlist }) {
|
|||||||
dateHash[startOfMonth.format("YYYY-MM-DD")].forEach((d) => {
|
dateHash[startOfMonth.format("YYYY-MM-DD")].forEach((d) => {
|
||||||
ret.toDateBody = ret.toDateBody + d.bodyhrs;
|
ret.toDateBody = ret.toDateBody + d.bodyhrs;
|
||||||
ret.toDatePaint = ret.toDatePaint + d.painthrs;
|
ret.toDatePaint = ret.toDatePaint + d.painthrs;
|
||||||
|
ret.toDateJobs++;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
startOfMonth = startOfMonth.add(1, "day");
|
startOfMonth = startOfMonth.add(1, "day");
|
||||||
@@ -87,7 +93,7 @@ export function ScoreboardTargetsTable({ bodyshop, scoreBoardlist }) {
|
|||||||
<Statistic
|
<Statistic
|
||||||
title={t("scoreboard.labels.dailytarget")}
|
title={t("scoreboard.labels.dailytarget")}
|
||||||
value={bodyshop.scoreboard_target.dailyBodyTarget}
|
value={bodyshop.scoreboard_target.dailyBodyTarget}
|
||||||
prefix="B"
|
prefix={t("scoreboard.labels.bodyabbrev")}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
<Col {...statSpans}>
|
<Col {...statSpans}>
|
||||||
@@ -140,7 +146,7 @@ export function ScoreboardTargetsTable({ bodyshop, scoreBoardlist }) {
|
|||||||
<Col {...statSpans}>
|
<Col {...statSpans}>
|
||||||
<Statistic
|
<Statistic
|
||||||
value={bodyshop.scoreboard_target.dailyPaintTarget}
|
value={bodyshop.scoreboard_target.dailyPaintTarget}
|
||||||
prefix="P"
|
prefix={t("scoreboard.labels.refinishabbrev")}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
<Col {...statSpans}>
|
<Col {...statSpans}>
|
||||||
@@ -181,7 +187,12 @@ export function ScoreboardTargetsTable({ bodyshop, scoreBoardlist }) {
|
|||||||
<Divider style={{ margin: 5 }} />
|
<Divider style={{ margin: 5 }} />
|
||||||
</Row>
|
</Row>
|
||||||
<Row>
|
<Row>
|
||||||
<Col {...statSpans}></Col>
|
<Col {...statSpans}>
|
||||||
|
<Statistic
|
||||||
|
value={"\u00A0"}
|
||||||
|
prefix={t("scoreboard.labels.total")}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
<Col {...statSpans}>
|
<Col {...statSpans}>
|
||||||
<Statistic
|
<Statistic
|
||||||
value={(values.todayPaint + values.todayBody).toFixed(1)}
|
value={(values.todayPaint + values.todayBody).toFixed(1)}
|
||||||
@@ -240,6 +251,29 @@ export function ScoreboardTargetsTable({ bodyshop, scoreBoardlist }) {
|
|||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
<Row>
|
||||||
|
<Divider style={{ margin: 5 }} />
|
||||||
|
</Row>
|
||||||
|
<Row>
|
||||||
|
<Col {...statSpans}>
|
||||||
|
<Statistic
|
||||||
|
value={"\u00A0"}
|
||||||
|
prefix={t("scoreboard.labels.jobs")}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
<Col {...statSpans}>
|
||||||
|
<Statistic value={values.todayJobs} />
|
||||||
|
</Col>
|
||||||
|
<Col {...statSpans} />
|
||||||
|
<Col {...statSpans}>
|
||||||
|
<Statistic value={values.weeklyJobs} />
|
||||||
|
</Col>
|
||||||
|
<Col {...statSpans} />
|
||||||
|
<Col {...statSpans} />
|
||||||
|
<Col {...statSpans}>
|
||||||
|
<Statistic value={values.toDateJobs} />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
Input,
|
Input,
|
||||||
InputNumber,
|
InputNumber,
|
||||||
Select,
|
Select,
|
||||||
|
Space,
|
||||||
Switch,
|
Switch,
|
||||||
Typography,
|
Typography,
|
||||||
} from "antd";
|
} from "antd";
|
||||||
@@ -17,6 +18,7 @@ import { selectBodyshop } from "../../redux/user/user.selectors";
|
|||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||||
|
import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component";
|
||||||
|
|
||||||
const SelectorDiv = styled.div`
|
const SelectorDiv = styled.div`
|
||||||
.ant-form-item .ant-select {
|
.ant-form-item .ant-select {
|
||||||
@@ -191,7 +193,7 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
|
|||||||
</LayoutFormRow>
|
</LayoutFormRow>
|
||||||
<LayoutFormRow header={t("bodyshop.labels.dms.cdk.payers")}>
|
<LayoutFormRow header={t("bodyshop.labels.dms.cdk.payers")}>
|
||||||
<Form.List name={["cdk_configuration", "payers"]}>
|
<Form.List name={["cdk_configuration", "payers"]}>
|
||||||
{(fields, { add, remove }) => {
|
{(fields, { add, remove, move }) => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{fields.map((field, index) => (
|
{fields.map((field, index) => (
|
||||||
@@ -249,11 +251,18 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
|
|||||||
</Select>
|
</Select>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<DeleteFilled
|
<Space align="center">
|
||||||
onClick={() => {
|
<DeleteFilled
|
||||||
remove(field.name);
|
onClick={() => {
|
||||||
}}
|
remove(field.name);
|
||||||
/>
|
}}
|
||||||
|
/>
|
||||||
|
<FormListMoveArrows
|
||||||
|
move={move}
|
||||||
|
index={index}
|
||||||
|
total={fields.length}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
</LayoutFormRow>
|
</LayoutFormRow>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
))}
|
))}
|
||||||
@@ -345,7 +354,7 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
|
|||||||
id="costs"
|
id="costs"
|
||||||
>
|
>
|
||||||
<Form.List name={["md_responsibility_centers", "costs"]}>
|
<Form.List name={["md_responsibility_centers", "costs"]}>
|
||||||
{(fields, { add, remove }) => {
|
{(fields, { add, remove, move }) => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{fields.map((field, index) => (
|
{fields.map((field, index) => (
|
||||||
@@ -462,12 +471,18 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
|
|||||||
<Input onBlur={handleBlur} />
|
<Input onBlur={handleBlur} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
)}
|
)}
|
||||||
|
<Space align="center">
|
||||||
<DeleteFilled
|
<DeleteFilled
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
remove(field.name);
|
remove(field.name);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
<FormListMoveArrows
|
||||||
|
move={move}
|
||||||
|
index={index}
|
||||||
|
total={fields.length}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
</LayoutFormRow>
|
</LayoutFormRow>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
))}
|
))}
|
||||||
@@ -493,7 +508,7 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
|
|||||||
id="profits"
|
id="profits"
|
||||||
>
|
>
|
||||||
<Form.List name={["md_responsibility_centers", "profits"]}>
|
<Form.List name={["md_responsibility_centers", "profits"]}>
|
||||||
{(fields, { add, remove }) => {
|
{(fields, { add, remove, move }) => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{fields.map((field, index) => (
|
{fields.map((field, index) => (
|
||||||
@@ -595,11 +610,18 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
|
|||||||
<Input onBlur={handleBlur} />
|
<Input onBlur={handleBlur} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
)}
|
)}
|
||||||
<DeleteFilled
|
<Space align="center">
|
||||||
onClick={() => {
|
<DeleteFilled
|
||||||
remove(field.name);
|
onClick={() => {
|
||||||
}}
|
remove(field.name);
|
||||||
/>
|
}}
|
||||||
|
/>
|
||||||
|
<FormListMoveArrows
|
||||||
|
move={move}
|
||||||
|
index={index}
|
||||||
|
total={fields.length}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
</LayoutFormRow>
|
</LayoutFormRow>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -7,7 +7,9 @@ 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 { alphaSort, statusSort } from "../../utils/sorters";
|
import { alphaSort, statusSort } from "../../utils/sorters";
|
||||||
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
import OwnerNameDisplay, {
|
||||||
|
OwnerNameDisplayFunction,
|
||||||
|
} from "../owner-name-display/owner-name-display.component";
|
||||||
import VehicleDetailUpdateJobsComponent from "../vehicle-detail-update-jobs/vehicle-detail-update-jobs.component";
|
import VehicleDetailUpdateJobsComponent from "../vehicle-detail-update-jobs/vehicle-detail-update-jobs.component";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
@@ -45,6 +47,10 @@ export function VehicleDetailJobsComponent({ vehicle, bodyshop }) {
|
|||||||
title: t("jobs.fields.owner"),
|
title: t("jobs.fields.owner"),
|
||||||
dataIndex: "owner",
|
dataIndex: "owner",
|
||||||
key: "owner",
|
key: "owner",
|
||||||
|
sorter: (a, b) =>
|
||||||
|
alphaSort(OwnerNameDisplayFunction(a), OwnerNameDisplayFunction(b)),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "owner" && state.sortedInfo.order,
|
||||||
render: (text, record) => (
|
render: (text, record) => (
|
||||||
<Link to={`/manage/owners/${record.owner.id}`}>
|
<Link to={`/manage/owners/${record.owner.id}`}>
|
||||||
<OwnerNameDisplay ownerObject={record} />
|
<OwnerNameDisplay ownerObject={record} />
|
||||||
@@ -63,9 +69,15 @@ export function VehicleDetailJobsComponent({ vehicle, bodyshop }) {
|
|||||||
title: t("jobs.fields.status"),
|
title: t("jobs.fields.status"),
|
||||||
dataIndex: "status",
|
dataIndex: "status",
|
||||||
key: "status",
|
key: "status",
|
||||||
sorter: (a, b) => statusSort(a.status, b.status, bodyshop.md_ro_statuses.statuses),
|
sorter: (a, b) =>
|
||||||
|
statusSort(a.status, b.status, bodyshop.md_ro_statuses.statuses),
|
||||||
sortOrder:
|
sortOrder:
|
||||||
state.sortedInfo.columnKey === "status" && state.sortedInfo.order,
|
state.sortedInfo.columnKey === "status" && state.sortedInfo.order,
|
||||||
|
filters: bodyshop.md_ro_statuses.statuses.map((status) => ({
|
||||||
|
text: status,
|
||||||
|
value: status,
|
||||||
|
})),
|
||||||
|
onFilter: (value, record) => value.includes(record.status),
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,8 +4,9 @@ import queryString from "query-string";
|
|||||||
import React, { useState } from "react";
|
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 { pageLimit } from "../../utils/config";
|
||||||
import VehicleVinDisplay from "../vehicle-vin-display/vehicle-vin-display.component";
|
import VehicleVinDisplay from "../vehicle-vin-display/vehicle-vin-display.component";
|
||||||
import {pageLimit} from "../../utils/config";
|
import { alphaSort } from './../../utils/sorters';
|
||||||
export default function VehiclesListComponent({
|
export default function VehiclesListComponent({
|
||||||
loading,
|
loading,
|
||||||
vehicles,
|
vehicles,
|
||||||
@@ -31,6 +32,8 @@ export default function VehiclesListComponent({
|
|||||||
title: t("vehicles.fields.v_vin"),
|
title: t("vehicles.fields.v_vin"),
|
||||||
dataIndex: "v_vin",
|
dataIndex: "v_vin",
|
||||||
key: "v_vin",
|
key: "v_vin",
|
||||||
|
sorter: (a, b) => alphaSort(a.v_vin, b.v_vin),
|
||||||
|
sortOrder: state.sortedInfo.columnKey === "v_vin" && state.sortedInfo.order,
|
||||||
render: (text, record) => (
|
render: (text, record) => (
|
||||||
<Link to={"/manage/vehicles/" + record.id}>
|
<Link to={"/manage/vehicles/" + record.id}>
|
||||||
<VehicleVinDisplay>{record.v_vin || "N/A"}</VehicleVinDisplay>
|
<VehicleVinDisplay>{record.v_vin || "N/A"}</VehicleVinDisplay>
|
||||||
@@ -51,8 +54,10 @@ export default function VehiclesListComponent({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("vehicles.fields.plate_no"),
|
title: t("vehicles.fields.plate_no"),
|
||||||
dataIndex: "plate",
|
dataIndex: "plate_no",
|
||||||
key: "plate",
|
key: "plate_no",
|
||||||
|
sorter: (a, b) => alphaSort(a.plate_no, b.plate_no),
|
||||||
|
sortOrder: state.sortedInfo.columnKey === "plate_no" && state.sortedInfo.order,
|
||||||
render: (text, record) => {
|
render: (text, record) => {
|
||||||
return (
|
return (
|
||||||
<span>{`${record.plate_st || ""} | ${record.plate_no || ""}`}</span>
|
<span>{`${record.plate_st || ""} | ${record.plate_no || ""}`}</span>
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import { getAuth, updatePassword, updateProfile } from "firebase/auth";
|
|||||||
import { getFirestore } from "firebase/firestore";
|
import { getFirestore } from "firebase/firestore";
|
||||||
import { getMessaging, getToken, onMessage } from "firebase/messaging";
|
import { getMessaging, getToken, onMessage } from "firebase/messaging";
|
||||||
import { store } from "../redux/store";
|
import { store } from "../redux/store";
|
||||||
|
import axios from "axios";
|
||||||
|
import { checkBeta } from "../utils/handleBeta";
|
||||||
|
|
||||||
const config = JSON.parse(process.env.REACT_APP_FIREBASE_CONFIG);
|
const config = JSON.parse(process.env.REACT_APP_FIREBASE_CONFIG);
|
||||||
initializeApp(config);
|
initializeApp(config);
|
||||||
@@ -86,6 +88,18 @@ export const logImEXEvent = (eventName, additionalParams, stateProp = null) => {
|
|||||||
null,
|
null,
|
||||||
...additionalParams,
|
...additionalParams,
|
||||||
};
|
};
|
||||||
|
axios.post("/ioevent", {
|
||||||
|
useremail:
|
||||||
|
(state.user && state.user.currentUser && state.user.currentUser.email) ||
|
||||||
|
null,
|
||||||
|
bodyshopid:
|
||||||
|
(state.user && state.user.bodyshop && state.user.bodyshop.id) || null,
|
||||||
|
operationName: eventName,
|
||||||
|
variables: additionalParams,
|
||||||
|
dbevent: false,
|
||||||
|
env: checkBeta() ? "beta" : "master",
|
||||||
|
});
|
||||||
|
|
||||||
// console.log(
|
// console.log(
|
||||||
// "%c[Analytics]",
|
// "%c[Analytics]",
|
||||||
// "background-color: green ;font-weight:bold;",
|
// "background-color: green ;font-weight:bold;",
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ export const INSERT_AUDIT_TRAIL = gql`
|
|||||||
bodyshopid
|
bodyshopid
|
||||||
created
|
created
|
||||||
operation
|
operation
|
||||||
|
type
|
||||||
useremail
|
useremail
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -704,6 +704,7 @@ export const GET_JOB_BY_PK = gql`
|
|||||||
other_amount_payable
|
other_amount_payable
|
||||||
owner {
|
owner {
|
||||||
id
|
id
|
||||||
|
note
|
||||||
ownr_fn
|
ownr_fn
|
||||||
ownr_ln
|
ownr_ln
|
||||||
ownr_co_nm
|
ownr_co_nm
|
||||||
@@ -1862,6 +1863,7 @@ export const QUERY_ALL_JOBS_PAGINATED_STATUS_FILTERED = gql`
|
|||||||
ownr_co_nm
|
ownr_co_nm
|
||||||
ownr_ph1
|
ownr_ph1
|
||||||
ownr_ph2
|
ownr_ph2
|
||||||
|
ownerid
|
||||||
plate_no
|
plate_no
|
||||||
plate_st
|
plate_st
|
||||||
v_vin
|
v_vin
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ export const QUERY_VEHICLE_BY_ID = gql`
|
|||||||
jobs(order_by: { date_open: desc }) {
|
jobs(order_by: { date_open: desc }) {
|
||||||
id
|
id
|
||||||
ro_number
|
ro_number
|
||||||
|
ownr_co_nm
|
||||||
ownr_fn
|
ownr_fn
|
||||||
ownr_ln
|
ownr_ln
|
||||||
owner {
|
owner {
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ import { setModalContext } from "../../redux/modals/modals.actions";
|
|||||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||||
import { DateFormatter } from "../../utils/DateFormatter";
|
import { DateFormatter } from "../../utils/DateFormatter";
|
||||||
import { TemplateList } from "../../utils/TemplateConstants";
|
import { TemplateList } from "../../utils/TemplateConstants";
|
||||||
|
import { pageLimit } from "../../utils/config";
|
||||||
import { alphaSort, dateSort } from "../../utils/sorters";
|
import { alphaSort, dateSort } from "../../utils/sorters";
|
||||||
import {pageLimit} from "../../utils/config";
|
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
setPartsOrderContext: (context) =>
|
setPartsOrderContext: (context) =>
|
||||||
@@ -125,9 +125,7 @@ export function BillsListPage({
|
|||||||
sortOrder:
|
sortOrder:
|
||||||
state.sortedInfo.columnKey === "is_credit_memo" &&
|
state.sortedInfo.columnKey === "is_credit_memo" &&
|
||||||
state.sortedInfo.order,
|
state.sortedInfo.order,
|
||||||
render: (text, record) => (
|
render: (text, record) => <Checkbox checked={record.is_credit_memo} />,
|
||||||
<Checkbox disabled checked={record.is_credit_memo} />
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("bills.fields.exported"),
|
title: t("bills.fields.exported"),
|
||||||
@@ -136,7 +134,7 @@ export function BillsListPage({
|
|||||||
sorter: (a, b) => a.exported - b.exported,
|
sorter: (a, b) => a.exported - b.exported,
|
||||||
sortOrder:
|
sortOrder:
|
||||||
state.sortedInfo.columnKey === "exported" && state.sortedInfo.order,
|
state.sortedInfo.columnKey === "exported" && state.sortedInfo.order,
|
||||||
render: (text, record) => <Checkbox disabled checked={record.exported} />,
|
render: (text, record) => <Checkbox checked={record.exported} />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("general.labels.actions"),
|
title: t("general.labels.actions"),
|
||||||
|
|||||||
@@ -40,8 +40,8 @@ const mapStateToProps = createStructuredSelector({
|
|||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||||
insertAuditTrail: ({ jobid, operation }) =>
|
insertAuditTrail: ({ jobid, operation, type }) =>
|
||||||
dispatch(insertAuditTrail({ jobid, operation })),
|
dispatch(insertAuditTrail({ jobid, operation, type })),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(DmsContainer);
|
export default connect(mapStateToProps, mapDispatchToProps)(DmsContainer);
|
||||||
@@ -127,6 +127,7 @@ export function DmsContainer({
|
|||||||
insertAuditTrail({
|
insertAuditTrail({
|
||||||
jobid: payload,
|
jobid: payload,
|
||||||
operation: AuditTrailMapping.jobexported(),
|
operation: AuditTrailMapping.jobexported(),
|
||||||
|
type: "jobexported",
|
||||||
});
|
});
|
||||||
history.push("/manage/accounting/receivables");
|
history.push("/manage/accounting/receivables");
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -12,7 +12,8 @@ import AlertComponent from "../../components/alert/alert.component";
|
|||||||
import { QUERY_EXPORT_LOG_PAGINATED } from "../../graphql/accounting.queries";
|
import { QUERY_EXPORT_LOG_PAGINATED } from "../../graphql/accounting.queries";
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
import { DateTimeFormatter } from "../../utils/DateFormatter";
|
import { DateTimeFormatter } from "../../utils/DateFormatter";
|
||||||
import {pageLimit} from "../../utils/config";
|
import { pageLimit } from "../../utils/config";
|
||||||
|
import { alphaSort, dateSort } from "./../../utils/sorters";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
@@ -34,11 +35,43 @@ export function ExportLogsPageComponent({ bodyshop }) {
|
|||||||
limit: pageLimit,
|
limit: pageLimit,
|
||||||
order: [
|
order: [
|
||||||
{
|
{
|
||||||
[sortcolumn || "created_at"]: sortorder
|
...(sortcolumn === "ro_number"
|
||||||
? sortorder === "descend"
|
? {
|
||||||
? "desc"
|
job: {
|
||||||
: "asc"
|
[sortcolumn || "created_at"]: sortorder
|
||||||
: "desc",
|
? sortorder === "descend"
|
||||||
|
? "desc"
|
||||||
|
: "asc"
|
||||||
|
: "desc",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
: sortcolumn === "invoice_number"
|
||||||
|
? {
|
||||||
|
bill: {
|
||||||
|
[sortcolumn || "created_at"]: sortorder
|
||||||
|
? sortorder === "descend"
|
||||||
|
? "desc"
|
||||||
|
: "asc"
|
||||||
|
: "desc",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
: sortcolumn === "paymentnum"
|
||||||
|
? {
|
||||||
|
payment: {
|
||||||
|
[sortcolumn || "created_at"]: sortorder
|
||||||
|
? sortorder === "descend"
|
||||||
|
? "desc"
|
||||||
|
: "asc"
|
||||||
|
: "desc",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
[sortcolumn || "created_at"]: sortorder
|
||||||
|
? sortorder === "descend"
|
||||||
|
? "desc"
|
||||||
|
: "asc"
|
||||||
|
: "desc",
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -68,6 +101,8 @@ export function ExportLogsPageComponent({ bodyshop }) {
|
|||||||
title: t("general.labels.created_at"),
|
title: t("general.labels.created_at"),
|
||||||
dataIndex: "created_at",
|
dataIndex: "created_at",
|
||||||
key: "created_at",
|
key: "created_at",
|
||||||
|
sorter: (a, b) => dateSort(a.created_at, b.created_at),
|
||||||
|
sortOrder: sortcolumn === "created_at" && sortorder,
|
||||||
render: (text, record) => (
|
render: (text, record) => (
|
||||||
<DateTimeFormatter>{record.created_at}</DateTimeFormatter>
|
<DateTimeFormatter>{record.created_at}</DateTimeFormatter>
|
||||||
),
|
),
|
||||||
@@ -81,7 +116,8 @@ export function ExportLogsPageComponent({ bodyshop }) {
|
|||||||
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: sortcolumn === "ro_number" && sortorder,
|
||||||
render: (text, record) =>
|
render: (text, record) =>
|
||||||
record.job && (
|
record.job && (
|
||||||
<Link to={`/manage/jobs/${record.job.id}`}>
|
<Link to={`/manage/jobs/${record.job.id}`}>
|
||||||
@@ -93,6 +129,8 @@ export function ExportLogsPageComponent({ bodyshop }) {
|
|||||||
title: t("bills.fields.invoice_number"),
|
title: t("bills.fields.invoice_number"),
|
||||||
dataIndex: "invoice_number",
|
dataIndex: "invoice_number",
|
||||||
key: "invoice_number",
|
key: "invoice_number",
|
||||||
|
sorter: (a, b) => alphaSort(a.invoice_number, b.invoice_number),
|
||||||
|
sortOrder: sortcolumn === "invoice_number" && sortorder,
|
||||||
render: (text, record) =>
|
render: (text, record) =>
|
||||||
record.bill && (
|
record.bill && (
|
||||||
<Link to={"/manage/bills?billid=" + (record.bill && record.bill.id)}>
|
<Link to={"/manage/bills?billid=" + (record.bill && record.bill.id)}>
|
||||||
@@ -104,6 +142,8 @@ export function ExportLogsPageComponent({ bodyshop }) {
|
|||||||
title: t("payments.fields.paymentnum"),
|
title: t("payments.fields.paymentnum"),
|
||||||
dataIndex: "paymentnum",
|
dataIndex: "paymentnum",
|
||||||
key: "paymentnum",
|
key: "paymentnum",
|
||||||
|
sorter: (a, b) => alphaSort(a.paymentnum, b.paymentnum),
|
||||||
|
sortOrder: sortcolumn === "paymentnum" && sortorder,
|
||||||
render: (text, record) =>
|
render: (text, record) =>
|
||||||
record.payment && (
|
record.payment && (
|
||||||
<Link
|
<Link
|
||||||
@@ -120,9 +160,14 @@ export function ExportLogsPageComponent({ bodyshop }) {
|
|||||||
title: t("general.labels.successful"),
|
title: t("general.labels.successful"),
|
||||||
dataIndex: "successful",
|
dataIndex: "successful",
|
||||||
key: "successful",
|
key: "successful",
|
||||||
render: (text, record) => (
|
sorter: (a, b) => Number(a.successful) - Number(b.successful),
|
||||||
<Checkbox disabled checked={record.successful} />
|
sortOrder: sortcolumn === "successful" && sortorder,
|
||||||
),
|
filters: [
|
||||||
|
{ text: "True", value: true },
|
||||||
|
{ text: "False", value: false },
|
||||||
|
],
|
||||||
|
onFilter: (value, record) => record.successful === value,
|
||||||
|
render: (text, record) => <Checkbox checked={record.successful} />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("general.labels.message"),
|
title: t("general.labels.message"),
|
||||||
|
|||||||
@@ -47,11 +47,11 @@ const mapStateToProps = createStructuredSelector({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
insertAuditTrail: ({ jobid, operation }) =>
|
insertAuditTrail: ({ jobid, operation, type }) =>
|
||||||
dispatch(insertAuditTrail({ jobid, operation })),
|
dispatch(insertAuditTrail({ jobid, operation, type })),
|
||||||
});
|
});
|
||||||
|
|
||||||
export function JobsCloseComponent({ job, bodyshop, jobRO, insertAuditTrail, }) {
|
export function JobsCloseComponent({ job, bodyshop, jobRO, insertAuditTrail }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
const client = useApolloClient();
|
const client = useApolloClient();
|
||||||
@@ -121,6 +121,7 @@ export function JobsCloseComponent({ job, bodyshop, jobRO, insertAuditTrail, })
|
|||||||
insertAuditTrail({
|
insertAuditTrail({
|
||||||
jobid: job.id,
|
jobid: job.id,
|
||||||
operation: AuditTrailMapping.jobinvoiced(),
|
operation: AuditTrailMapping.jobinvoiced(),
|
||||||
|
type: "jobinvoiced",
|
||||||
});
|
});
|
||||||
// history.push(`/manage/jobs/${job.id}`);
|
// history.push(`/manage/jobs/${job.id}`);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import { createStructuredSelector } from "reselect";
|
|||||||
import FormFieldsChanged from "../../components/form-fields-changed-alert/form-fields-changed-alert.component";
|
import FormFieldsChanged from "../../components/form-fields-changed-alert/form-fields-changed-alert.component";
|
||||||
import JobAuditTrail from "../../components/job-audit-trail/job-audit-trail.component";
|
import JobAuditTrail from "../../components/job-audit-trail/job-audit-trail.component";
|
||||||
import JobsLinesContainer from "../../components/job-detail-lines/job-lines.container";
|
import JobsLinesContainer from "../../components/job-detail-lines/job-lines.container";
|
||||||
|
import JobLifecycleComponent from "../../components/job-lifecycle/job-lifecycle.component";
|
||||||
import JobLineUpsertModalContainer from "../../components/job-lines-upsert-modal/job-lines-upsert-modal.container";
|
import JobLineUpsertModalContainer from "../../components/job-lines-upsert-modal/job-lines-upsert-modal.container";
|
||||||
import JobReconciliationModal from "../../components/job-reconciliation-modal/job-reconciliation.modal.container";
|
import JobReconciliationModal from "../../components/job-reconciliation-modal/job-reconciliation.modal.container";
|
||||||
import JobSyncButton from "../../components/job-sync-button/job-sync-button.component";
|
import JobSyncButton from "../../components/job-sync-button/job-sync-button.component";
|
||||||
@@ -54,7 +55,6 @@ 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,
|
||||||
@@ -63,8 +63,8 @@ const mapStateToProps = createStructuredSelector({
|
|||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
setPrintCenterContext: (context) =>
|
setPrintCenterContext: (context) =>
|
||||||
dispatch(setModalContext({ context: context, modal: "printCenter" })),
|
dispatch(setModalContext({ context: context, modal: "printCenter" })),
|
||||||
insertAuditTrail: ({ jobid, operation }) =>
|
insertAuditTrail: ({ jobid, operation, type }) =>
|
||||||
dispatch(insertAuditTrail({ jobid, operation })),
|
dispatch(insertAuditTrail({ jobid, operation, type })),
|
||||||
});
|
});
|
||||||
export function JobsDetailPage({
|
export function JobsDetailPage({
|
||||||
bodyshop,
|
bodyshop,
|
||||||
@@ -177,6 +177,7 @@ export function JobsDetailPage({
|
|||||||
? DateTimeFormat(changedAuditFields[key])
|
? DateTimeFormat(changedAuditFields[key])
|
||||||
: changedAuditFields[key]
|
: changedAuditFields[key]
|
||||||
),
|
),
|
||||||
|
type: "jobfieldchange",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -334,15 +335,23 @@ export function JobsDetailPage({
|
|||||||
>
|
>
|
||||||
<JobsDetailLaborContainer job={job} jobId={job.id} />
|
<JobsDetailLaborContainer job={job} jobId={job.id} />
|
||||||
</Tabs.TabPane>
|
</Tabs.TabPane>
|
||||||
<Tabs.TabPane
|
<Tabs.TabPane
|
||||||
forceRender
|
forceRender
|
||||||
tab={<span><BarsOutlined />{t('menus.jobsdetail.lifecycle')}</span>}
|
tab={
|
||||||
key="lifecycle"
|
<span>
|
||||||
>
|
<BarsOutlined />
|
||||||
<JobLifecycleComponent job={job} statuses={bodyshop.md_ro_statuses}/>
|
{t("menus.jobsdetail.lifecycle")}
|
||||||
</Tabs.TabPane>
|
</span>
|
||||||
|
}
|
||||||
|
key="lifecycle"
|
||||||
|
>
|
||||||
|
<JobLifecycleComponent
|
||||||
|
job={job}
|
||||||
|
statuses={bodyshop.md_ro_statuses}
|
||||||
|
/>
|
||||||
|
</Tabs.TabPane>
|
||||||
|
|
||||||
<Tabs.TabPane
|
<Tabs.TabPane
|
||||||
forceRender
|
forceRender
|
||||||
tab={
|
tab={
|
||||||
<span>
|
<span>
|
||||||
|
|||||||
@@ -54,9 +54,9 @@ export const setOnline = (isOnline) => ({
|
|||||||
payload: isOnline,
|
payload: isOnline,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const insertAuditTrail = ({ jobid, billid, operation }) => ({
|
export const insertAuditTrail = ({ jobid, billid, operation, type }) => ({
|
||||||
type: ApplicationActionTypes.INSERT_AUDIT_TRAIL,
|
type: ApplicationActionTypes.INSERT_AUDIT_TRAIL,
|
||||||
payload: { jobid, billid, operation },
|
payload: { jobid, billid, operation, type },
|
||||||
});
|
});
|
||||||
export const setProblemJobs = (problemJobs) => ({
|
export const setProblemJobs = (problemJobs) => ({
|
||||||
type: ApplicationActionTypes.SET_PROBLEM_JOBS,
|
type: ApplicationActionTypes.SET_PROBLEM_JOBS,
|
||||||
|
|||||||
@@ -266,7 +266,7 @@ export function* onInsertAuditTrail() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function* insertAuditTrailSaga({
|
export function* insertAuditTrailSaga({
|
||||||
payload: { jobid, billid, operation },
|
payload: { jobid, billid, operation, type },
|
||||||
}) {
|
}) {
|
||||||
const state = yield select();
|
const state = yield select();
|
||||||
const bodyshop = state.user.bodyshop;
|
const bodyshop = state.user.bodyshop;
|
||||||
@@ -278,6 +278,7 @@ export function* insertAuditTrailSaga({
|
|||||||
jobid,
|
jobid,
|
||||||
billid,
|
billid,
|
||||||
operation,
|
operation,
|
||||||
|
type,
|
||||||
useremail: currentUser.email,
|
useremail: currentUser.email,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -259,6 +259,7 @@
|
|||||||
"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",
|
||||||
@@ -477,7 +478,6 @@
|
|||||||
"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",
|
||||||
@@ -608,7 +608,7 @@
|
|||||||
"dms": {
|
"dms": {
|
||||||
"cdk": {
|
"cdk": {
|
||||||
"controllist": "Control Number List",
|
"controllist": "Control Number List",
|
||||||
"payers": "CDK Payers"
|
"payers": "Payers"
|
||||||
},
|
},
|
||||||
"cdk_dealerid": "CDK Dealer ID",
|
"cdk_dealerid": "CDK Dealer ID",
|
||||||
"pbs_serialnumber": "PBS Serial Number",
|
"pbs_serialnumber": "PBS Serial Number",
|
||||||
@@ -844,8 +844,8 @@
|
|||||||
"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}}.",
|
||||||
"surveycompletesubtitle": "This survey was already completed on {{date}}."
|
"surveycompletetitle": "Survey previously completed"
|
||||||
},
|
},
|
||||||
"fields": {
|
"fields": {
|
||||||
"completedon": "Completed On",
|
"completedon": "Completed On",
|
||||||
@@ -854,13 +854,13 @@
|
|||||||
"validuntil": "Valid Until"
|
"validuntil": "Valid Until"
|
||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
|
"copyright": "Copyright © $t(titles.app). All Rights Reserved.",
|
||||||
|
"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.",
|
||||||
"nologgedinuser": "Please log out of $t(titles.app)",
|
"nologgedinuser": "Please log out of $t(titles.app)",
|
||||||
"nologgedinuser_sub": "Users of $t(titles.app) 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.",
|
||||||
@@ -898,7 +898,8 @@
|
|||||||
"scheduledindate": "Sheduled In Today: {{date}}",
|
"scheduledindate": "Sheduled In Today: {{date}}",
|
||||||
"scheduledintoday": "Sheduled In Today",
|
"scheduledintoday": "Sheduled In Today",
|
||||||
"scheduledoutdate": "Sheduled Out Today: {{date}}",
|
"scheduledoutdate": "Sheduled Out Today: {{date}}",
|
||||||
"scheduledouttoday": "Sheduled Out Today"
|
"scheduledouttoday": "Sheduled Out Today",
|
||||||
|
"joblifecycle": "Job Lifecycle"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dms": {
|
"dms": {
|
||||||
@@ -1113,6 +1114,7 @@
|
|||||||
"loadingshop": "Loading shop data...",
|
"loadingshop": "Loading shop data...",
|
||||||
"loggingin": "Authorizing...",
|
"loggingin": "Authorizing...",
|
||||||
"markedexported": "Manually marked as exported.",
|
"markedexported": "Manually marked as exported.",
|
||||||
|
"media": "Media",
|
||||||
"message": "Message",
|
"message": "Message",
|
||||||
"monday": "Monday",
|
"monday": "Monday",
|
||||||
"na": "N/A",
|
"na": "N/A",
|
||||||
@@ -1236,7 +1238,15 @@
|
|||||||
"relative_end": "Relative End",
|
"relative_end": "Relative End",
|
||||||
"relative_start": "Relative Start",
|
"relative_start": "Relative Start",
|
||||||
"start": "Start",
|
"start": "Start",
|
||||||
"value": "Value"
|
"value": "Value",
|
||||||
|
"status": "Status",
|
||||||
|
"percentage": "Percentage",
|
||||||
|
"human_readable": "Human Readable",
|
||||||
|
"status_count": "In Status"
|
||||||
|
},
|
||||||
|
"titles": {
|
||||||
|
"dashboard": "Job Lifecycle",
|
||||||
|
"top_durations": "Top Durations"
|
||||||
},
|
},
|
||||||
"content": {
|
"content": {
|
||||||
"current_status_accumulated_time": "Current Status Accumulated Time",
|
"current_status_accumulated_time": "Current Status Accumulated Time",
|
||||||
@@ -1248,7 +1258,9 @@
|
|||||||
"title": "Job Lifecycle Component",
|
"title": "Job Lifecycle Component",
|
||||||
"title_durations": "Historical Status Durations",
|
"title_durations": "Historical Status Durations",
|
||||||
"title_loading": "Loading",
|
"title_loading": "Loading",
|
||||||
"title_transitions": "Transitions"
|
"title_transitions": "Transitions",
|
||||||
|
"calculated_based_on": "Calculated based on",
|
||||||
|
"jobs_in_since": "Jobs in since"
|
||||||
},
|
},
|
||||||
"errors": {
|
"errors": {
|
||||||
"fetch": "Error getting Job Lifecycle Data"
|
"fetch": "Error getting Job Lifecycle Data"
|
||||||
@@ -1561,7 +1573,7 @@
|
|||||||
"federal_tax_payable": "Federal Tax Payable",
|
"federal_tax_payable": "Federal Tax Payable",
|
||||||
"federal_tax_rate": "Federal Tax Rate",
|
"federal_tax_rate": "Federal Tax Rate",
|
||||||
"ins_addr1": "Insurance Co. Address",
|
"ins_addr1": "Insurance Co. Address",
|
||||||
"ins_city": "Insurance City",
|
"ins_city": "Insurance Co. City",
|
||||||
"ins_co_id": "Insurance Co. ID",
|
"ins_co_id": "Insurance Co. ID",
|
||||||
"ins_co_nm": "Insurance Company Name",
|
"ins_co_nm": "Insurance Company Name",
|
||||||
"ins_co_nm_short": "Ins. Co.",
|
"ins_co_nm_short": "Ins. Co.",
|
||||||
@@ -1823,6 +1835,7 @@
|
|||||||
"job": "Job Details",
|
"job": "Job Details",
|
||||||
"jobcosting": "Job Costing",
|
"jobcosting": "Job Costing",
|
||||||
"jobtotals": "Job Totals",
|
"jobtotals": "Job Totals",
|
||||||
|
"labor_hrs": "B/P/T Hrs",
|
||||||
"labor_rates_subtotal": "Labor Rates Subtotal",
|
"labor_rates_subtotal": "Labor Rates Subtotal",
|
||||||
"laborallocations": "Labor Allocations",
|
"laborallocations": "Labor Allocations",
|
||||||
"labortotals": "Labor Totals",
|
"labortotals": "Labor Totals",
|
||||||
@@ -2417,6 +2430,7 @@
|
|||||||
"invoice_total_payable": "Invoice (Total Payable)",
|
"invoice_total_payable": "Invoice (Total Payable)",
|
||||||
"iou_form": "IOU Form",
|
"iou_form": "IOU Form",
|
||||||
"job_costing_ro": "Job Costing",
|
"job_costing_ro": "Job Costing",
|
||||||
|
"job_lifecycle_ro": "Job Lifecycle",
|
||||||
"job_notes": "Job Notes",
|
"job_notes": "Job Notes",
|
||||||
"key_tag": "Key Tag",
|
"key_tag": "Key Tag",
|
||||||
"labels": {
|
"labels": {
|
||||||
@@ -2583,15 +2597,17 @@
|
|||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
"advanced_filters": "Advanced Filters and Sorters",
|
"advanced_filters": "Advanced Filters and Sorters",
|
||||||
"advanced_filters_show": "Show",
|
"advanced_filters_false": "False",
|
||||||
"advanced_filters_hide": "Hide",
|
|
||||||
"advanced_filters_filters": "Filters",
|
|
||||||
"advanced_filters_sorters": "Sorters",
|
|
||||||
"advanced_filters_filter_field": "Field",
|
"advanced_filters_filter_field": "Field",
|
||||||
"advanced_filters_sorter_field": "Field",
|
|
||||||
"advanced_filters_sorter_direction": "Direction",
|
|
||||||
"advanced_filters_filter_operator": "Operator",
|
"advanced_filters_filter_operator": "Operator",
|
||||||
"advanced_filters_filter_value": "Value",
|
"advanced_filters_filter_value": "Value",
|
||||||
|
"advanced_filters_filters": "Filters",
|
||||||
|
"advanced_filters_hide": "Hide",
|
||||||
|
"advanced_filters_show": "Show",
|
||||||
|
"advanced_filters_sorter_direction": "Direction",
|
||||||
|
"advanced_filters_sorter_field": "Field",
|
||||||
|
"advanced_filters_sorters": "Sorters",
|
||||||
|
"advanced_filters_true": "True",
|
||||||
"dates": "Dates",
|
"dates": "Dates",
|
||||||
"employee": "Employee",
|
"employee": "Employee",
|
||||||
"filterson": "Filters on {{object}}: {{field}}",
|
"filterson": "Filters on {{object}}: {{field}}",
|
||||||
@@ -2673,6 +2689,8 @@
|
|||||||
"job_costing_ro_date_summary": "Job Costing by RO - Summary",
|
"job_costing_ro_date_summary": "Job Costing by RO - Summary",
|
||||||
"job_costing_ro_estimator": "Job Costing by Estimator",
|
"job_costing_ro_estimator": "Job Costing by Estimator",
|
||||||
"job_costing_ro_ins_co": "Job Costing by RO Source",
|
"job_costing_ro_ins_co": "Job Costing by RO Source",
|
||||||
|
"job_lifecycle_date_detail": "Job Lifecycle by Date - Detail",
|
||||||
|
"job_lifecycle_date_summary": "Job Lifecycle by Date - Summary",
|
||||||
"jobs_completed_not_invoiced": "Jobs Completed not Invoiced",
|
"jobs_completed_not_invoiced": "Jobs Completed not Invoiced",
|
||||||
"jobs_invoiced_not_exported": "Jobs Invoiced not Exported",
|
"jobs_invoiced_not_exported": "Jobs Invoiced not Exported",
|
||||||
"jobs_reconcile": "Parts/Sublet/Labor Reconciliation",
|
"jobs_reconcile": "Parts/Sublet/Labor Reconciliation",
|
||||||
@@ -2762,6 +2780,7 @@
|
|||||||
"allemployeetimetickets": "All Employee Time Tickets",
|
"allemployeetimetickets": "All Employee Time Tickets",
|
||||||
"asoftodaytarget": "As of Today",
|
"asoftodaytarget": "As of Today",
|
||||||
"body": "Body",
|
"body": "Body",
|
||||||
|
"bodyabbrev": "B",
|
||||||
"bodycharttitle": "Body Targets vs Actual",
|
"bodycharttitle": "Body Targets vs Actual",
|
||||||
"calendarperiod": "Periods based on calendar weeks/months.",
|
"calendarperiod": "Periods based on calendar weeks/months.",
|
||||||
"combinedcharttitle": "Combined Targets vs Actual",
|
"combinedcharttitle": "Combined Targets vs Actual",
|
||||||
@@ -2778,6 +2797,7 @@
|
|||||||
"productivestatistics": "Productive Hours Statistics",
|
"productivestatistics": "Productive Hours Statistics",
|
||||||
"productivetimeticketsoverdate": "Productive Hours over Selected Dates",
|
"productivetimeticketsoverdate": "Productive Hours over Selected Dates",
|
||||||
"refinish": "Refinish",
|
"refinish": "Refinish",
|
||||||
|
"refinishabbrev": "R",
|
||||||
"refinishcharttitle": "Refinish Targets vs Actual",
|
"refinishcharttitle": "Refinish Targets vs Actual",
|
||||||
"targets": "Targets",
|
"targets": "Targets",
|
||||||
"thismonth": "This Month",
|
"thismonth": "This Month",
|
||||||
@@ -2785,6 +2805,7 @@
|
|||||||
"timetickets": "Time Tickets",
|
"timetickets": "Time Tickets",
|
||||||
"timeticketsemployee": "Time Tickets by Employee",
|
"timeticketsemployee": "Time Tickets by Employee",
|
||||||
"todateactual": "Actual (MTD)",
|
"todateactual": "Actual (MTD)",
|
||||||
|
"total": "Total",
|
||||||
"totalhrs": "Total Hours",
|
"totalhrs": "Total Hours",
|
||||||
"totaloverperiod": "Total over Selected Dates",
|
"totaloverperiod": "Total over Selected Dates",
|
||||||
"weeklyactual": "Actual (W)",
|
"weeklyactual": "Actual (W)",
|
||||||
|
|||||||
@@ -259,6 +259,7 @@
|
|||||||
"saving": ""
|
"saving": ""
|
||||||
},
|
},
|
||||||
"fields": {
|
"fields": {
|
||||||
|
"ReceivableCustomField": "",
|
||||||
"address1": "",
|
"address1": "",
|
||||||
"address2": "",
|
"address2": "",
|
||||||
"appt_alt_transport": "",
|
"appt_alt_transport": "",
|
||||||
@@ -477,7 +478,6 @@
|
|||||||
"editaccess": ""
|
"editaccess": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ReceivableCustomField": "",
|
|
||||||
"responsibilitycenter": "",
|
"responsibilitycenter": "",
|
||||||
"responsibilitycenter_accountdesc": "",
|
"responsibilitycenter_accountdesc": "",
|
||||||
"responsibilitycenter_accountitem": "",
|
"responsibilitycenter_accountitem": "",
|
||||||
@@ -844,8 +844,8 @@
|
|||||||
"notconfigured": "",
|
"notconfigured": "",
|
||||||
"notfoundsubtitle": "",
|
"notfoundsubtitle": "",
|
||||||
"notfoundtitle": "",
|
"notfoundtitle": "",
|
||||||
"surveycompletetitle": "",
|
"surveycompletesubtitle": "",
|
||||||
"surveycompletesubtitle": ""
|
"surveycompletetitle": ""
|
||||||
},
|
},
|
||||||
"fields": {
|
"fields": {
|
||||||
"completedon": "",
|
"completedon": "",
|
||||||
@@ -854,13 +854,13 @@
|
|||||||
"validuntil": ""
|
"validuntil": ""
|
||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
|
"copyright": "",
|
||||||
|
"greeting": "",
|
||||||
|
"intro": "",
|
||||||
"nologgedinuser": "",
|
"nologgedinuser": "",
|
||||||
"nologgedinuser_sub": "",
|
"nologgedinuser_sub": "",
|
||||||
"noneselected": "",
|
"noneselected": "",
|
||||||
"title": "",
|
"title": ""
|
||||||
"greeting": "",
|
|
||||||
"intro": "",
|
|
||||||
"copyright": ""
|
|
||||||
},
|
},
|
||||||
"successes": {
|
"successes": {
|
||||||
"created": "",
|
"created": "",
|
||||||
@@ -898,7 +898,8 @@
|
|||||||
"scheduledindate": "",
|
"scheduledindate": "",
|
||||||
"scheduledintoday": "",
|
"scheduledintoday": "",
|
||||||
"scheduledoutdate": "",
|
"scheduledoutdate": "",
|
||||||
"scheduledouttoday": ""
|
"scheduledouttoday": "",
|
||||||
|
"joblifecycle": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dms": {
|
"dms": {
|
||||||
@@ -1236,7 +1237,15 @@
|
|||||||
"relative_end": "",
|
"relative_end": "",
|
||||||
"relative_start": "",
|
"relative_start": "",
|
||||||
"start": "",
|
"start": "",
|
||||||
"value": ""
|
"value": "",
|
||||||
|
"status": "",
|
||||||
|
"percentage": "",
|
||||||
|
"human_readable": "",
|
||||||
|
"status_count": ""
|
||||||
|
},
|
||||||
|
"titles": {
|
||||||
|
"dashboard": "",
|
||||||
|
"top_durations": ""
|
||||||
},
|
},
|
||||||
"content": {
|
"content": {
|
||||||
"current_status_accumulated_time": "",
|
"current_status_accumulated_time": "",
|
||||||
@@ -1248,7 +1257,9 @@
|
|||||||
"title": "",
|
"title": "",
|
||||||
"title_durations": "",
|
"title_durations": "",
|
||||||
"title_loading": "",
|
"title_loading": "",
|
||||||
"title_transitions": ""
|
"title_transitions": "",
|
||||||
|
"calculated_based_on": "",
|
||||||
|
"jobs_in_since": ""
|
||||||
},
|
},
|
||||||
"errors": {
|
"errors": {
|
||||||
"fetch": "Error al obtener los datos del ciclo de vida del trabajo"
|
"fetch": "Error al obtener los datos del ciclo de vida del trabajo"
|
||||||
@@ -1823,6 +1834,7 @@
|
|||||||
"job": "",
|
"job": "",
|
||||||
"jobcosting": "",
|
"jobcosting": "",
|
||||||
"jobtotals": "",
|
"jobtotals": "",
|
||||||
|
"labor_hrs": "",
|
||||||
"labor_rates_subtotal": "",
|
"labor_rates_subtotal": "",
|
||||||
"laborallocations": "",
|
"laborallocations": "",
|
||||||
"labortotals": "",
|
"labortotals": "",
|
||||||
@@ -2417,6 +2429,7 @@
|
|||||||
"invoice_total_payable": "",
|
"invoice_total_payable": "",
|
||||||
"iou_form": "",
|
"iou_form": "",
|
||||||
"job_costing_ro": "",
|
"job_costing_ro": "",
|
||||||
|
"job_lifecycle_ro": "",
|
||||||
"job_notes": "",
|
"job_notes": "",
|
||||||
"key_tag": "",
|
"key_tag": "",
|
||||||
"labels": {
|
"labels": {
|
||||||
@@ -2583,15 +2596,17 @@
|
|||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
"advanced_filters": "",
|
"advanced_filters": "",
|
||||||
"advanced_filters_show": "",
|
"advanced_filters_false": "",
|
||||||
"advanced_filters_hide": "",
|
|
||||||
"advanced_filters_filters": "",
|
|
||||||
"advanced_filters_sorters": "",
|
|
||||||
"advanced_filters_filter_field": "",
|
"advanced_filters_filter_field": "",
|
||||||
"advanced_filters_sorter_field": "",
|
|
||||||
"advanced_filters_sorter_direction": "",
|
|
||||||
"advanced_filters_filter_operator": "",
|
"advanced_filters_filter_operator": "",
|
||||||
"advanced_filters_filter_value": "",
|
"advanced_filters_filter_value": "",
|
||||||
|
"advanced_filters_filters": "",
|
||||||
|
"advanced_filters_hide": "",
|
||||||
|
"advanced_filters_show": "",
|
||||||
|
"advanced_filters_sorter_direction": "",
|
||||||
|
"advanced_filters_sorter_field": "",
|
||||||
|
"advanced_filters_sorters": "",
|
||||||
|
"advanced_filters_true": "",
|
||||||
"dates": "",
|
"dates": "",
|
||||||
"employee": "",
|
"employee": "",
|
||||||
"filterson": "",
|
"filterson": "",
|
||||||
@@ -2673,6 +2688,8 @@
|
|||||||
"job_costing_ro_date_summary": "",
|
"job_costing_ro_date_summary": "",
|
||||||
"job_costing_ro_estimator": "",
|
"job_costing_ro_estimator": "",
|
||||||
"job_costing_ro_ins_co": "",
|
"job_costing_ro_ins_co": "",
|
||||||
|
"job_lifecycle_date_detail": "",
|
||||||
|
"job_lifecycle_date_summary": "",
|
||||||
"jobs_completed_not_invoiced": "",
|
"jobs_completed_not_invoiced": "",
|
||||||
"jobs_invoiced_not_exported": "",
|
"jobs_invoiced_not_exported": "",
|
||||||
"jobs_reconcile": "",
|
"jobs_reconcile": "",
|
||||||
@@ -2762,6 +2779,7 @@
|
|||||||
"allemployeetimetickets": "",
|
"allemployeetimetickets": "",
|
||||||
"asoftodaytarget": "",
|
"asoftodaytarget": "",
|
||||||
"body": "",
|
"body": "",
|
||||||
|
"bodyabbrev": "",
|
||||||
"bodycharttitle": "",
|
"bodycharttitle": "",
|
||||||
"calendarperiod": "",
|
"calendarperiod": "",
|
||||||
"combinedcharttitle": "",
|
"combinedcharttitle": "",
|
||||||
@@ -2778,6 +2796,7 @@
|
|||||||
"productivestatistics": "",
|
"productivestatistics": "",
|
||||||
"productivetimeticketsoverdate": "",
|
"productivetimeticketsoverdate": "",
|
||||||
"refinish": "",
|
"refinish": "",
|
||||||
|
"refinishabbrev": "",
|
||||||
"refinishcharttitle": "",
|
"refinishcharttitle": "",
|
||||||
"targets": "",
|
"targets": "",
|
||||||
"thismonth": "",
|
"thismonth": "",
|
||||||
@@ -2785,6 +2804,7 @@
|
|||||||
"timetickets": "",
|
"timetickets": "",
|
||||||
"timeticketsemployee": "",
|
"timeticketsemployee": "",
|
||||||
"todateactual": "",
|
"todateactual": "",
|
||||||
|
"total": "",
|
||||||
"totalhrs": "",
|
"totalhrs": "",
|
||||||
"totaloverperiod": "",
|
"totaloverperiod": "",
|
||||||
"weeklyactual": "",
|
"weeklyactual": "",
|
||||||
|
|||||||
@@ -259,6 +259,7 @@
|
|||||||
"saving": ""
|
"saving": ""
|
||||||
},
|
},
|
||||||
"fields": {
|
"fields": {
|
||||||
|
"ReceivableCustomField": "",
|
||||||
"address1": "",
|
"address1": "",
|
||||||
"address2": "",
|
"address2": "",
|
||||||
"appt_alt_transport": "",
|
"appt_alt_transport": "",
|
||||||
@@ -477,7 +478,6 @@
|
|||||||
"editaccess": ""
|
"editaccess": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ReceivableCustomField": "",
|
|
||||||
"responsibilitycenter": "",
|
"responsibilitycenter": "",
|
||||||
"responsibilitycenter_accountdesc": "",
|
"responsibilitycenter_accountdesc": "",
|
||||||
"responsibilitycenter_accountitem": "",
|
"responsibilitycenter_accountitem": "",
|
||||||
@@ -844,8 +844,8 @@
|
|||||||
"notconfigured": "",
|
"notconfigured": "",
|
||||||
"notfoundsubtitle": "",
|
"notfoundsubtitle": "",
|
||||||
"notfoundtitle": "",
|
"notfoundtitle": "",
|
||||||
"surveycompletetitle": "",
|
"surveycompletesubtitle": "",
|
||||||
"surveycompletesubtitle": ""
|
"surveycompletetitle": ""
|
||||||
},
|
},
|
||||||
"fields": {
|
"fields": {
|
||||||
"completedon": "",
|
"completedon": "",
|
||||||
@@ -854,13 +854,13 @@
|
|||||||
"validuntil": ""
|
"validuntil": ""
|
||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
|
"copyright": "",
|
||||||
|
"greeting": "",
|
||||||
|
"intro": "",
|
||||||
"nologgedinuser": "",
|
"nologgedinuser": "",
|
||||||
"nologgedinuser_sub": "",
|
"nologgedinuser_sub": "",
|
||||||
"noneselected": "",
|
"noneselected": "",
|
||||||
"title": "",
|
"title": ""
|
||||||
"greeting": "",
|
|
||||||
"intro": "",
|
|
||||||
"copyright": ""
|
|
||||||
},
|
},
|
||||||
"successes": {
|
"successes": {
|
||||||
"created": "",
|
"created": "",
|
||||||
@@ -1236,7 +1236,15 @@
|
|||||||
"relative_end": "",
|
"relative_end": "",
|
||||||
"relative_start": "",
|
"relative_start": "",
|
||||||
"start": "",
|
"start": "",
|
||||||
"value": ""
|
"value": "",
|
||||||
|
"status": "",
|
||||||
|
"percentage": "",
|
||||||
|
"human_readable": "",
|
||||||
|
"status_count": ""
|
||||||
|
},
|
||||||
|
"titles": {
|
||||||
|
"dashboard": "",
|
||||||
|
"top_durations": ""
|
||||||
},
|
},
|
||||||
"content": {
|
"content": {
|
||||||
"current_status_accumulated_time": "",
|
"current_status_accumulated_time": "",
|
||||||
@@ -1248,7 +1256,10 @@
|
|||||||
"title": "",
|
"title": "",
|
||||||
"title_durations": "",
|
"title_durations": "",
|
||||||
"title_loading": "",
|
"title_loading": "",
|
||||||
"title_transitions": ""
|
"title_transitions": "",
|
||||||
|
"calculated_based_on": "",
|
||||||
|
"jobs_in_since": "",
|
||||||
|
"joblifecycle": ""
|
||||||
},
|
},
|
||||||
"errors": {
|
"errors": {
|
||||||
"fetch": "Erreur lors de l'obtention des données du cycle de vie des tâches"
|
"fetch": "Erreur lors de l'obtention des données du cycle de vie des tâches"
|
||||||
@@ -1823,6 +1834,7 @@
|
|||||||
"job": "",
|
"job": "",
|
||||||
"jobcosting": "",
|
"jobcosting": "",
|
||||||
"jobtotals": "",
|
"jobtotals": "",
|
||||||
|
"labor_hrs": "",
|
||||||
"labor_rates_subtotal": "",
|
"labor_rates_subtotal": "",
|
||||||
"laborallocations": "",
|
"laborallocations": "",
|
||||||
"labortotals": "",
|
"labortotals": "",
|
||||||
@@ -2417,6 +2429,7 @@
|
|||||||
"invoice_total_payable": "",
|
"invoice_total_payable": "",
|
||||||
"iou_form": "",
|
"iou_form": "",
|
||||||
"job_costing_ro": "",
|
"job_costing_ro": "",
|
||||||
|
"job_lifecycle_ro": "",
|
||||||
"job_notes": "",
|
"job_notes": "",
|
||||||
"key_tag": "",
|
"key_tag": "",
|
||||||
"labels": {
|
"labels": {
|
||||||
@@ -2583,15 +2596,17 @@
|
|||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
"advanced_filters": "",
|
"advanced_filters": "",
|
||||||
"advanced_filters_show": "",
|
"advanced_filters_false": "",
|
||||||
"advanced_filters_hide": "",
|
|
||||||
"advanced_filters_filters": "",
|
|
||||||
"advanced_filters_sorters": "",
|
|
||||||
"advanced_filters_filter_field": "",
|
"advanced_filters_filter_field": "",
|
||||||
"advanced_filters_sorter_field": "",
|
|
||||||
"advanced_filters_sorter_direction": "",
|
|
||||||
"advanced_filters_filter_operator": "",
|
"advanced_filters_filter_operator": "",
|
||||||
"advanced_filters_filter_value": "",
|
"advanced_filters_filter_value": "",
|
||||||
|
"advanced_filters_filters": "",
|
||||||
|
"advanced_filters_hide": "",
|
||||||
|
"advanced_filters_show": "",
|
||||||
|
"advanced_filters_sorter_direction": "",
|
||||||
|
"advanced_filters_sorter_field": "",
|
||||||
|
"advanced_filters_sorters": "",
|
||||||
|
"advanced_filters_true": "",
|
||||||
"dates": "",
|
"dates": "",
|
||||||
"employee": "",
|
"employee": "",
|
||||||
"filterson": "",
|
"filterson": "",
|
||||||
@@ -2673,6 +2688,8 @@
|
|||||||
"job_costing_ro_date_summary": "",
|
"job_costing_ro_date_summary": "",
|
||||||
"job_costing_ro_estimator": "",
|
"job_costing_ro_estimator": "",
|
||||||
"job_costing_ro_ins_co": "",
|
"job_costing_ro_ins_co": "",
|
||||||
|
"job_lifecycle_date_detail": "",
|
||||||
|
"job_lifecycle_date_summary": "",
|
||||||
"jobs_completed_not_invoiced": "",
|
"jobs_completed_not_invoiced": "",
|
||||||
"jobs_invoiced_not_exported": "",
|
"jobs_invoiced_not_exported": "",
|
||||||
"jobs_reconcile": "",
|
"jobs_reconcile": "",
|
||||||
@@ -2762,6 +2779,7 @@
|
|||||||
"allemployeetimetickets": "",
|
"allemployeetimetickets": "",
|
||||||
"asoftodaytarget": "",
|
"asoftodaytarget": "",
|
||||||
"body": "",
|
"body": "",
|
||||||
|
"bodyabbrev": "",
|
||||||
"bodycharttitle": "",
|
"bodycharttitle": "",
|
||||||
"calendarperiod": "",
|
"calendarperiod": "",
|
||||||
"combinedcharttitle": "",
|
"combinedcharttitle": "",
|
||||||
@@ -2778,6 +2796,7 @@
|
|||||||
"productivestatistics": "",
|
"productivestatistics": "",
|
||||||
"productivetimeticketsoverdate": "",
|
"productivetimeticketsoverdate": "",
|
||||||
"refinish": "",
|
"refinish": "",
|
||||||
|
"refinishabbrev": "",
|
||||||
"refinishcharttitle": "",
|
"refinishcharttitle": "",
|
||||||
"targets": "",
|
"targets": "",
|
||||||
"thismonth": "",
|
"thismonth": "",
|
||||||
@@ -2785,6 +2804,7 @@
|
|||||||
"timetickets": "",
|
"timetickets": "",
|
||||||
"timeticketsemployee": "",
|
"timeticketsemployee": "",
|
||||||
"todateactual": "",
|
"todateactual": "",
|
||||||
|
"total": "",
|
||||||
"totalhrs": "",
|
"totalhrs": "",
|
||||||
"totaloverperiod": "",
|
"totaloverperiod": "",
|
||||||
"weeklyactual": "",
|
"weeklyactual": "",
|
||||||
|
|||||||
@@ -514,6 +514,14 @@ export const TemplateList = (type, context) => {
|
|||||||
group: "financial",
|
group: "financial",
|
||||||
dms: true,
|
dms: true,
|
||||||
},
|
},
|
||||||
|
job_lifecycle_ro: {
|
||||||
|
title: i18n.t("printcenter.jobs.job_lifecycle_ro"),
|
||||||
|
description: "",
|
||||||
|
subject: i18n.t("printcenter.jobs.job_lifecycle_ro"),
|
||||||
|
key: "job_lifecycle_ro",
|
||||||
|
disabled: false,
|
||||||
|
group: "post",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
: {}),
|
: {}),
|
||||||
...(!type || type === "job_special"
|
...(!type || type === "job_special"
|
||||||
@@ -2048,6 +2056,30 @@ export const TemplateList = (type, context) => {
|
|||||||
datedisable: true,
|
datedisable: true,
|
||||||
group: "customers",
|
group: "customers",
|
||||||
},
|
},
|
||||||
|
job_lifecycle_date_detail: {
|
||||||
|
title: i18n.t("reportcenter.templates.job_lifecycle_date_detail"),
|
||||||
|
subject: i18n.t("reportcenter.templates.job_lifecycle_date_detail"),
|
||||||
|
key: "job_lifecycle_date_detail",
|
||||||
|
//idtype: "vendor",
|
||||||
|
disabled: false,
|
||||||
|
rangeFilter: {
|
||||||
|
object: i18n.t("reportcenter.labels.objects.jobs"),
|
||||||
|
field: i18n.t("jobs.fields.date_invoiced"),
|
||||||
|
},
|
||||||
|
group: "jobs",
|
||||||
|
},
|
||||||
|
job_lifecycle_date_summary: {
|
||||||
|
title: i18n.t("reportcenter.templates.job_lifecycle_date_summary"),
|
||||||
|
subject: i18n.t("reportcenter.templates.job_lifecycle_date_summary"),
|
||||||
|
key: "job_lifecycle_date_summary",
|
||||||
|
//idtype: "vendor",
|
||||||
|
disabled: false,
|
||||||
|
rangeFilter: {
|
||||||
|
object: i18n.t("reportcenter.labels.objects.jobs"),
|
||||||
|
field: i18n.t("jobs.fields.date_invoiced"),
|
||||||
|
},
|
||||||
|
group: "jobs",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
: {}),
|
: {}),
|
||||||
...(!type || type === "courtesycarcontract"
|
...(!type || type === "courtesycarcontract"
|
||||||
|
|||||||
@@ -2,22 +2,66 @@ import {Kind, parse, print, visit} from "graphql";
|
|||||||
import client from "./GraphQLClient";
|
import client from "./GraphQLClient";
|
||||||
import {gql} from "@apollo/client";
|
import {gql} from "@apollo/client";
|
||||||
|
|
||||||
|
/* eslint-disable no-loop-func */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The available operators for filtering (string)
|
||||||
|
* @type {[{label: string, value: string},{label: string, value: string},{label: string, value: string},{label: string, value: string},{label: string, value: string},null,null,null]}
|
||||||
|
*/
|
||||||
const STRING_OPERATORS = [
|
const STRING_OPERATORS = [
|
||||||
{value: "_eq", label: "equals"},
|
{value: "_eq", label: "equals"},
|
||||||
{value: "_neq", label: "does not equal"},
|
{value: "_neq", label: "does not equal"},
|
||||||
{value: "_like", label: "contains"},
|
{value: "_like", label: "contains"},
|
||||||
{value: "_nlike", label: "does not contain"},
|
{value: "_nlike", label: "does not contain"},
|
||||||
{value: "_ilike", label: "contains case-insensitive"},
|
{value: "_ilike", label: "contains case-insensitive"},
|
||||||
{value: "_nilike", label: "does not contain case-insensitive"}
|
{value: "_nilike", label: "does not contain case-insensitive"},
|
||||||
|
{value: "_in", label: "in", type: "array"},
|
||||||
|
{value: "_nin", label: "not in", type: "array"}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The available operators for filtering (dates)
|
||||||
|
* @type {[{label: string, value: string},{label: string, value: string},{label: string, value: string},{label: string, value: string},{label: string, value: string},null,null,null]}
|
||||||
|
*/
|
||||||
|
const DATE_OPERATORS = [
|
||||||
|
{value: "_eq", label: "equals"},
|
||||||
|
{value: "_neq", label: "does not equal"},
|
||||||
|
{value: "_gt", label: "greater than"},
|
||||||
|
{value: "_lt", label: "less than"},
|
||||||
|
{value: "_gte", label: "greater than or equal"},
|
||||||
|
{value: "_lte", label: "less than or equal"},
|
||||||
|
{value: "_in", label: "in", type: "array"},
|
||||||
|
{value: "_nin", label: "not in", type: "array"}
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The available operators for filtering (booleans)
|
||||||
|
* @type {[{label: string, value: string},{label: string, value: string}]}
|
||||||
|
*/
|
||||||
|
const BOOLEAN_OPERATORS = [
|
||||||
|
{value: "_eq", label: "equals"},
|
||||||
|
{value: "_neq", label: "does not equal"},
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The available operators for filtering (numbers)
|
||||||
|
* @type {[{label: string, value: string},{label: string, value: string},{label: string, value: string},{label: string, value: string},{label: string, value: string},null,null,null]}
|
||||||
|
*/
|
||||||
const NUMBER_OPERATORS = [
|
const NUMBER_OPERATORS = [
|
||||||
{value: "_eq", label: "equals"},
|
{value: "_eq", label: "equals"},
|
||||||
{value: "_neq", label: "does not equal"},
|
{value: "_neq", label: "does not equal"},
|
||||||
{value: "_gt", label: "greater than"},
|
{value: "_gt", label: "greater than"},
|
||||||
{value: "_lt", label: "less than"},
|
{value: "_lt", label: "less than"},
|
||||||
{value: "_gte", label: "greater than or equal"},
|
{value: "_gte", label: "greater than or equal"},
|
||||||
{value: "_lte", label: "less than or equal"}
|
{value: "_lte", label: "less than or equal"},
|
||||||
|
{value: "_in", label: "in", type: "array"},
|
||||||
|
{value: "_nin", label: "not in", type: "array"}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The available operators for sorting
|
||||||
|
* @type {[{label: string, value: string},{label: string, value: string}]}
|
||||||
|
*/
|
||||||
const ORDER_BY_OPERATORS = [
|
const ORDER_BY_OPERATORS = [
|
||||||
{value: "asc", label: "ascending"},
|
{value: "asc", label: "ascending"},
|
||||||
{value: "desc", label: "descending"}
|
{value: "desc", label: "descending"}
|
||||||
@@ -31,7 +75,6 @@ export function getOrderOperatorsByType() {
|
|||||||
return ORDER_BY_OPERATORS;
|
return ORDER_BY_OPERATORS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the available operators for filtering
|
* Get the available operators for filtering
|
||||||
* @param type
|
* @param type
|
||||||
@@ -40,13 +83,14 @@ export function getOrderOperatorsByType() {
|
|||||||
export function getWhereOperatorsByType(type = 'string') {
|
export function getWhereOperatorsByType(type = 'string') {
|
||||||
const operators = {
|
const operators = {
|
||||||
string: STRING_OPERATORS,
|
string: STRING_OPERATORS,
|
||||||
number: NUMBER_OPERATORS
|
number: NUMBER_OPERATORS,
|
||||||
|
boolean: BOOLEAN_OPERATORS,
|
||||||
|
bool: BOOLEAN_OPERATORS,
|
||||||
|
date: DATE_OPERATORS
|
||||||
};
|
};
|
||||||
return operators[type];
|
return operators[type];
|
||||||
}
|
}
|
||||||
|
|
||||||
/* eslint-disable no-loop-func */
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse a GraphQL query into an AST
|
* Parse a GraphQL query into an AST
|
||||||
* @param query
|
* @param query
|
||||||
@@ -78,11 +122,9 @@ export async function generateTemplate(templateQueryToExecute, templateObject, u
|
|||||||
// Parse the query and apply the filters and sorters
|
// Parse the query and apply the filters and sorters
|
||||||
const ast = parseQuery(templateQueryToExecute);
|
const ast = parseQuery(templateQueryToExecute);
|
||||||
|
|
||||||
let filterFields = [];
|
|
||||||
|
|
||||||
if (templateObject?.filters && templateObject?.filters?.length) {
|
if (templateObject?.filters && templateObject?.filters?.length) {
|
||||||
applyFilters(ast, templateObject.filters, filterFields);
|
applyFilters(ast, templateObject.filters);
|
||||||
wrapFiltersInAnd(ast, filterFields);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (templateObject?.sorters && templateObject?.sorters?.length) {
|
if (templateObject?.sorters && templateObject?.sorters?.length) {
|
||||||
@@ -109,7 +151,6 @@ export async function generateTemplate(templateQueryToExecute, templateObject, u
|
|||||||
return {contextData, useShopSpecificTemplate};
|
return {contextData, useShopSpecificTemplate};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Apply sorters to the AST
|
* Apply sorters to the AST
|
||||||
* @param ast
|
* @param ast
|
||||||
@@ -149,16 +190,16 @@ export function applySorters(ast, sorters) {
|
|||||||
if (!orderByArg) {
|
if (!orderByArg) {
|
||||||
orderByArg = {
|
orderByArg = {
|
||||||
kind: Kind.ARGUMENT,
|
kind: Kind.ARGUMENT,
|
||||||
name: { kind: Kind.NAME, value: 'order_by' },
|
name: {kind: Kind.NAME, value: 'order_by'},
|
||||||
value: { kind: Kind.OBJECT, fields: [] },
|
value: {kind: Kind.OBJECT, fields: []},
|
||||||
};
|
};
|
||||||
currentSelection.arguments.push(orderByArg);
|
currentSelection.arguments.push(orderByArg);
|
||||||
}
|
}
|
||||||
|
|
||||||
const sorterField = {
|
const sorterField = {
|
||||||
kind: Kind.OBJECT_FIELD,
|
kind: Kind.OBJECT_FIELD,
|
||||||
name: { kind: Kind.NAME, value: targetFieldName },
|
name: {kind: Kind.NAME, value: targetFieldName},
|
||||||
value: { kind: Kind.ENUM, value: sorter.direction }, // Adjust if your schema uses a different type for sorting directions
|
value: {kind: Kind.ENUM, value: sorter.direction}, // Adjust if your schema uses a different type for sorting directions
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add the new sorter condition
|
// Add the new sorter condition
|
||||||
@@ -170,10 +211,59 @@ export function applySorters(ast, sorters) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply Top Level Sub to the AST
|
||||||
|
* @param node
|
||||||
|
* @param fieldPath
|
||||||
|
* @param filterField
|
||||||
|
*/
|
||||||
|
function applyTopLevelSub(node, fieldPath, filterField) {
|
||||||
|
// Find or create the where argument for the top-level subfield
|
||||||
|
let whereArg = node.selectionSet.selections
|
||||||
|
.find(selection => selection.name.value === fieldPath[0])
|
||||||
|
?.arguments.find(arg => arg.name.value === 'where');
|
||||||
|
|
||||||
|
if (!whereArg) {
|
||||||
|
whereArg = {
|
||||||
|
kind: Kind.ARGUMENT,
|
||||||
|
name: {kind: Kind.NAME, value: 'where'},
|
||||||
|
value: {kind: Kind.OBJECT, fields: []},
|
||||||
|
};
|
||||||
|
const topLevelSubSelection = node.selectionSet.selections.find(selection =>
|
||||||
|
selection.name.value === fieldPath[0]
|
||||||
|
);
|
||||||
|
if (topLevelSubSelection) {
|
||||||
|
topLevelSubSelection.arguments = topLevelSubSelection.arguments || [];
|
||||||
|
topLevelSubSelection.arguments.push(whereArg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Correctly position the nested filter without an extra 'where'
|
||||||
|
if (fieldPath.length > 2) { // More than one level deep
|
||||||
|
let currentField = whereArg.value;
|
||||||
|
fieldPath.slice(1, -1).forEach((path, index) => {
|
||||||
|
let existingField = currentField.fields.find(f => f.name.value === path);
|
||||||
|
if (!existingField) {
|
||||||
|
existingField = {
|
||||||
|
kind: Kind.OBJECT_FIELD,
|
||||||
|
name: {kind: Kind.NAME, value: path},
|
||||||
|
value: {kind: Kind.OBJECT, fields: []}
|
||||||
|
};
|
||||||
|
currentField.fields.push(existingField);
|
||||||
|
}
|
||||||
|
currentField = existingField.value;
|
||||||
|
});
|
||||||
|
currentField.fields.push(filterField);
|
||||||
|
} else { // Directly under the top level
|
||||||
|
whereArg.value.fields.push(filterField);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Apply filters to the AST
|
* Apply filters to the AST
|
||||||
* @param ast
|
* @param ast
|
||||||
* @param filters
|
* @param filters
|
||||||
|
* @returns {ASTNode}
|
||||||
*/
|
*/
|
||||||
export function applyFilters(ast, filters) {
|
export function applyFilters(ast, filters) {
|
||||||
return visit(ast, {
|
return visit(ast, {
|
||||||
@@ -182,192 +272,197 @@ export function applyFilters(ast, filters) {
|
|||||||
filters.forEach(filter => {
|
filters.forEach(filter => {
|
||||||
const fieldPath = filter.field.split('.');
|
const fieldPath = filter.field.split('.');
|
||||||
let topLevel = false;
|
let topLevel = false;
|
||||||
|
let topLevelSub = false;
|
||||||
|
|
||||||
// Determine if the filter should be applied at the top level
|
// Determine if the filter should be applied at the top level
|
||||||
if (fieldPath[0].startsWith('[') && fieldPath[0].endsWith(']')) {
|
if (fieldPath.length === 2) {
|
||||||
fieldPath[0] = fieldPath[0].substring(1, fieldPath[0].length - 1); // Strip the brackets
|
|
||||||
topLevel = true;
|
topLevel = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (topLevel) {
|
if (fieldPath.length > 2 && fieldPath[0].startsWith('[') && fieldPath[0].endsWith(']')) {
|
||||||
// Construct the filter for a top-level application
|
fieldPath[0] = fieldPath[0].substring(1, fieldPath[0].length - 1); // Strip the brackets
|
||||||
const targetFieldName = fieldPath[fieldPath.length - 1];
|
topLevelSub = true;
|
||||||
const filterValue = {
|
|
||||||
kind: getGraphQLKind(filter.value),
|
|
||||||
value: filter.value,
|
|
||||||
};
|
|
||||||
|
|
||||||
const nestedFilter = {
|
|
||||||
kind: Kind.OBJECT_FIELD,
|
|
||||||
name: { kind: Kind.NAME, value: targetFieldName },
|
|
||||||
value: {
|
|
||||||
kind: Kind.OBJECT,
|
|
||||||
fields: [{
|
|
||||||
kind: Kind.OBJECT_FIELD,
|
|
||||||
name: { kind: Kind.NAME, value: filter.operator },
|
|
||||||
value: filterValue,
|
|
||||||
}],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// Find or create the where argument for the top-level field
|
|
||||||
let whereArg = node.selectionSet.selections
|
|
||||||
.find(selection => selection.name.value === fieldPath[0])
|
|
||||||
?.arguments.find(arg => arg.name.value === 'where');
|
|
||||||
|
|
||||||
if (!whereArg) {
|
|
||||||
whereArg = {
|
|
||||||
kind: Kind.ARGUMENT,
|
|
||||||
name: { kind: Kind.NAME, value: 'where' },
|
|
||||||
value: { kind: Kind.OBJECT, fields: [] },
|
|
||||||
};
|
|
||||||
const topLevelSelection = node.selectionSet.selections.find(selection =>
|
|
||||||
selection.name.value === fieldPath[0]
|
|
||||||
);
|
|
||||||
if (topLevelSelection) {
|
|
||||||
topLevelSelection.arguments = topLevelSelection.arguments || [];
|
|
||||||
topLevelSelection.arguments.push(whereArg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Correctly position the nested filter without an extra 'where'
|
|
||||||
if (fieldPath.length > 2) { // More than one level deep
|
|
||||||
let currentField = whereArg.value;
|
|
||||||
fieldPath.slice(1, -1).forEach((path, index) => {
|
|
||||||
let existingField = currentField.fields.find(f => f.name.value === path);
|
|
||||||
if (!existingField) {
|
|
||||||
existingField = {
|
|
||||||
kind: Kind.OBJECT_FIELD,
|
|
||||||
name: { kind: Kind.NAME, value: path },
|
|
||||||
value: { kind: Kind.OBJECT, fields: [] }
|
|
||||||
};
|
|
||||||
currentField.fields.push(existingField);
|
|
||||||
}
|
|
||||||
currentField = existingField.value;
|
|
||||||
});
|
|
||||||
currentField.fields.push(nestedFilter);
|
|
||||||
} else { // Directly under the top level
|
|
||||||
whereArg.value.fields.push(nestedFilter);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Initialize a reference to the current selection to traverse down the AST
|
|
||||||
let currentSelection = node;
|
|
||||||
let whereArgFound = false;
|
|
||||||
|
|
||||||
// Iterate over the fieldPath, except for the last entry, to navigate the structure
|
|
||||||
for (let i = 0; i < fieldPath.length - 1; i++) {
|
|
||||||
const fieldName = fieldPath[i];
|
|
||||||
let fieldFound = false;
|
|
||||||
|
|
||||||
// Check if the current selection has a selectionSet and selections
|
|
||||||
if (currentSelection.selectionSet && currentSelection.selectionSet.selections) {
|
|
||||||
// Look for the field in the current selection's selections
|
|
||||||
const selection = currentSelection.selectionSet.selections.find(sel => sel.name.value === fieldName);
|
|
||||||
if (selection) {
|
|
||||||
// Move down the AST to the found selection
|
|
||||||
currentSelection = selection;
|
|
||||||
fieldFound = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the field was not found in the current path, it's an issue
|
|
||||||
if (!fieldFound) {
|
|
||||||
console.error(`Field ${fieldName} not found in the current selection.`);
|
|
||||||
return; // Exit the loop and function due to error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// At this point, currentSelection should be the parent field where the filter needs to be applied
|
|
||||||
// Check if the 'where' argument already exists in the current selection
|
|
||||||
const whereArg = currentSelection.arguments.find(arg => arg.name.value === 'where');
|
|
||||||
if (whereArg) {
|
|
||||||
whereArgFound = true;
|
|
||||||
} else {
|
|
||||||
// If not found, create a new 'where' argument for the current selection
|
|
||||||
currentSelection.arguments.push({
|
|
||||||
kind: Kind.ARGUMENT,
|
|
||||||
name: { kind: Kind.NAME, value: 'where' },
|
|
||||||
value: { kind: Kind.OBJECT, fields: [] } // Empty fields array to be populated with the filter
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assuming the last entry in fieldPath is the field to apply the filter on
|
|
||||||
const targetField = fieldPath[fieldPath.length - 1];
|
|
||||||
const filterValue = {
|
|
||||||
kind: getGraphQLKind(filter.value),
|
|
||||||
value: filter.value,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Construct the filter field object
|
|
||||||
const filterField = {
|
|
||||||
kind: Kind.OBJECT_FIELD,
|
|
||||||
name: { kind: Kind.NAME, value: targetField },
|
|
||||||
value: {
|
|
||||||
kind: Kind.OBJECT,
|
|
||||||
fields: [{
|
|
||||||
kind: Kind.OBJECT_FIELD,
|
|
||||||
name: { kind: Kind.NAME, value: filter.operator },
|
|
||||||
value: filterValue,
|
|
||||||
}],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// Add the filter field to the 'where' clause of the current selection
|
|
||||||
if (whereArgFound) {
|
|
||||||
whereArg.value.fields.push(filterField);
|
|
||||||
} else {
|
|
||||||
// If the whereArg was newly created, find it again (since we didn't store its reference) and add the filter
|
|
||||||
currentSelection.arguments.find(arg => arg.name.value === 'where').value.fields.push(filterField);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Construct the filter for a top-level application
|
||||||
|
const targetFieldName = fieldPath[fieldPath.length - 1];
|
||||||
|
|
||||||
|
let filterValue = createFilterValue(filter);
|
||||||
|
let filterField = createFilterField(targetFieldName, filter, filterValue);
|
||||||
|
|
||||||
|
if (topLevel) {
|
||||||
|
applyTopLevelFilter(node, fieldPath, filterField);
|
||||||
|
} else if (topLevelSub) {
|
||||||
|
applyTopLevelSub(node, fieldPath, filterField);
|
||||||
|
} else {
|
||||||
|
applyNestedFilter(node, fieldPath, filterField);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a filter value based on the filter
|
||||||
|
* @param filter
|
||||||
|
* @returns {{kind: (Kind|Kind.INT), value}|{kind: Kind.LIST, values: *}}
|
||||||
|
*/
|
||||||
|
function createFilterValue(filter) {
|
||||||
|
if (Array.isArray(filter.value)) {
|
||||||
|
// If it's an array, create a list value with the array items
|
||||||
|
return {
|
||||||
|
kind: Kind.LIST,
|
||||||
|
values: filter.value.map(item => ({
|
||||||
|
kind: getGraphQLKind(item),
|
||||||
|
value: item,
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
// If it's not an array, use the existing logic
|
||||||
|
return {
|
||||||
|
kind: getGraphQLKind(filter.value),
|
||||||
|
value: filter.value,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a filter field based on the target field and filter
|
||||||
|
* @param targetFieldName
|
||||||
|
* @param filter
|
||||||
|
* @param filterValue
|
||||||
|
* @returns {{kind: Kind.OBJECT_FIELD, name: {kind: Kind.NAME, value}, value: {kind: Kind.OBJECT, fields: [{kind: Kind.OBJECT_FIELD, name: {kind: Kind.NAME, value}, value}]}}}
|
||||||
|
*/
|
||||||
|
function createFilterField(targetFieldName, filter, filterValue) {
|
||||||
|
return {
|
||||||
|
kind: Kind.OBJECT_FIELD,
|
||||||
|
name: {kind: Kind.NAME, value: targetFieldName},
|
||||||
|
value: {
|
||||||
|
kind: Kind.OBJECT,
|
||||||
|
fields: [{
|
||||||
|
kind: Kind.OBJECT_FIELD,
|
||||||
|
name: {kind: Kind.NAME, value: filter.operator},
|
||||||
|
value: filterValue,
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply a top-level filter to the AST
|
||||||
|
* @param node
|
||||||
|
* @param fieldPath
|
||||||
|
* @param filterField
|
||||||
|
*/
|
||||||
|
function applyTopLevelFilter(node, fieldPath, filterField) {
|
||||||
|
// Find or create the where argument for the top-level field
|
||||||
|
let whereArg = node.selectionSet.selections
|
||||||
|
.find(selection => selection.name.value === fieldPath[0])
|
||||||
|
?.arguments.find(arg => arg.name.value === 'where');
|
||||||
|
|
||||||
|
if (!whereArg) {
|
||||||
|
whereArg = {
|
||||||
|
kind: Kind.ARGUMENT,
|
||||||
|
name: {kind: Kind.NAME, value: 'where'},
|
||||||
|
value: {kind: Kind.OBJECT, fields: []},
|
||||||
|
};
|
||||||
|
const topLevelSelection = node.selectionSet.selections.find(selection =>
|
||||||
|
selection.name.value === fieldPath[0]
|
||||||
|
);
|
||||||
|
if (topLevelSelection) {
|
||||||
|
topLevelSelection.arguments = topLevelSelection.arguments || [];
|
||||||
|
topLevelSelection.arguments.push(whereArg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Correctly position the nested filter without an extra 'where'
|
||||||
|
if (fieldPath.length > 2) { // More than one level deep
|
||||||
|
let currentField = whereArg.value;
|
||||||
|
fieldPath.slice(1, -1).forEach((path, index) => {
|
||||||
|
let existingField = currentField.fields.find(f => f.name.value === path);
|
||||||
|
if (!existingField) {
|
||||||
|
existingField = {
|
||||||
|
kind: Kind.OBJECT_FIELD,
|
||||||
|
name: {kind: Kind.NAME, value: path},
|
||||||
|
value: {kind: Kind.OBJECT, fields: []}
|
||||||
|
};
|
||||||
|
currentField.fields.push(existingField);
|
||||||
|
}
|
||||||
|
currentField = existingField.value;
|
||||||
|
});
|
||||||
|
currentField.fields.push(filterField);
|
||||||
|
} else { // Directly under the top level
|
||||||
|
whereArg.value.fields.push(filterField);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply a nested filter to the AST
|
||||||
|
* @param node
|
||||||
|
* @param fieldPath
|
||||||
|
* @param filterField
|
||||||
|
*/
|
||||||
|
function applyNestedFilter(node, fieldPath, filterField) {
|
||||||
|
// Initialize a reference to the current selection to traverse down the AST
|
||||||
|
let currentSelection = node;
|
||||||
|
|
||||||
|
// Iterate over the fieldPath, except for the last entry, to navigate the structure
|
||||||
|
for (let i = 0; i < fieldPath.length - 1; i++) {
|
||||||
|
const fieldName = fieldPath[i];
|
||||||
|
let fieldFound = false;
|
||||||
|
|
||||||
|
// Check if the current selection has a selectionSet and selections
|
||||||
|
if (currentSelection.selectionSet && currentSelection.selectionSet.selections) {
|
||||||
|
// Look for the field in the current selection's selections
|
||||||
|
const selection = currentSelection.selectionSet.selections.find(sel => sel.name.value === fieldName);
|
||||||
|
if (selection) {
|
||||||
|
// Move down the AST to the found selection
|
||||||
|
currentSelection = selection;
|
||||||
|
fieldFound = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the field was not found in the current path, it's an issue
|
||||||
|
if (!fieldFound) {
|
||||||
|
console.error(`Field ${fieldName} not found in the current selection.`);
|
||||||
|
return; // Exit the loop and function due to error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// At this point, currentSelection should be the parent field where the filter needs to be applied
|
||||||
|
// Check if the 'where' argument already exists in the current selection
|
||||||
|
const whereArg = currentSelection.arguments.find(arg => arg.name.value === 'where');
|
||||||
|
if (!whereArg) {
|
||||||
|
// If not found, create a new 'where' argument for the current selection
|
||||||
|
currentSelection.arguments.push({
|
||||||
|
kind: Kind.ARGUMENT,
|
||||||
|
name: {kind: Kind.NAME, value: 'where'},
|
||||||
|
value: {kind: Kind.OBJECT, fields: []} // Empty fields array to be populated with the filter
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the filter field to the 'where' clause of the current selection
|
||||||
|
currentSelection.arguments.find(arg => arg.name.value === 'where').value.fields.push(filterField);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the GraphQL kind for a value
|
* Get the GraphQL kind for a value
|
||||||
* @param value
|
* @param value
|
||||||
* @returns {Kind|Kind.INT}
|
* @returns {Kind|Kind.INT}
|
||||||
*/
|
*/
|
||||||
function getGraphQLKind(value) {
|
function getGraphQLKind(value) {
|
||||||
if (typeof value === 'number') {
|
if (Array.isArray(value)) {
|
||||||
|
return Kind.LIST;
|
||||||
|
} else if (typeof value === 'number') {
|
||||||
return value % 1 === 0 ? Kind.INT : Kind.FLOAT;
|
return value % 1 === 0 ? Kind.INT : Kind.FLOAT;
|
||||||
} else if (typeof value === 'boolean') {
|
} else if (typeof value === 'boolean') {
|
||||||
return Kind.BOOLEAN;
|
return Kind.BOOLEAN;
|
||||||
} else if (typeof value === 'string') {
|
} else if (typeof value === 'string') {
|
||||||
return Kind.STRING;
|
return Kind.STRING;
|
||||||
|
} else if (value instanceof Date) {
|
||||||
|
return Kind.STRING; // GraphQL does not have a Date type, so we return it as a string
|
||||||
}
|
}
|
||||||
// Extend with more types as needed
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/* eslint-enable no-loop-func */
|
||||||
* Wrap filters in an 'and' object
|
|
||||||
* @param ast
|
|
||||||
* @param filterFields
|
|
||||||
*/
|
|
||||||
export function wrapFiltersInAnd(ast, filterFields) {
|
|
||||||
visit(ast, {
|
|
||||||
OperationDefinition: {
|
|
||||||
enter(node) {
|
|
||||||
node.selectionSet.selections.forEach((selection) => {
|
|
||||||
let whereArg = selection.arguments.find(arg => arg.name.value === 'where');
|
|
||||||
if (filterFields.length > 1) {
|
|
||||||
const andFilter = {
|
|
||||||
kind: Kind.OBJECT_FIELD,
|
|
||||||
name: {kind: Kind.NAME, value: '_and'},
|
|
||||||
value: {kind: Kind.LIST, values: filterFields}
|
|
||||||
};
|
|
||||||
whereArg.value.fields.push(andFilter);
|
|
||||||
} else if (filterFields.length === 1) {
|
|
||||||
whereArg.value.fields.push(filterFields[0].fields[0]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/* eslint-enable no-loop-func */
|
|
||||||
|
|||||||
@@ -259,28 +259,30 @@
|
|||||||
- active:
|
- active:
|
||||||
_eq: true
|
_eq: true
|
||||||
columns:
|
columns:
|
||||||
- id
|
- billid
|
||||||
|
- bodyshopid
|
||||||
- created
|
- created
|
||||||
- operation
|
- id
|
||||||
|
- jobid
|
||||||
- new_val
|
- new_val
|
||||||
- old_val
|
- old_val
|
||||||
|
- operation
|
||||||
|
- type
|
||||||
- useremail
|
- useremail
|
||||||
- bodyshopid
|
|
||||||
- jobid
|
|
||||||
- billid
|
|
||||||
select_permissions:
|
select_permissions:
|
||||||
- role: user
|
- role: user
|
||||||
permission:
|
permission:
|
||||||
columns:
|
columns:
|
||||||
|
- billid
|
||||||
|
- bodyshopid
|
||||||
|
- created
|
||||||
- id
|
- id
|
||||||
|
- jobid
|
||||||
- new_val
|
- new_val
|
||||||
- old_val
|
- old_val
|
||||||
- operation
|
- operation
|
||||||
|
- type
|
||||||
- useremail
|
- useremail
|
||||||
- created
|
|
||||||
- billid
|
|
||||||
- bodyshopid
|
|
||||||
- jobid
|
|
||||||
filter:
|
filter:
|
||||||
bodyshop:
|
bodyshop:
|
||||||
associations:
|
associations:
|
||||||
@@ -567,6 +569,13 @@
|
|||||||
table:
|
table:
|
||||||
name: parts_orders
|
name: parts_orders
|
||||||
schema: public
|
schema: public
|
||||||
|
- name: tasks
|
||||||
|
using:
|
||||||
|
foreign_key_constraint_on:
|
||||||
|
column: billid
|
||||||
|
table:
|
||||||
|
name: tasks
|
||||||
|
schema: public
|
||||||
insert_permissions:
|
insert_permissions:
|
||||||
- role: user
|
- role: user
|
||||||
permission:
|
permission:
|
||||||
@@ -816,6 +825,13 @@
|
|||||||
table:
|
table:
|
||||||
name: inventory
|
name: inventory
|
||||||
schema: public
|
schema: public
|
||||||
|
- name: ioevents
|
||||||
|
using:
|
||||||
|
foreign_key_constraint_on:
|
||||||
|
column: bodyshopid
|
||||||
|
table:
|
||||||
|
name: ioevents
|
||||||
|
schema: public
|
||||||
- name: jobs
|
- name: jobs
|
||||||
using:
|
using:
|
||||||
foreign_key_constraint_on:
|
foreign_key_constraint_on:
|
||||||
@@ -844,6 +860,13 @@
|
|||||||
table:
|
table:
|
||||||
name: phonebook
|
name: phonebook
|
||||||
schema: public
|
schema: public
|
||||||
|
- name: tasks
|
||||||
|
using:
|
||||||
|
foreign_key_constraint_on:
|
||||||
|
column: bodyshopid
|
||||||
|
table:
|
||||||
|
name: tasks
|
||||||
|
schema: public
|
||||||
- name: timetickets
|
- name: timetickets
|
||||||
using:
|
using:
|
||||||
foreign_key_constraint_on:
|
foreign_key_constraint_on:
|
||||||
@@ -2673,6 +2696,13 @@
|
|||||||
- table:
|
- table:
|
||||||
name: ioevents
|
name: ioevents
|
||||||
schema: public
|
schema: public
|
||||||
|
object_relationships:
|
||||||
|
- name: bodyshop
|
||||||
|
using:
|
||||||
|
foreign_key_constraint_on: bodyshopid
|
||||||
|
- name: user
|
||||||
|
using:
|
||||||
|
foreign_key_constraint_on: useremail
|
||||||
- table:
|
- table:
|
||||||
name: job_ar_schema
|
name: job_ar_schema
|
||||||
schema: public
|
schema: public
|
||||||
@@ -2822,6 +2852,13 @@
|
|||||||
table:
|
table:
|
||||||
name: parts_order_lines
|
name: parts_order_lines
|
||||||
schema: public
|
schema: public
|
||||||
|
- name: tasks
|
||||||
|
using:
|
||||||
|
foreign_key_constraint_on:
|
||||||
|
column: joblineid
|
||||||
|
table:
|
||||||
|
name: tasks
|
||||||
|
schema: public
|
||||||
insert_permissions:
|
insert_permissions:
|
||||||
- role: user
|
- role: user
|
||||||
permission:
|
permission:
|
||||||
@@ -3309,6 +3346,13 @@
|
|||||||
table:
|
table:
|
||||||
name: scoreboard
|
name: scoreboard
|
||||||
schema: public
|
schema: public
|
||||||
|
- name: tasks
|
||||||
|
using:
|
||||||
|
foreign_key_constraint_on:
|
||||||
|
column: jobid
|
||||||
|
table:
|
||||||
|
name: tasks
|
||||||
|
schema: public
|
||||||
- name: timetickets
|
- name: timetickets
|
||||||
using:
|
using:
|
||||||
foreign_key_constraint_on:
|
foreign_key_constraint_on:
|
||||||
@@ -4198,7 +4242,7 @@
|
|||||||
interval_sec: 10
|
interval_sec: 10
|
||||||
num_retries: 0
|
num_retries: 0
|
||||||
timeout_sec: 60
|
timeout_sec: 60
|
||||||
webhook_from_env: HASURA_API_URL
|
webhook: https://worktest.home.irony.online
|
||||||
headers:
|
headers:
|
||||||
- name: event-secret
|
- name: event-secret
|
||||||
value_from_env: EVENT_SECRET
|
value_from_env: EVENT_SECRET
|
||||||
@@ -5006,6 +5050,13 @@
|
|||||||
table:
|
table:
|
||||||
name: parts_order_lines
|
name: parts_order_lines
|
||||||
schema: public
|
schema: public
|
||||||
|
- name: tasks
|
||||||
|
using:
|
||||||
|
foreign_key_constraint_on:
|
||||||
|
column: partsorderid
|
||||||
|
table:
|
||||||
|
name: tasks
|
||||||
|
schema: public
|
||||||
insert_permissions:
|
insert_permissions:
|
||||||
- role: user
|
- role: user
|
||||||
permission:
|
permission:
|
||||||
@@ -5621,6 +5672,128 @@
|
|||||||
_eq: X-Hasura-User-Id
|
_eq: X-Hasura-User-Id
|
||||||
- active:
|
- active:
|
||||||
_eq: true
|
_eq: true
|
||||||
|
- table:
|
||||||
|
name: tasks
|
||||||
|
schema: public
|
||||||
|
object_relationships:
|
||||||
|
- name: bill
|
||||||
|
using:
|
||||||
|
foreign_key_constraint_on: billid
|
||||||
|
- name: bodyshop
|
||||||
|
using:
|
||||||
|
foreign_key_constraint_on: bodyshopid
|
||||||
|
- name: job
|
||||||
|
using:
|
||||||
|
foreign_key_constraint_on: jobid
|
||||||
|
- name: jobline
|
||||||
|
using:
|
||||||
|
foreign_key_constraint_on: joblineid
|
||||||
|
- name: parts_order
|
||||||
|
using:
|
||||||
|
foreign_key_constraint_on: partsorderid
|
||||||
|
- name: user
|
||||||
|
using:
|
||||||
|
foreign_key_constraint_on: assigned_to
|
||||||
|
- name: userByCreatedBy
|
||||||
|
using:
|
||||||
|
foreign_key_constraint_on: created_by
|
||||||
|
insert_permissions:
|
||||||
|
- role: user
|
||||||
|
permission:
|
||||||
|
check:
|
||||||
|
bodyshop:
|
||||||
|
associations:
|
||||||
|
_and:
|
||||||
|
- user:
|
||||||
|
authid:
|
||||||
|
_eq: X-Hasura-User-Id
|
||||||
|
- active:
|
||||||
|
_eq: true
|
||||||
|
columns:
|
||||||
|
- completed
|
||||||
|
- deleted
|
||||||
|
- priority
|
||||||
|
- assigned_to
|
||||||
|
- created_by
|
||||||
|
- description
|
||||||
|
- title
|
||||||
|
- completed_at
|
||||||
|
- created_at
|
||||||
|
- deleted_at
|
||||||
|
- due_date
|
||||||
|
- remind_at
|
||||||
|
- updated_at
|
||||||
|
- billid
|
||||||
|
- bodyshopid
|
||||||
|
- id
|
||||||
|
- jobid
|
||||||
|
- joblineid
|
||||||
|
- partsorderid
|
||||||
|
select_permissions:
|
||||||
|
- role: user
|
||||||
|
permission:
|
||||||
|
columns:
|
||||||
|
- completed
|
||||||
|
- deleted
|
||||||
|
- priority
|
||||||
|
- assigned_to
|
||||||
|
- created_by
|
||||||
|
- description
|
||||||
|
- title
|
||||||
|
- completed_at
|
||||||
|
- created_at
|
||||||
|
- deleted_at
|
||||||
|
- due_date
|
||||||
|
- remind_at
|
||||||
|
- updated_at
|
||||||
|
- billid
|
||||||
|
- bodyshopid
|
||||||
|
- id
|
||||||
|
- jobid
|
||||||
|
- joblineid
|
||||||
|
- partsorderid
|
||||||
|
filter:
|
||||||
|
bodyshop:
|
||||||
|
associations:
|
||||||
|
_and:
|
||||||
|
- user:
|
||||||
|
authid:
|
||||||
|
_eq: X-Hasura-User-Id
|
||||||
|
- active:
|
||||||
|
_eq: true
|
||||||
|
update_permissions:
|
||||||
|
- role: user
|
||||||
|
permission:
|
||||||
|
columns:
|
||||||
|
- completed
|
||||||
|
- deleted
|
||||||
|
- priority
|
||||||
|
- assigned_to
|
||||||
|
- created_by
|
||||||
|
- description
|
||||||
|
- title
|
||||||
|
- completed_at
|
||||||
|
- created_at
|
||||||
|
- deleted_at
|
||||||
|
- due_date
|
||||||
|
- remind_at
|
||||||
|
- updated_at
|
||||||
|
- billid
|
||||||
|
- bodyshopid
|
||||||
|
- id
|
||||||
|
- jobid
|
||||||
|
- joblineid
|
||||||
|
- partsorderid
|
||||||
|
filter:
|
||||||
|
bodyshop:
|
||||||
|
associations:
|
||||||
|
_and:
|
||||||
|
- user:
|
||||||
|
authid:
|
||||||
|
_eq: X-Hasura-User-Id
|
||||||
|
- active:
|
||||||
|
_eq: true
|
||||||
|
check: null
|
||||||
- table:
|
- table:
|
||||||
name: timetickets
|
name: timetickets
|
||||||
schema: public
|
schema: public
|
||||||
@@ -6004,6 +6177,13 @@
|
|||||||
table:
|
table:
|
||||||
name: exportlog
|
name: exportlog
|
||||||
schema: public
|
schema: public
|
||||||
|
- name: ioevents
|
||||||
|
using:
|
||||||
|
foreign_key_constraint_on:
|
||||||
|
column: useremail
|
||||||
|
table:
|
||||||
|
name: ioevents
|
||||||
|
schema: public
|
||||||
- name: messages
|
- name: messages
|
||||||
using:
|
using:
|
||||||
foreign_key_constraint_on:
|
foreign_key_constraint_on:
|
||||||
@@ -6032,6 +6212,20 @@
|
|||||||
table:
|
table:
|
||||||
name: parts_orders
|
name: parts_orders
|
||||||
schema: public
|
schema: public
|
||||||
|
- name: tasks
|
||||||
|
using:
|
||||||
|
foreign_key_constraint_on:
|
||||||
|
column: assigned_to
|
||||||
|
table:
|
||||||
|
name: tasks
|
||||||
|
schema: public
|
||||||
|
- name: tasksByCreatedBy
|
||||||
|
using:
|
||||||
|
foreign_key_constraint_on:
|
||||||
|
column: created_by
|
||||||
|
table:
|
||||||
|
name: tasks
|
||||||
|
schema: public
|
||||||
- name: timetickets
|
- name: timetickets
|
||||||
using:
|
using:
|
||||||
foreign_key_constraint_on:
|
foreign_key_constraint_on:
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
-- Could not auto-generate a down migration.
|
||||||
|
-- Please write an appropriate down migration for the SQL below:
|
||||||
|
-- alter table "public"."audit_trail" add column "type" text
|
||||||
|
-- null;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
alter table "public"."audit_trail" add column "type" text
|
||||||
|
null;
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
-- Could not auto-generate a down migration.
|
||||||
|
-- Please write an appropriate down migration for the SQL below:
|
||||||
|
-- alter table "public"."ioevents" add column "useremail" text
|
||||||
|
-- not null;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
alter table "public"."ioevents" add column "useremail" text;
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
-- Could not auto-generate a down migration.
|
||||||
|
-- Please write an appropriate down migration for the SQL below:
|
||||||
|
-- alter table "public"."ioevents" add column "bodyshopid" uuid
|
||||||
|
-- null;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
alter table "public"."ioevents" add column "bodyshopid" uuid
|
||||||
|
null;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
alter table "public"."ioevents" alter column "useremail" set not null;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
alter table "public"."ioevents" alter column "useremail" drop not null;
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
-- Could not auto-generate a down migration.
|
||||||
|
-- Please write an appropriate down migration for the SQL below:
|
||||||
|
-- alter table "public"."ioevents" add column "env" text
|
||||||
|
-- null;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
alter table "public"."ioevents" add column "env" text
|
||||||
|
null;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
DROP INDEX IF EXISTS "public"."ioevents_useremail";
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
CREATE INDEX "ioevents_useremail" on
|
||||||
|
"public"."ioevents" using btree ("useremail");
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
alter table "public"."ioevents" drop constraint "ioevents_useremail_fkey";
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
alter table "public"."ioevents"
|
||||||
|
add constraint "ioevents_useremail_fkey"
|
||||||
|
foreign key ("useremail")
|
||||||
|
references "public"."users"
|
||||||
|
("email") on update set null on delete set null;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
alter table "public"."ioevents" drop constraint "ioevents_bodyshopid_fkey";
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
alter table "public"."ioevents"
|
||||||
|
add constraint "ioevents_bodyshopid_fkey"
|
||||||
|
foreign key ("bodyshopid")
|
||||||
|
references "public"."bodyshops"
|
||||||
|
("id") on update set null on delete set null;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
DROP INDEX IF EXISTS "public"."idx_audit_trail_type";
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
CREATE INDEX "idx_audit_trail_type" on
|
||||||
|
"public"."audit_trail" using btree ("type");
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
DROP TABLE "public"."tasks";
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
CREATE TABLE "public"."tasks" ("id" uuid NOT NULL DEFAULT gen_random_uuid(), "created_at" timestamptz NOT NULL DEFAULT now(), "updated_at" timestamptz NOT NULL DEFAULT now(), "title" text NOT NULL, "description" Text, "deleted" boolean NOT NULL DEFAULT false, "deleted_at" timestamptz, "due_date" timestamptz, "created_by" text NOT NULL, "assigned_to" Text, "completed" boolean NOT NULL DEFAULT false, "completed_at" timestamptz, "remind_at" timestamptz, "priority" numeric, "bodyshopid" UUID NOT NULL, "jobid" UUID NOT NULL, "joblineid" UUID, "partsorderid" UUID, "billid" UUID, PRIMARY KEY ("id") , FOREIGN KEY ("created_by") REFERENCES "public"."users"("email") ON UPDATE restrict ON DELETE restrict, FOREIGN KEY ("assigned_to") REFERENCES "public"."users"("email") ON UPDATE restrict ON DELETE restrict, FOREIGN KEY ("bodyshopid") REFERENCES "public"."bodyshops"("id") ON UPDATE restrict ON DELETE restrict, FOREIGN KEY ("jobid") REFERENCES "public"."jobs"("id") ON UPDATE cascade ON DELETE cascade, FOREIGN KEY ("joblineid") REFERENCES "public"."joblines"("id") ON UPDATE set null ON DELETE set null, FOREIGN KEY ("partsorderid") REFERENCES "public"."parts_orders"("id") ON UPDATE set null ON DELETE set null, FOREIGN KEY ("billid") REFERENCES "public"."bills"("id") ON UPDATE set null ON DELETE set null);
|
||||||
|
CREATE OR REPLACE FUNCTION "public"."set_current_timestamp_updated_at"()
|
||||||
|
RETURNS TRIGGER AS $$
|
||||||
|
DECLARE
|
||||||
|
_new record;
|
||||||
|
BEGIN
|
||||||
|
_new := NEW;
|
||||||
|
_new."updated_at" = NOW();
|
||||||
|
RETURN _new;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql;
|
||||||
|
CREATE TRIGGER "set_public_tasks_updated_at"
|
||||||
|
BEFORE UPDATE ON "public"."tasks"
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE PROCEDURE "public"."set_current_timestamp_updated_at"();
|
||||||
|
COMMENT ON TRIGGER "set_public_tasks_updated_at" ON "public"."tasks"
|
||||||
|
IS 'trigger to set value of column "updated_at" to current timestamp on row update';
|
||||||
|
CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
||||||
748
package-lock.json
generated
748
package-lock.json
generated
@@ -12,6 +12,7 @@
|
|||||||
"@aws-sdk/client-secrets-manager": "^3.454.0",
|
"@aws-sdk/client-secrets-manager": "^3.454.0",
|
||||||
"@aws-sdk/client-ses": "^3.454.0",
|
"@aws-sdk/client-ses": "^3.454.0",
|
||||||
"@aws-sdk/credential-provider-node": "^3.451.0",
|
"@aws-sdk/credential-provider-node": "^3.451.0",
|
||||||
|
"@azure/storage-blob": "^12.17.0",
|
||||||
"@opensearch-project/opensearch": "^2.4.0",
|
"@opensearch-project/opensearch": "^2.4.0",
|
||||||
"aws4": "^1.12.0",
|
"aws4": "^1.12.0",
|
||||||
"axios": "^1.6.2",
|
"axios": "^1.6.2",
|
||||||
@@ -52,6 +53,7 @@
|
|||||||
"xmlbuilder2": "^3.1.1"
|
"xmlbuilder2": "^3.1.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
|
||||||
"concurrently": "^8.2.2",
|
"concurrently": "^8.2.2",
|
||||||
"source-map-explorer": "^2.5.2"
|
"source-map-explorer": "^2.5.2"
|
||||||
},
|
},
|
||||||
@@ -695,11 +697,496 @@
|
|||||||
"tslib": "^2.3.1"
|
"tslib": "^2.3.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/parser": {
|
"node_modules/@azure/abort-controller": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-TrRLIoSQVzfAJX9H1JeFjzAoDGcoK1IYX1UImfceTZpsyYfWr09Ss1aHW1y5TrrR3iq6RZLBwJ3E24uwPhwahw==",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@azure/core-auth": {
|
||||||
|
"version": "1.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.6.0.tgz",
|
||||||
|
"integrity": "sha512-3X9wzaaGgRaBCwhLQZDtFp5uLIXCPrGbwJNWPPugvL4xbIGgScv77YzzxToKGLAKvG9amDoofMoP+9hsH1vs1w==",
|
||||||
|
"dependencies": {
|
||||||
|
"@azure/abort-controller": "^2.0.0",
|
||||||
|
"@azure/core-util": "^1.1.0",
|
||||||
|
"tslib": "^2.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@azure/core-auth/node_modules/@azure/abort-controller": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-RP/mR/WJchR+g+nQFJGOec+nzeN/VvjlwbinccoqfhTsTHbb8X5+mLDp48kHT0ueyum0BNSwGm0kX0UZuIqTGg==",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@azure/core-http": {
|
||||||
|
"version": "3.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@azure/core-http/-/core-http-3.0.4.tgz",
|
||||||
|
"integrity": "sha512-Fok9VVhMdxAFOtqiiAtg74fL0UJkt0z3D+ouUUxcRLzZNBioPRAMJFVxiWoJljYpXsRi4GDQHzQHDc9AiYaIUQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@azure/abort-controller": "^1.0.0",
|
||||||
|
"@azure/core-auth": "^1.3.0",
|
||||||
|
"@azure/core-tracing": "1.0.0-preview.13",
|
||||||
|
"@azure/core-util": "^1.1.1",
|
||||||
|
"@azure/logger": "^1.0.0",
|
||||||
|
"@types/node-fetch": "^2.5.0",
|
||||||
|
"@types/tunnel": "^0.0.3",
|
||||||
|
"form-data": "^4.0.0",
|
||||||
|
"node-fetch": "^2.6.7",
|
||||||
|
"process": "^0.11.10",
|
||||||
|
"tslib": "^2.2.0",
|
||||||
|
"tunnel": "^0.0.6",
|
||||||
|
"uuid": "^8.3.0",
|
||||||
|
"xml2js": "^0.5.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@azure/core-http/node_modules/uuid": {
|
||||||
|
"version": "8.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|
||||||
|
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
|
||||||
|
"bin": {
|
||||||
|
"uuid": "dist/bin/uuid"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@azure/core-http/node_modules/xml2js": {
|
||||||
|
"version": "0.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz",
|
||||||
|
"integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==",
|
||||||
|
"dependencies": {
|
||||||
|
"sax": ">=0.6.0",
|
||||||
|
"xmlbuilder": "~11.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@azure/core-http/node_modules/xmlbuilder": {
|
||||||
|
"version": "11.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
|
||||||
|
"integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@azure/core-lro": {
|
||||||
|
"version": "2.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@azure/core-lro/-/core-lro-2.6.0.tgz",
|
||||||
|
"integrity": "sha512-PyRNcaIOfMgoUC01/24NoG+k8O81VrKxYARnDlo+Q2xji0/0/j2nIt8BwQh294pb1c5QnXTDPbNR4KzoDKXEoQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@azure/abort-controller": "^2.0.0",
|
||||||
|
"@azure/core-util": "^1.2.0",
|
||||||
|
"@azure/logger": "^1.0.0",
|
||||||
|
"tslib": "^2.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@azure/core-lro/node_modules/@azure/abort-controller": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-RP/mR/WJchR+g+nQFJGOec+nzeN/VvjlwbinccoqfhTsTHbb8X5+mLDp48kHT0ueyum0BNSwGm0kX0UZuIqTGg==",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@azure/core-paging": {
|
||||||
|
"version": "1.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@azure/core-paging/-/core-paging-1.5.0.tgz",
|
||||||
|
"integrity": "sha512-zqWdVIt+2Z+3wqxEOGzR5hXFZ8MGKK52x4vFLw8n58pR6ZfKRx3EXYTxTaYxYHc/PexPUTyimcTWFJbji9Z6Iw==",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@azure/core-tracing": {
|
||||||
|
"version": "1.0.0-preview.13",
|
||||||
|
"resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.0-preview.13.tgz",
|
||||||
|
"integrity": "sha512-KxDlhXyMlh2Jhj2ykX6vNEU0Vou4nHr025KoSEiz7cS3BNiHNaZcdECk/DmLkEB0as5T7b/TpRcehJ5yV6NeXQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@opentelemetry/api": "^1.0.1",
|
||||||
|
"tslib": "^2.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@azure/core-util": {
|
||||||
|
"version": "1.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.7.0.tgz",
|
||||||
|
"integrity": "sha512-Zq2i3QO6k9DA8vnm29mYM4G8IE9u1mhF1GUabVEqPNX8Lj833gdxQ2NAFxt2BZsfAL+e9cT8SyVN7dFVJ/Hf0g==",
|
||||||
|
"dependencies": {
|
||||||
|
"@azure/abort-controller": "^2.0.0",
|
||||||
|
"tslib": "^2.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@azure/core-util/node_modules/@azure/abort-controller": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-RP/mR/WJchR+g+nQFJGOec+nzeN/VvjlwbinccoqfhTsTHbb8X5+mLDp48kHT0ueyum0BNSwGm0kX0UZuIqTGg==",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@azure/logger": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-ustrPY8MryhloQj7OWGe+HrYx+aoiOxzbXTtgblbV3xwCqpzUK36phH3XNHQKj3EPonyFUuDTfR3qFhTEAuZEg==",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@azure/storage-blob": {
|
||||||
|
"version": "12.17.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@azure/storage-blob/-/storage-blob-12.17.0.tgz",
|
||||||
|
"integrity": "sha512-sM4vpsCpcCApagRW5UIjQNlNylo02my2opgp0Emi8x888hZUvJ3dN69Oq20cEGXkMUWnoCrBaB0zyS3yeB87sQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@azure/abort-controller": "^1.0.0",
|
||||||
|
"@azure/core-http": "^3.0.0",
|
||||||
|
"@azure/core-lro": "^2.2.0",
|
||||||
|
"@azure/core-paging": "^1.1.1",
|
||||||
|
"@azure/core-tracing": "1.0.0-preview.13",
|
||||||
|
"@azure/logger": "^1.0.0",
|
||||||
|
"events": "^3.0.0",
|
||||||
|
"tslib": "^2.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/code-frame": {
|
||||||
|
"version": "7.23.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz",
|
||||||
|
"integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/highlight": "^7.23.4",
|
||||||
|
"chalk": "^2.4.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/code-frame/node_modules/ansi-styles": {
|
||||||
|
"version": "3.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
|
||||||
|
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"color-convert": "^1.9.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/code-frame/node_modules/chalk": {
|
||||||
|
"version": "2.4.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
|
||||||
|
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-styles": "^3.2.1",
|
||||||
|
"escape-string-regexp": "^1.0.5",
|
||||||
|
"supports-color": "^5.3.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/code-frame/node_modules/color-convert": {
|
||||||
|
"version": "1.9.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
||||||
|
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"color-name": "1.1.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/code-frame/node_modules/color-name": {
|
||||||
|
"version": "1.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
||||||
|
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/@babel/code-frame/node_modules/escape-string-regexp": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
||||||
|
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/code-frame/node_modules/has-flag": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/code-frame/node_modules/supports-color": {
|
||||||
|
"version": "5.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||||
|
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"has-flag": "^3.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/generator": {
|
||||||
|
"version": "7.17.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.7.tgz",
|
||||||
|
"integrity": "sha512-oLcVCTeIFadUoArDTwpluncplrYBmTCCZZgXCbgNGvOBBiSDDK3eWO4b/+eOTli5tKv1lg+a5/NAXg+nTcei1w==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/types": "^7.17.0",
|
||||||
|
"jsesc": "^2.5.1",
|
||||||
|
"source-map": "^0.5.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/generator/node_modules/source-map": {
|
||||||
|
"version": "0.5.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
|
||||||
|
"integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/helper-environment-visitor": {
|
||||||
|
"version": "7.22.20",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz",
|
||||||
|
"integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/helper-function-name": {
|
||||||
|
"version": "7.23.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz",
|
||||||
|
"integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/template": "^7.22.15",
|
||||||
|
"@babel/types": "^7.23.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/helper-function-name/node_modules/@babel/types": {
|
||||||
|
"version": "7.23.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.9.tgz",
|
||||||
|
"integrity": "sha512-dQjSq/7HaSjRM43FFGnv5keM2HsxpmyV1PfaSVm0nzzjwwTmjOe6J4bC8e3+pTEIgHaHj+1ZlLThRJ2auc/w1Q==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/helper-string-parser": "^7.23.4",
|
||||||
|
"@babel/helper-validator-identifier": "^7.22.20",
|
||||||
|
"to-fast-properties": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/helper-hoist-variables": {
|
||||||
|
"version": "7.22.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz",
|
||||||
|
"integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/types": "^7.22.5"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/helper-hoist-variables/node_modules/@babel/types": {
|
||||||
|
"version": "7.23.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.9.tgz",
|
||||||
|
"integrity": "sha512-dQjSq/7HaSjRM43FFGnv5keM2HsxpmyV1PfaSVm0nzzjwwTmjOe6J4bC8e3+pTEIgHaHj+1ZlLThRJ2auc/w1Q==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/helper-string-parser": "^7.23.4",
|
||||||
|
"@babel/helper-validator-identifier": "^7.22.20",
|
||||||
|
"to-fast-properties": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/helper-split-export-declaration": {
|
||||||
|
"version": "7.22.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz",
|
||||||
|
"integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/types": "^7.22.5"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/helper-split-export-declaration/node_modules/@babel/types": {
|
||||||
|
"version": "7.23.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.9.tgz",
|
||||||
|
"integrity": "sha512-dQjSq/7HaSjRM43FFGnv5keM2HsxpmyV1PfaSVm0nzzjwwTmjOe6J4bC8e3+pTEIgHaHj+1ZlLThRJ2auc/w1Q==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/helper-string-parser": "^7.23.4",
|
||||||
|
"@babel/helper-validator-identifier": "^7.22.20",
|
||||||
|
"to-fast-properties": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/helper-string-parser": {
|
||||||
"version": "7.23.4",
|
"version": "7.23.4",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.4.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz",
|
||||||
"integrity": "sha512-vf3Xna6UEprW+7t6EtOmFpHNAuxw3xqPZghy+brsnusscJRW5BMUzzHZc5ICjULee81WeUV2jjakG09MDglJXQ==",
|
"integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==",
|
||||||
"optional": true,
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/helper-validator-identifier": {
|
||||||
|
"version": "7.22.20",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz",
|
||||||
|
"integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/highlight": {
|
||||||
|
"version": "7.23.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz",
|
||||||
|
"integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/helper-validator-identifier": "^7.22.20",
|
||||||
|
"chalk": "^2.4.2",
|
||||||
|
"js-tokens": "^4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/highlight/node_modules/ansi-styles": {
|
||||||
|
"version": "3.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
|
||||||
|
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"color-convert": "^1.9.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/highlight/node_modules/chalk": {
|
||||||
|
"version": "2.4.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
|
||||||
|
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-styles": "^3.2.1",
|
||||||
|
"escape-string-regexp": "^1.0.5",
|
||||||
|
"supports-color": "^5.3.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/highlight/node_modules/color-convert": {
|
||||||
|
"version": "1.9.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
||||||
|
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"color-name": "1.1.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/highlight/node_modules/color-name": {
|
||||||
|
"version": "1.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
||||||
|
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/@babel/highlight/node_modules/escape-string-regexp": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
||||||
|
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/highlight/node_modules/has-flag": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/highlight/node_modules/supports-color": {
|
||||||
|
"version": "5.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||||
|
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"has-flag": "^3.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/parser": {
|
||||||
|
"version": "7.23.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.9.tgz",
|
||||||
|
"integrity": "sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA==",
|
||||||
|
"devOptional": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"parser": "bin/babel-parser.js"
|
"parser": "bin/babel-parser.js"
|
||||||
},
|
},
|
||||||
@@ -718,6 +1205,97 @@
|
|||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@babel/template": {
|
||||||
|
"version": "7.23.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.23.9.tgz",
|
||||||
|
"integrity": "sha512-+xrD2BWLpvHKNmX2QbpdpsBaWnRxahMwJjO+KZk2JOElj5nSmKezyS1B4u+QbHMTX69t4ukm6hh9lsYQ7GHCKA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/code-frame": "^7.23.5",
|
||||||
|
"@babel/parser": "^7.23.9",
|
||||||
|
"@babel/types": "^7.23.9"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/template/node_modules/@babel/types": {
|
||||||
|
"version": "7.23.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.9.tgz",
|
||||||
|
"integrity": "sha512-dQjSq/7HaSjRM43FFGnv5keM2HsxpmyV1PfaSVm0nzzjwwTmjOe6J4bC8e3+pTEIgHaHj+1ZlLThRJ2auc/w1Q==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/helper-string-parser": "^7.23.4",
|
||||||
|
"@babel/helper-validator-identifier": "^7.22.20",
|
||||||
|
"to-fast-properties": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/traverse": {
|
||||||
|
"version": "7.23.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz",
|
||||||
|
"integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/code-frame": "^7.22.13",
|
||||||
|
"@babel/generator": "^7.23.0",
|
||||||
|
"@babel/helper-environment-visitor": "^7.22.20",
|
||||||
|
"@babel/helper-function-name": "^7.23.0",
|
||||||
|
"@babel/helper-hoist-variables": "^7.22.5",
|
||||||
|
"@babel/helper-split-export-declaration": "^7.22.6",
|
||||||
|
"@babel/parser": "^7.23.0",
|
||||||
|
"@babel/types": "^7.23.0",
|
||||||
|
"debug": "^4.1.0",
|
||||||
|
"globals": "^11.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/traverse/node_modules/@babel/generator": {
|
||||||
|
"version": "7.23.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz",
|
||||||
|
"integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/types": "^7.23.6",
|
||||||
|
"@jridgewell/gen-mapping": "^0.3.2",
|
||||||
|
"@jridgewell/trace-mapping": "^0.3.17",
|
||||||
|
"jsesc": "^2.5.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/traverse/node_modules/@babel/types": {
|
||||||
|
"version": "7.23.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.9.tgz",
|
||||||
|
"integrity": "sha512-dQjSq/7HaSjRM43FFGnv5keM2HsxpmyV1PfaSVm0nzzjwwTmjOe6J4bC8e3+pTEIgHaHj+1ZlLThRJ2auc/w1Q==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/helper-string-parser": "^7.23.4",
|
||||||
|
"@babel/helper-validator-identifier": "^7.22.20",
|
||||||
|
"to-fast-properties": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/types": {
|
||||||
|
"version": "7.17.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz",
|
||||||
|
"integrity": "sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/helper-validator-identifier": "^7.16.7",
|
||||||
|
"to-fast-properties": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@colors/colors": {
|
"node_modules/@colors/colors": {
|
||||||
"version": "1.6.0",
|
"version": "1.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz",
|
||||||
@@ -1034,6 +1612,54 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@jonkemp/package-utils/-/package-utils-1.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/@jonkemp/package-utils/-/package-utils-1.0.8.tgz",
|
||||||
"integrity": "sha512-bIcKnH5YmtTYr7S6J3J86dn/rFiklwRpOqbTOQ9C0WMmR9FKHVb3bxs2UYfqEmNb93O4nbA97sb6rtz33i9SyA=="
|
"integrity": "sha512-bIcKnH5YmtTYr7S6J3J86dn/rFiklwRpOqbTOQ9C0WMmR9FKHVb3bxs2UYfqEmNb93O4nbA97sb6rtz33i9SyA=="
|
||||||
},
|
},
|
||||||
|
"node_modules/@jridgewell/gen-mapping": {
|
||||||
|
"version": "0.3.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.4.tgz",
|
||||||
|
"integrity": "sha512-Oud2QPM5dHviZNn4y/WhhYKSXksv+1xLEIsNrAbGcFzUN3ubqWRFT5gwPchNc5NuzILOU4tPBDTZ4VwhL8Y7cw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@jridgewell/set-array": "^1.0.1",
|
||||||
|
"@jridgewell/sourcemap-codec": "^1.4.10",
|
||||||
|
"@jridgewell/trace-mapping": "^0.3.9"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@jridgewell/resolve-uri": {
|
||||||
|
"version": "3.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
|
||||||
|
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@jridgewell/set-array": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@jridgewell/sourcemap-codec": {
|
||||||
|
"version": "1.4.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
|
||||||
|
"integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/@jridgewell/trace-mapping": {
|
||||||
|
"version": "0.3.23",
|
||||||
|
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.23.tgz",
|
||||||
|
"integrity": "sha512-9/4foRoUKp8s96tSkh8DlAAc5A0Ty8vLXld+l9gjKKY6ckwI8G15f0hskGmuLZu78ZlGa1vtsfOa+lnB4vG6Jg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@jridgewell/resolve-uri": "^3.1.0",
|
||||||
|
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@jsdoc/salty": {
|
"node_modules/@jsdoc/salty": {
|
||||||
"version": "0.2.6",
|
"version": "0.2.6",
|
||||||
"resolved": "https://registry.npmjs.org/@jsdoc/salty/-/salty-0.2.6.tgz",
|
"resolved": "https://registry.npmjs.org/@jsdoc/salty/-/salty-0.2.6.tgz",
|
||||||
@@ -1106,6 +1732,14 @@
|
|||||||
"yarn": "^1.22.10"
|
"yarn": "^1.22.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@opentelemetry/api": {
|
||||||
|
"version": "1.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.7.0.tgz",
|
||||||
|
"integrity": "sha512-AdY5wvN0P2vXBi3b29hxZgSFvdhdxPB9+f0B6s//P9Q8nibRWeA3cHm8UmLpio9ABigkVHJ5NMPk+Mz8VCCyrw==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@pkgjs/parseargs": {
|
"node_modules/@pkgjs/parseargs": {
|
||||||
"version": "0.11.0",
|
"version": "0.11.0",
|
||||||
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
|
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
|
||||||
@@ -1714,6 +2348,29 @@
|
|||||||
"node": ">= 6"
|
"node": ">= 6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@trivago/prettier-plugin-sort-imports": {
|
||||||
|
"version": "4.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@trivago/prettier-plugin-sort-imports/-/prettier-plugin-sort-imports-4.3.0.tgz",
|
||||||
|
"integrity": "sha512-r3n0onD3BTOVUNPhR4lhVK4/pABGpbA7bW3eumZnYdKaHkf1qEC+Mag6DPbGNuuh0eG8AaYj+YqmVHSiGslaTQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/generator": "7.17.7",
|
||||||
|
"@babel/parser": "^7.20.5",
|
||||||
|
"@babel/traverse": "7.23.2",
|
||||||
|
"@babel/types": "7.17.0",
|
||||||
|
"javascript-natural-sort": "0.7.1",
|
||||||
|
"lodash": "^4.17.21"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@vue/compiler-sfc": "3.x",
|
||||||
|
"prettier": "2.x - 3.x"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@vue/compiler-sfc": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/body-parser": {
|
"node_modules/@types/body-parser": {
|
||||||
"version": "1.19.5",
|
"version": "1.19.5",
|
||||||
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz",
|
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz",
|
||||||
@@ -1857,6 +2514,15 @@
|
|||||||
"undici-types": "~5.26.4"
|
"undici-types": "~5.26.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/node-fetch": {
|
||||||
|
"version": "2.6.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.11.tgz",
|
||||||
|
"integrity": "sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "*",
|
||||||
|
"form-data": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/qs": {
|
"node_modules/@types/qs": {
|
||||||
"version": "6.9.10",
|
"version": "6.9.10",
|
||||||
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.10.tgz",
|
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.10.tgz",
|
||||||
@@ -1906,6 +2572,14 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz",
|
"resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz",
|
||||||
"integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw=="
|
"integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw=="
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/tunnel": {
|
||||||
|
"version": "0.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/tunnel/-/tunnel-0.0.3.tgz",
|
||||||
|
"integrity": "sha512-sOUTGn6h1SfQ+gbgqC364jLFBw2lnFqkgF3q0WovEHRLMrVD1sd5aufqi/aJObLekJO+Aq5z646U4Oxy6shXMA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@xmldom/xmldom": {
|
"node_modules/@xmldom/xmldom": {
|
||||||
"version": "0.8.10",
|
"version": "0.8.10",
|
||||||
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz",
|
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz",
|
||||||
@@ -3307,6 +3981,14 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/events": {
|
||||||
|
"version": "3.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
|
||||||
|
"integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.8.x"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/express": {
|
"node_modules/express": {
|
||||||
"version": "4.18.2",
|
"version": "4.18.2",
|
||||||
"resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz",
|
"resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz",
|
||||||
@@ -3854,6 +4536,15 @@
|
|||||||
"url": "https://github.com/sponsors/isaacs"
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/globals": {
|
||||||
|
"version": "11.12.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
|
||||||
|
"integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/google-auth-library": {
|
"node_modules/google-auth-library": {
|
||||||
"version": "8.9.0",
|
"version": "8.9.0",
|
||||||
"resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-8.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-8.9.0.tgz",
|
||||||
@@ -4446,6 +5137,12 @@
|
|||||||
"node": "*"
|
"node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/javascript-natural-sort": {
|
||||||
|
"version": "0.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz",
|
||||||
|
"integrity": "sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/jose": {
|
"node_modules/jose": {
|
||||||
"version": "4.15.4",
|
"version": "4.15.4",
|
||||||
"resolved": "https://registry.npmjs.org/jose/-/jose-4.15.4.tgz",
|
"resolved": "https://registry.npmjs.org/jose/-/jose-4.15.4.tgz",
|
||||||
@@ -4459,6 +5156,12 @@
|
|||||||
"resolved": "https://registry.npmjs.org/js-md4/-/js-md4-0.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/js-md4/-/js-md4-0.3.2.tgz",
|
||||||
"integrity": "sha512-/GDnfQYsltsjRswQhN9fhv3EMw2sCpUdrdxyWDOUK7eyD++r3gRhzgiQgc/x4MAv2i1iuQ4lxO5mvqM3vj4bwA=="
|
"integrity": "sha512-/GDnfQYsltsjRswQhN9fhv3EMw2sCpUdrdxyWDOUK7eyD++r3gRhzgiQgc/x4MAv2i1iuQ4lxO5mvqM3vj4bwA=="
|
||||||
},
|
},
|
||||||
|
"node_modules/js-tokens": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/js-yaml": {
|
"node_modules/js-yaml": {
|
||||||
"version": "3.14.1",
|
"version": "3.14.1",
|
||||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
|
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
|
||||||
@@ -4540,6 +5243,18 @@
|
|||||||
"integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==",
|
"integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==",
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
|
"node_modules/jsesc": {
|
||||||
|
"version": "2.5.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
|
||||||
|
"integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
|
||||||
|
"dev": true,
|
||||||
|
"bin": {
|
||||||
|
"jsesc": "bin/jsesc"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/json-2-csv": {
|
"node_modules/json-2-csv": {
|
||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/json-2-csv/-/json-2-csv-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/json-2-csv/-/json-2-csv-5.0.1.tgz",
|
||||||
@@ -5457,6 +6172,14 @@
|
|||||||
"node": ">= 0.8.0"
|
"node": ">= 0.8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/process": {
|
||||||
|
"version": "0.11.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
|
||||||
|
"integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/process-nextick-args": {
|
"node_modules/process-nextick-args": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||||
@@ -6894,6 +7617,15 @@
|
|||||||
"url": "https://github.com/sponsors/isaacs"
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/to-fast-properties": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/toidentifier": {
|
"node_modules/toidentifier": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
|
||||||
@@ -6949,6 +7681,14 @@
|
|||||||
"node": ">=0.6.x"
|
"node": ">=0.6.x"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/tunnel": {
|
||||||
|
"version": "0.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
|
||||||
|
"integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.6.11 <=0.7.0 || >=0.7.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/tunnel-agent": {
|
"node_modules/tunnel-agent": {
|
||||||
"version": "0.6.0",
|
"version": "0.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
"@aws-sdk/client-secrets-manager": "^3.454.0",
|
"@aws-sdk/client-secrets-manager": "^3.454.0",
|
||||||
"@aws-sdk/client-ses": "^3.454.0",
|
"@aws-sdk/client-ses": "^3.454.0",
|
||||||
"@aws-sdk/credential-provider-node": "^3.451.0",
|
"@aws-sdk/credential-provider-node": "^3.451.0",
|
||||||
|
"@azure/storage-blob": "^12.17.0",
|
||||||
"@opensearch-project/opensearch": "^2.4.0",
|
"@opensearch-project/opensearch": "^2.4.0",
|
||||||
"aws4": "^1.12.0",
|
"aws4": "^1.12.0",
|
||||||
"axios": "^1.6.2",
|
"axios": "^1.6.2",
|
||||||
@@ -61,6 +62,7 @@
|
|||||||
"xmlbuilder2": "^3.1.1"
|
"xmlbuilder2": "^3.1.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
|
||||||
"concurrently": "^8.2.2",
|
"concurrently": "^8.2.2",
|
||||||
"source-map-explorer": "^2.5.2"
|
"source-map-explorer": "^2.5.2"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -201,19 +201,24 @@ exports.default = async (req, res) => {
|
|||||||
} finally {
|
} finally {
|
||||||
sftp.end();
|
sftp.end();
|
||||||
}
|
}
|
||||||
sendServerEmail({
|
// sendServerEmail({
|
||||||
subject: `Kaizen Report ${moment().format("MM-DD-YY")}`,
|
// subject: `Kaizen Report ${moment().format("MM-DD-YY")}`,
|
||||||
text: `Errors: ${allErrors.map((e) => JSON.stringify(e, null, 2))}
|
// text: `Errors: ${allErrors.map((e) => JSON.stringify(e, null, 2))}
|
||||||
Uploaded: ${JSON.stringify(
|
// Uploaded: ${JSON.stringify(
|
||||||
allxmlsToUpload.map((x) => ({ filename: x.filename, count: x.count })),
|
// allxmlsToUpload.map((x) => ({ filename: x.filename, count: x.count })),
|
||||||
null,
|
// null,
|
||||||
2
|
// 2
|
||||||
)}
|
// )}
|
||||||
`,
|
// `,
|
||||||
});
|
// });
|
||||||
res.sendStatus(200);
|
res.sendStatus(200);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(200).json(error);
|
res.status(200).json(error);
|
||||||
|
sendServerEmail({
|
||||||
|
subject: `Kaizen Report ${moment().format("MM-DD-YY @ HH:mm:ss")}`,
|
||||||
|
text: `Errors: JSON.stringify(error)}
|
||||||
|
All Errors: ${allErrors.map((e) => JSON.stringify(e, null, 2))}`,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user